summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author npwoods <npwoods@mess.org>2022-12-16 14:03:59 -0500
committer GitHub <noreply@github.com>2022-12-17 06:03:59 +1100
commit13910382a5e846677f30d1645bfb44e4983de557 (patch)
treebc75e208d5dc4305a6721dff63efaec2864db178 /src/lib
parent14c3d33e86ceeeeb57c44cb7d6aa85685f79813a (diff)
osd/modules/file: Don't magically substitute environment variables when opening files. (#9859)
* util/options.cpp: Added option types for single and multiple paths. * util/options.cpp: Substitute environment variables in values from defaults and INI files. * ui/dirmenu.cpp: Removed hard-coded list of multi-path options. * plugins: Don't substitute environment variables in path options.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/util/options.cpp201
-rw-r--r--src/lib/util/options.h23
2 files changed, 193 insertions, 31 deletions
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp
index cac92c37020..7bf64c344e4 100644
--- a/src/lib/util/options.cpp
+++ b/src/lib/util/options.cpp
@@ -12,6 +12,7 @@
#include "corefile.h"
#include "corestr.h"
+#include "osdcore.h"
#include <locale>
#include <string>
@@ -20,6 +21,7 @@
#include <cctype>
#include <cstdarg>
#include <cstdlib>
+#include <sstream>
const int core_options::MAX_UNADORNED_OPTIONS;
@@ -113,7 +115,7 @@ options_error_exception::options_error_exception(std::string &&message)
// entry - constructor
//-------------------------------------------------
-core_options::entry::entry(std::vector<std::string> &&names, core_options::option_type type, const char *description)
+core_options::entry::entry(std::vector<std::string> &&names, option_type type, const char *description)
: m_names(std::move(names))
, m_priority(OPTION_PRIORITY_DEFAULT)
, m_type(type)
@@ -122,7 +124,7 @@ core_options::entry::entry(std::vector<std::string> &&names, core_options::optio
assert(m_names.empty() == (m_type == option_type::HEADER));
}
-core_options::entry::entry(std::string &&name, core_options::option_type type, const char *description)
+core_options::entry::entry(std::string &&name, option_type type, const char *description)
: entry(std::vector<std::string>({ std::move(name) }), type, description)
{
}
@@ -150,10 +152,44 @@ const char *core_options::entry::value() const noexcept
//-------------------------------------------------
+// entry::value_unsubstituted
+//-------------------------------------------------
+
+const char *core_options::entry::value_unsubstituted() const noexcept
+{
+ return value();
+}
+
+
+//-------------------------------------------------
+// entry::copy_from
+//-------------------------------------------------
+
+void core_options::entry::copy_from(const entry &that, bool always_override)
+{
+ // it is invalid to set the value on a header
+ assert(type() != option_type::HEADER);
+
+ // only set the value if we have priority
+ if (always_override || that.priority() >= priority())
+ {
+ if (internal_copy_value(that))
+ {
+ m_priority = that.priority();
+
+ // invoke the value changed handler, if appropriate
+ if (m_value_changed_handler)
+ m_value_changed_handler(value());
+ }
+ }
+}
+
+
+//-------------------------------------------------
// entry::set_value
//-------------------------------------------------
-void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override)
+void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override, bool perform_substitutions)
{
// it is invalid to set the value on a header
assert(type() != option_type::HEADER);
@@ -163,7 +199,7 @@ void core_options::entry::set_value(std::string &&newvalue, int priority_value,
// only set the value if we have priority
if (always_override || priority_value >= priority())
{
- internal_set_value(std::move(newvalue));
+ internal_set_value(std::move(newvalue), perform_substitutions);
m_priority = priority_value;
// invoke the value changed handler, if appropriate
@@ -185,6 +221,29 @@ void core_options::entry::set_default_value(std::string &&newvalue)
//-------------------------------------------------
+// entry::internal_copy_value
+//-------------------------------------------------
+
+bool core_options::entry::internal_copy_value(const entry &that)
+{
+ char const *const newvalue = that.value();
+ if (newvalue)
+ {
+ std::string stringval = newvalue;
+ validate(stringval);
+
+ internal_set_value(std::move(stringval), false);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
// entry::validate
//-------------------------------------------------
@@ -273,12 +332,14 @@ void core_options::entry::validate(const std::string &data)
}
break;
- case core_options::option_type::STRING:
+ case option_type::STRING:
+ case option_type::PATH:
+ case option_type::MULTIPATH:
// strings can be anything
break;
- case core_options::option_type::INVALID:
- case core_options::option_type::HEADER:
+ case option_type::INVALID:
+ case option_type::HEADER:
default:
// anything else is invalid
throw options_error_exception("Attempted to set invalid option %s\n", name());
@@ -337,10 +398,12 @@ const std::string &core_options::entry::default_value() const noexcept
core_options::simple_entry::simple_entry(std::vector<std::string> &&names, const char *description, core_options::option_type type, std::string &&defdata, std::string &&minimum, std::string &&maximum)
: entry(std::move(names), type, description)
- , m_defdata(std::move(defdata))
+ , m_defdata_unsubst(std::move(defdata))
, m_minimum(std::move(minimum))
, m_maximum(std::move(maximum))
{
+ m_defdata = type_specific_substitutions(m_defdata_unsubst);
+ m_data_unsubst = m_defdata_unsubst;
m_data = m_defdata;
}
@@ -366,6 +429,8 @@ const char *core_options::simple_entry::value() const noexcept
case core_options::option_type::INTEGER:
case core_options::option_type::FLOAT:
case core_options::option_type::STRING:
+ case core_options::option_type::PATH:
+ case core_options::option_type::MULTIPATH:
return m_data.c_str();
default:
@@ -377,6 +442,30 @@ const char *core_options::simple_entry::value() const noexcept
//-------------------------------------------------
+// simple_entry::value_unsubstituted
+//-------------------------------------------------
+
+const char *core_options::simple_entry::value_unsubstituted() const noexcept
+{
+ switch (type())
+ {
+ case core_options::option_type::BOOLEAN:
+ case core_options::option_type::INTEGER:
+ case core_options::option_type::FLOAT:
+ case core_options::option_type::STRING:
+ case core_options::option_type::PATH:
+ case core_options::option_type::MULTIPATH:
+ return m_data_unsubst.c_str();
+
+ default:
+ // this is an option type for which returning a value is
+ // a meaningless operation (e.g. - core_options::option_type::COMMAND)
+ return nullptr;
+ }
+}
+
+
+//-------------------------------------------------
// simple_entry::default_value
//-------------------------------------------------
@@ -388,12 +477,38 @@ const std::string &core_options::simple_entry::default_value() const noexcept
//-------------------------------------------------
-// internal_set_value
+// simple_entry::internal_set_value
+//-------------------------------------------------
+
+void core_options::simple_entry::internal_set_value(std::string &&newvalue, bool perform_substitutions)
+{
+ m_data = perform_substitutions
+ ? type_specific_substitutions(newvalue)
+ : newvalue;
+ m_data_unsubst = std::move(newvalue);
+}
+
+
+//-------------------------------------------------
+// simple_entry::internal_copy_value
//-------------------------------------------------
-void core_options::simple_entry::internal_set_value(std::string &&newvalue)
+bool core_options::simple_entry::internal_copy_value(const entry &that)
{
- m_data = std::move(newvalue);
+ simple_entry const *const simple = dynamic_cast<simple_entry const *>(&that);
+ if (!simple)
+ {
+ return entry::internal_copy_value(that);
+ }
+ else
+ {
+ validate(simple->m_data);
+
+ m_data = simple->m_data;
+ m_data_unsubst = simple->m_data_unsubst;
+
+ return true;
+ }
}
@@ -403,7 +518,47 @@ void core_options::simple_entry::internal_set_value(std::string &&newvalue)
void core_options::simple_entry::set_default_value(std::string &&newvalue)
{
- m_data = m_defdata = std::move(newvalue);
+ m_data = m_defdata = type_specific_substitutions(newvalue);
+ m_data_unsubst = std::move(newvalue);
+}
+
+
+//-------------------------------------------------
+// type_specific_substitutions
+//-------------------------------------------------
+
+std::string core_options::simple_entry::type_specific_substitutions(std::string_view s) const noexcept
+{
+ switch (type())
+ {
+ case option_type::PATH:
+ return osd_subst_env(s);
+
+ case option_type::MULTIPATH:
+ {
+ std::ostringstream result;
+ while (!s.empty())
+ {
+ std::string_view::size_type split = s.find(';');
+ if (std::string_view::npos == split)
+ split = s.length();
+ result << osd_subst_env(s.substr(0, split));
+ if (s.length() > split)
+ {
+ result << s[split];
+ s.remove_prefix(split + 1);
+ }
+ else
+ {
+ s.remove_prefix(split);
+ }
+ }
+ return std::move(result).str();
+ }
+
+ default:
+ return std::string(s);
+ }
}
@@ -717,7 +872,7 @@ void core_options::parse_command_line(const std::vector<std::string> &args, int
}
// set the new data
- do_set_value(*curentry, newdata, priority, error_stream, condition);
+ do_set_value(*curentry, newdata, priority, error_stream, condition, false);
}
// did we have any errors that may need to be aggregated?
@@ -791,7 +946,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool i
}
// set the new data
- do_set_value(*curentry, trim_spaces_and_quotes(optiondata), priority, error_stream, condition);
+ do_set_value(*curentry, trim_spaces_and_quotes(optiondata), priority, error_stream, condition, true);
}
// did we have any errors that may need to be aggregated?
@@ -837,11 +992,7 @@ void core_options::copy_from(const core_options &that)
// identify the source entry
const entry::shared_const_ptr source_entry = that.get_entry(dest_entry->name());
if (source_entry)
- {
- const char *value = source_entry->value();
- if (value)
- dest_entry->set_value(value, source_entry->priority(), true);
- }
+ dest_entry->copy_from(*source_entry, false);
}
}
}
@@ -875,7 +1026,7 @@ std::string core_options::output_ini(const core_options *diff) const
else
{
const std::string &name(curentry->name());
- const char *value(curentry->value());
+ const char *value(curentry->value_unsubstituted());
// check if it's unadorned
bool is_unadorned = false;
@@ -889,7 +1040,7 @@ std::string core_options::output_ini(const core_options *diff) const
if (value)
{
// look up counterpart in diff, if diff is specified
- if (!diff || strcmp(value, diff->value(name.c_str())))
+ if (!diff || strcmp(value, diff->get_entry(name.c_str())->value_unsubstituted()))
{
// output header, if we have one
if (last_header)
@@ -1022,7 +1173,7 @@ void core_options::set_value(std::string_view name, std::string &&value, int pri
{
auto entry = get_entry(name);
assert(entry != nullptr);
- entry->set_value(std::move(value), priority);
+ entry->set_value(std::move(value), priority, false, false);
}
void core_options::set_value(std::string_view name, int value, int priority)
@@ -1063,13 +1214,13 @@ void core_options::remove_entry(core_options::entry &delentry)
// do_set_value
//-------------------------------------------------
-void core_options::do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition)
+void core_options::do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions)
{
// this is called when parsing a command line or an INI - we want to catch the option_exception and write
// any exception messages to the error stream
try
{
- curentry.set_value(std::string(data), priority);
+ curentry.set_value(std::string(data), priority, false, perform_substitutions);
}
catch (options_warning_exception &ex)
{
@@ -1156,7 +1307,7 @@ void core_options::simple_entry::revert(int priority_hi, int priority_lo)
// if our priority is within the range, revert to the default
if (priority() <= priority_hi && priority() >= priority_lo)
{
- set_value(std::string(default_value()), priority(), true);
+ set_value(std::string(m_defdata_unsubst), priority(), true, true);
set_priority(OPTION_PRIORITY_DEFAULT);
}
}
diff --git a/src/lib/util/options.h b/src/lib/util/options.h
index 57ca7160203..99db519a5e5 100644
--- a/src/lib/util/options.h
+++ b/src/lib/util/options.h
@@ -104,7 +104,9 @@ public:
BOOLEAN, // boolean option
INTEGER, // integer option
FLOAT, // floating-point option
- STRING // string option
+ STRING, // string option
+ PATH, // single path option
+ MULTIPATH // semicolon-delimited paths option
};
// information about a single entry in the options
@@ -128,6 +130,7 @@ public:
const std::vector<std::string> &names() const noexcept { return m_names; }
const std::string &name() const noexcept { return m_names[0]; }
virtual const char *value() const noexcept;
+ virtual const char *value_unsubstituted() const noexcept;
int priority() const noexcept { return m_priority; }
void set_priority(int priority) noexcept { m_priority = priority; }
option_type type() const noexcept { return m_type; }
@@ -138,18 +141,20 @@ public:
bool has_range() const noexcept;
// mutators
- void set_value(std::string &&newvalue, int priority, bool always_override = false);
+ void copy_from(const entry &that, bool always_override);
+ void set_value(std::string &&newvalue, int priority, bool always_override = false, bool perform_substitutions = false);
virtual void set_default_value(std::string &&newvalue);
void set_description(const char *description) { m_description = description; }
void set_value_changed_handler(std::function<void(const char *)> &&handler) { m_value_changed_handler = std::move(handler); }
virtual void revert(int priority_hi, int priority_lo) { }
protected:
- virtual void internal_set_value(std::string &&newvalue) = 0;
+ virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) = 0;
+ virtual bool internal_copy_value(const entry &that);
- private:
void validate(const std::string &value);
+ private:
const std::vector<std::string> m_names;
int m_priority;
core_options::option_type m_type;
@@ -231,6 +236,7 @@ private:
// getters
virtual const char *value() const noexcept override;
+ virtual const char *value_unsubstituted() const noexcept override;
virtual const char *minimum() const noexcept override;
virtual const char *maximum() const noexcept override;
virtual const std::string &default_value() const noexcept override;
@@ -239,14 +245,19 @@ private:
virtual void set_default_value(std::string &&newvalue) override;
protected:
- virtual void internal_set_value(std::string &&newvalue) override;
+ virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override;
+ virtual bool internal_copy_value(const entry &that) override;
private:
// internal state
std::string m_data; // data for this item
+ std::string m_data_unsubst; // data for this item, prior to any substitution
std::string m_defdata; // default data for this item
+ std::string m_defdata_unsubst; // default data for this item, prior to any substitution
std::string m_minimum; // minimum value
std::string m_maximum; // maximum value
+
+ std::string type_specific_substitutions(std::string_view s) const noexcept;
};
// used internally in core_options
@@ -259,7 +270,7 @@ private:
// internal helpers
void add_to_entry_map(const std::string &name, entry::shared_ptr &entry);
- void do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition);
+ void do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions);
void throw_options_exception_if_appropriate(condition_type condition, std::ostringstream &error_stream);
// internal state