summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author npwoods <npwoods@alumni.cmu.edu>2017-06-24 22:48:56 -0400
committer Vas Crabb <cuavas@users.noreply.github.com>2017-06-25 12:48:56 +1000
commitb193e05cd7c8456a2648d43854645da84f56ddbd (patch)
tree406d4dca91e403acdcea26379bcf65e567ca4834 /src/lib
parent1b28df0b39225c3017e7bc843c14b58f548162ac (diff)
Overhaul to how MAME handles options, take two (#2341)
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/util/options.cpp980
-rw-r--r--src/lib/util/options.h298
2 files changed, 713 insertions, 565 deletions
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp
index 2c56843947a..e989d6e4204 100644
--- a/src/lib/util/options.cpp
+++ b/src/lib/util/options.cpp
@@ -64,244 +64,479 @@ namespace
}
};
+//**************************************************************************
+// OPTIONS EXCEPTION CLASS
+//**************************************************************************
+
+//-------------------------------------------------
+// options_exception - constructor
+//-------------------------------------------------
+
+options_exception::options_exception(std::string &&message)
+ : m_message(std::move(message))
+{
+}
+
+
+//-------------------------------------------------
+// options_warning_exception - constructor
+//-------------------------------------------------
+
+options_warning_exception::options_warning_exception(std::string &&message)
+ : options_exception(std::move(message))
+{
+}
+
+
+//-------------------------------------------------
+// options_error_exception - constructor
+//-------------------------------------------------
+
+options_error_exception::options_error_exception(std::string &&message)
+ : options_exception(std::move(message))
+{
+}
+
//**************************************************************************
-// CORE OPTIONS ENTRY
+// CORE OPTIONS ENTRY BASE CLASS
//**************************************************************************
//-------------------------------------------------
// entry - constructor
//-------------------------------------------------
-core_options::entry::entry(const char *name, const char *description, uint32_t flags, const char *defvalue)
- : m_next(nullptr),
- m_flags(flags),
- m_error_reported(false),
- m_priority(OPTION_PRIORITY_DEFAULT),
- m_description(description),
- m_changed(false)
+core_options::entry::entry(std::vector<std::string> &&names, core_options::option_type type, const char *description)
+ : m_names(std::move(names))
+ , m_priority(OPTION_PRIORITY_DEFAULT)
+ , m_type(type)
+ , m_description(description)
{
- // copy in the name(s) as appropriate
- if (name != nullptr)
+ assert(m_names.empty() == (m_type == option_type::HEADER));
+}
+
+core_options::entry::entry(std::string &&name, core_options::option_type type, const char *description)
+ : entry(std::vector<std::string>({ std::move(name) }), type, description)
+{
+}
+
+
+//-------------------------------------------------
+// entry - destructor
+//-------------------------------------------------
+
+core_options::entry::~entry()
+{
+}
+
+
+//-------------------------------------------------
+// entry::value
+//-------------------------------------------------
+
+const char *core_options::entry::value() const
+{
+ // returning 'nullptr' from here signifies a value entry that is essentially "write only"
+ // and cannot be meaningfully persisted (e.g. - a command or the software name)
+ return nullptr;
+}
+
+
+//-------------------------------------------------
+// entry::set_value
+//-------------------------------------------------
+
+void core_options::entry::set_value(std::string &&newvalue, int priority_value, 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 || priority_value >= priority())
{
- // first extract any range
- std::string namestr(name);
- int lparen = namestr.find_first_of('(',0);
- int dash = namestr.find_first_of('-',lparen + 1);
- int rparen = namestr.find_first_of(')',dash + 1);
- if (lparen != -1 && dash != -1 && rparen != -1)
+ internal_set_value(std::move(newvalue));
+ m_priority = priority_value;
+
+ // invoke the value changed handler, if appropriate
+ if (m_value_changed_handler)
+ m_value_changed_handler(value());
+ }
+}
+
+
+//-------------------------------------------------
+// entry::set_default_value
+//-------------------------------------------------
+
+void core_options::entry::set_default_value(std::string &&newvalue)
+{
+ // set_default_value() is not necessarily supported for all entry types
+ throw false;
+}
+
+
+//-------------------------------------------------
+// entry::validate
+//-------------------------------------------------
+
+void core_options::entry::validate(const std::string &data)
+{
+ float fval;
+ int ival;
+
+ switch (type())
+ {
+ case option_type::BOOLEAN:
+ // booleans must be 0 or 1
+ if (sscanf(data.c_str(), "%d", &ival) != 1 || ival < 0 || ival > 1)
+ throw options_warning_exception("Illegal boolean value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+ break;
+
+ case option_type::INTEGER:
+ // integers must be integral
+ if (sscanf(data.c_str(), "%d", &ival) != 1)
+ throw options_warning_exception("Illegal integer value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+
+ // range checking
+ if (has_range())
{
- strtrimspace(m_minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1))));
- strtrimspace(m_maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1))));
- namestr.erase(lparen, rparen + 1 - lparen);
+ int minimum_integer = atoi(minimum());
+ int maximum_integer = atoi(maximum());
+ if (ival < minimum_integer || ival > maximum_integer)
+ throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be between %d and %d); reverting to %s\n", name(), data, minimum_integer, maximum_integer, value());
}
+ break;
- // then chop up any semicolon-separated names
- int semi;
- int nameindex = 0;
- while ((semi = namestr.find_first_of(';')) != -1 && nameindex < ARRAY_LENGTH(m_name))
+ case option_type::FLOAT:
+ if (sscanf(data.c_str(), "%f", &fval) != 1)
+ throw options_warning_exception("Illegal float value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+
+ // range checking
+ if (has_range())
{
- m_name[nameindex++].assign(namestr.substr(0, semi));
- namestr.erase(0, semi + 1);
+ float minimum_float = atof(minimum());
+ float maximum_float = atof(maximum());
+ if (fval < minimum_float || fval > maximum_float)
+ throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be between %f and %f); reverting to %s\n", name(), data, minimum_float, maximum_float, value());
}
+ break;
- // finally add the last item
- if (nameindex < ARRAY_LENGTH(m_name))
- m_name[nameindex++] = namestr;
+ case OPTION_STRING:
+ // strings can be anything
+ break;
+
+ case OPTION_INVALID:
+ case OPTION_HEADER:
+ default:
+ // anything else is invalid
+ throw options_error_exception("Attempted to set invalid option %s\n", name());
}
+}
- // set the default value
- if (defvalue != nullptr)
- m_defdata = defvalue;
- m_data = m_defdata;
+
+//-------------------------------------------------
+// entry::minimum
+//-------------------------------------------------
+
+const char *core_options::entry::minimum() const
+{
+ return nullptr;
}
//-------------------------------------------------
-// set_value - update our data value
+// entry::maximum
//-------------------------------------------------
-void core_options::entry::set_value(const char *newdata, int priority)
+const char *core_options::entry::maximum() const
{
- // ignore if we don't have priority
- if (priority < m_priority)
- return;
+ return nullptr;
+}
+
- // set the data and priority, then bump the sequence
- m_data = newdata;
- m_priority = priority;
+//-------------------------------------------------
+// entry::has_range
+//-------------------------------------------------
+
+bool core_options::entry::has_range() const
+{
+ return minimum() && maximum();
}
//-------------------------------------------------
-// set_default_value - set the default value of
-// an option, and reset the current value to it
+// entry::default_value
//-------------------------------------------------
-void core_options::entry::set_default_value(const char *defvalue)
+const std::string &core_options::entry::default_value() const
{
- m_data = defvalue;
- m_defdata = defvalue;
- m_priority = OPTION_PRIORITY_DEFAULT;
+ // I don't really want this generally available, but MewUI seems to need it. Please
+ // do not use
+ throw false;
}
+//**************************************************************************
+// CORE OPTIONS SIMPLE ENTRYCLASS
+//**************************************************************************
+
//-------------------------------------------------
-// set_description - set the description of
-// an option
+// simple_entry - constructor
//-------------------------------------------------
-void core_options::entry::set_description(const char *description)
+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_minimum(std::move(minimum))
+ , m_maximum(std::move(maximum))
{
- m_description = description;
+ m_data = m_defdata;
}
-void core_options::entry::set_flag(uint32_t mask, uint32_t flag)
+//-------------------------------------------------
+// simple_entry - destructor
+//-------------------------------------------------
+
+core_options::simple_entry::~simple_entry()
{
- m_flags = ( m_flags & mask ) | flag;
}
//-------------------------------------------------
-// revert - revert back to our default if we are
-// within the given priority range
+// simple_entry::value
//-------------------------------------------------
-void core_options::entry::revert(int priority_hi, int priority_lo)
+const char *core_options::simple_entry::value() const
{
- // if our priority is within the range, revert to the default
- if (m_priority <= priority_hi && m_priority >= priority_lo)
+ const char *result;
+ switch (type())
{
- m_data = m_defdata;
- m_priority = OPTION_PRIORITY_DEFAULT;
+ case core_options::option_type::BOOLEAN:
+ case core_options::option_type::INTEGER:
+ case core_options::option_type::FLOAT:
+ case core_options::option_type::STRING:
+ result = m_data.c_str();
+ break;
+
+ default:
+ // this is an option type for which returning a value is
+ // a meaningless operation (e.g. - core_options::option_type::COMMAND)
+ result = nullptr;
+ break;
}
+ return result;
}
-
-//**************************************************************************
-// CORE OPTIONS
-//**************************************************************************
-
//-------------------------------------------------
-// core_options - constructor
+// simple_entry::default_value
//-------------------------------------------------
-core_options::core_options()
+const std::string &core_options::simple_entry::default_value() const
{
+ // only MewUI seems to need this; please don't use
+ return m_defdata;
}
-core_options::core_options(const options_entry *entrylist)
+
+//-------------------------------------------------
+// internal_set_value
+//-------------------------------------------------
+
+void core_options::simple_entry::internal_set_value(std::string &&newvalue)
{
- add_entries(entrylist);
+ m_data = std::move(newvalue);
}
-core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2)
+
+//-------------------------------------------------
+// set_default_value
+//-------------------------------------------------
+
+void core_options::simple_entry::set_default_value(std::string &&newvalue)
{
- add_entries(entrylist1);
- add_entries(entrylist2);
+ m_defdata = std::move(newvalue);
}
-core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3)
+
+//-------------------------------------------------
+// minimum
+//-------------------------------------------------
+
+const char *core_options::simple_entry::minimum() const
{
- add_entries(entrylist1);
- add_entries(entrylist2);
- add_entries(entrylist3);
+ return m_minimum.c_str();
}
-core_options::core_options(const core_options &src)
+
+//-------------------------------------------------
+// maximum
+//-------------------------------------------------
+
+const char *core_options::simple_entry::maximum() const
{
- copyfrom(src);
+ return m_maximum.c_str();
}
+//**************************************************************************
+// CORE OPTIONS
+//**************************************************************************
+
//-------------------------------------------------
-// ~core_options - destructor
+// core_options - constructor
//-------------------------------------------------
-core_options::~core_options()
+core_options::core_options()
{
}
//-------------------------------------------------
-// operator= - assignment operator
+// ~core_options - destructor
//-------------------------------------------------
-core_options &core_options::operator=(const core_options &rhs)
+core_options::~core_options()
{
- // ignore self-assignment
- if (this != &rhs)
- copyfrom(rhs);
- return *this;
}
//-------------------------------------------------
-// operator== - compare two sets of options
+// add_entry - adds an entry
//-------------------------------------------------
-bool core_options::operator==(const core_options &rhs)
+void core_options::add_entry(entry::shared_ptr &&entry, const char *after_header)
{
- // iterate over options in the first list
- for (entry &curentry : m_entrylist)
- if (!curentry.is_header())
- {
- // if the values differ, return false
- if (strcmp(curentry.value(), rhs.value(curentry.name())) != 0)
- return false;
- }
+ // update the entry map
+ for (const std::string &name : entry->names())
+ {
+ // append the entry
+ add_to_entry_map(std::string(name), entry);
+
+ // for booleans, add the "-noXYZ" option as well
+ if (entry->type() == option_type::BOOLEAN)
+ add_to_entry_map(std::string("no") + name, entry);
+ }
- return true;
+ // and add the entry to the vector
+ m_entries.emplace_back(std::move(entry));
}
//-------------------------------------------------
-// operator!= - compare two sets of options
+// add_to_entry_map - adds an entry to the entry
+// map
//-------------------------------------------------
-bool core_options::operator!=(const core_options &rhs)
+void core_options::add_to_entry_map(std::string &&name, entry::shared_ptr &entry)
{
- return !operator==(rhs);
+ // it is illegal to call this method for something that already ex0ists
+ assert(m_entrymap.find(name) == m_entrymap.end());
+
+ // append the entry
+ m_entrymap.emplace(std::make_pair(name, entry::weak_ptr(entry)));
}
//-------------------------------------------------
-// add_entry - add an entry to the current
-// options set
+// add_entry - adds an entry based on an
+// options_entry
//-------------------------------------------------
-void core_options::add_entry(const char *name, const char *description, uint32_t flags, const char *defvalue, bool override_existing)
+void core_options::add_entry(const options_entry &opt, bool override_existing)
{
- // allocate a new entry
- auto newentry = global_alloc(entry(name, description, flags, defvalue));
- if (newentry->name() != nullptr)
+ std::vector<std::string> names;
+ std::string minimum, maximum;
+
+ // copy in the name(s) as appropriate
+ if (opt.name)
{
- // see if we match an existing entry
- auto checkentry = m_entrymap.find(newentry->name());
- if (checkentry != m_entrymap.end())
+ // first extract any range
+ std::string namestr(opt.name);
+ int lparen = namestr.find_first_of('(', 0);
+ int dash = namestr.find_first_of('-', lparen + 1);
+ int rparen = namestr.find_first_of(')', dash + 1);
+ if (lparen != -1 && dash != -1 && rparen != -1)
{
- entry *existing = checkentry->second;
- // if we're overriding existing entries, then remove the old one
- if (override_existing)
- m_entrylist.remove(*existing);
+ strtrimspace(minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1))));
+ strtrimspace(maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1))));
+ namestr.erase(lparen, rparen + 1 - lparen);
+ }
+
+ // then chop up any semicolon-separated names
+ size_t semi;
+ while ((semi = namestr.find_first_of(';')) != std::string::npos)
+ {
+ names.push_back(namestr.substr(0, semi));
+ namestr.erase(0, semi + 1);
+ }
+
+ // finally add the last item
+ names.push_back(std::move(namestr));
+ }
- // otherwise, just override the default and current values and throw out the new entry
+ // we might be called with an existing entry
+ entry::shared_ptr existing_entry;
+ do
+ {
+ for (const std::string &name : names)
+ {
+ existing_entry = get_entry(name.c_str());
+ if (existing_entry)
+ break;
+ }
+
+ if (existing_entry)
+ {
+ if (override_existing)
+ remove_entry(*existing_entry);
else
- {
- existing->set_default_value(newentry->value());
- global_free(newentry);
return;
- }
}
+ } while (existing_entry);
+
+ // set the default value
+ std::string defdata = opt.defvalue ? opt.defvalue : "";
+
+ // create and add the entry
+ add_entry(
+ std::move(names),
+ opt.description,
+ opt.type,
+ std::move(defdata),
+ std::move(minimum),
+ std::move(maximum));
+}
+
+
+//-------------------------------------------------
+// add_entry
+//-------------------------------------------------
+
+void core_options::add_entry(std::vector<std::string> &&names, const char *description, option_type type, std::string &&default_value, std::string &&minimum, std::string &&maximum)
+{
+ // create the entry
+ entry::shared_ptr new_entry = std::make_shared<simple_entry>(
+ std::move(names),
+ description,
+ type,
+ std::move(default_value),
+ std::move(minimum),
+ std::move(maximum));
+
+ // and add it
+ add_entry(std::move(new_entry));
+}
- // need to call value_changed() with initial value
- value_changed(newentry->name(), newentry->value());
- }
- // add us to the list and maps
- append_entry(*newentry);
+//-------------------------------------------------
+// add_header
+//-------------------------------------------------
+
+void core_options::add_header(const char *description)
+{
+ add_entry(std::vector<std::string>(), description, option_type::HEADER);
}
@@ -313,8 +548,8 @@ void core_options::add_entry(const char *name, const char *description, uint32_t
void core_options::add_entries(const options_entry *entrylist, bool override_existing)
{
// loop over entries until we hit a nullptr name
- for ( ; entrylist->name != nullptr || (entrylist->flags & OPTION_HEADER) != 0; entrylist++)
- add_entry(*entrylist, override_existing);
+ for (int i = 0; entrylist[i].name || entrylist[i].type == option_type::HEADER; i++)
+ add_entry(entrylist[i], override_existing);
}
@@ -325,13 +560,8 @@ void core_options::add_entries(const options_entry *entrylist, bool override_exi
void core_options::set_default_value(const char *name, const char *defvalue)
{
- // find the entry and bail if we can't
- auto curentry = m_entrymap.find(name);
- if (curentry == m_entrymap.end())
- return;
-
// update the data and default data
- curentry->second->set_default_value(defvalue);
+ get_entry(name)->set_default_value(defvalue);
}
@@ -342,13 +572,8 @@ void core_options::set_default_value(const char *name, const char *defvalue)
void core_options::set_description(const char *name, const char *description)
{
- // find the entry and bail if we can't
- auto curentry = m_entrymap.find(name);
- if (curentry == m_entrymap.end())
- return;
-
// update the data and default data
- curentry->second->set_description(description);
+ get_entry(name)->set_description(description);
}
@@ -357,10 +582,12 @@ void core_options::set_description(const char *name, const char *description)
// command line arguments
//-------------------------------------------------
-bool core_options::parse_command_line(std::vector<std::string> &args, int priority, std::string &error_string)
+void core_options::parse_command_line(const std::vector<std::string> &args, int priority, bool ignore_unknown_options)
{
+ std::ostringstream error_stream;
+ condition_type condition = condition_type::NONE;
+
// reset the errors and the command
- error_string.clear();
m_command.clear();
// we want to identify commands first
@@ -368,23 +595,20 @@ bool core_options::parse_command_line(std::vector<std::string> &args, int priori
{
if (!args[arg].empty() && args[arg][0] == '-')
{
- auto curentry = m_entrymap.find(&args[arg][1]);
- if (curentry != m_entrymap.end() && curentry->second->type() == OPTION_COMMAND)
+ auto curentry = get_entry(&args[arg][1]);
+ if (curentry && curentry->type() == OPTION_COMMAND)
{
// can only have one command
if (!m_command.empty())
- {
- error_string.append(string_format("Error: multiple commands specified -%s and %s\n", m_command, args[arg]));
- return false;
- }
- m_command = curentry->second->name();
+ throw options_error_exception("Error: multiple commands specified -%s and %s\n", m_command, args[arg]);
+
+ m_command = curentry->name();
}
}
}
// iterate through arguments
int unadorned_index = 0;
- size_t new_argc = 1;
for (size_t arg = 1; arg < args.size(); arg++)
{
// determine the entry name to search for
@@ -396,39 +620,26 @@ bool core_options::parse_command_line(std::vector<std::string> &args, int priori
if (is_unadorned && !m_command.empty())
{
m_command_arguments.push_back(std::move(args[arg]));
- args[arg].clear();
+ command_argument_processed();
continue;
}
// find our entry; if not found, continue
- auto curentry = m_entrymap.find(optionname);
- if (curentry == m_entrymap.end())
+ auto curentry = get_entry(optionname);
+ if (!curentry)
{
- // we need to relocate this option
- if (new_argc != arg)
- args[new_argc] = std::move(args[arg]);
- new_argc++;
-
- if (!is_unadorned)
- {
- arg++;
- if (arg < args.size())
- {
- if (new_argc != arg)
- args[new_argc] = std::move(args[arg]);
- new_argc++;
- }
- }
+ if (!ignore_unknown_options)
+ throw options_error_exception("Error: unknown option: -%s\n", optionname);
continue;
}
// at this point, we've already processed commands
- if (curentry->second->type() == OPTION_COMMAND)
+ if (curentry->type() == OPTION_COMMAND)
continue;
// get the data for this argument, special casing booleans
std::string newdata;
- if (curentry->second->type() == OPTION_BOOLEAN)
+ if (curentry->type() == option_type::BOOLEAN)
{
newdata = (strncmp(&curarg[1], "no", 2) == 0) ? "0" : "1";
}
@@ -438,22 +649,19 @@ bool core_options::parse_command_line(std::vector<std::string> &args, int priori
}
else if (arg + 1 < args.size())
{
- args[arg++].clear();
- newdata = std::move(args[arg]);
+ newdata = args[++arg];
}
else
{
- error_string.append(string_format("Error: option %s expected a parameter\n", curarg));
- return false;
+ throw options_error_exception("Error: option %s expected a parameter\n", curarg);
}
- args[arg].clear();
// set the new data
- validate_and_set_data(*curentry->second, std::move(newdata), priority, error_string);
+ do_set_value(*curentry, std::move(newdata), priority, error_stream, condition);
}
- args.resize(new_argc);
- return true;
+ // did we have any errors that may need to be aggregated?
+ throw_options_exception_if_appropriate(condition, error_stream);
}
@@ -462,8 +670,11 @@ bool core_options::parse_command_line(std::vector<std::string> &args, int priori
// an INI file
//-------------------------------------------------
-bool core_options::parse_ini_file(util::core_file &inifile, int priority, bool ignore_unknown_options, std::string &error_string)
+void core_options::parse_ini_file(util::core_file &inifile, int priority, bool ignore_unknown_options, bool always_override)
{
+ std::ostringstream error_stream;
+ condition_type condition = condition_type::NONE;
+
// loop over lines in the file
char buffer[4096];
while (inifile.gets(buffer, ARRAY_LENGTH(buffer)) != nullptr)
@@ -487,7 +698,8 @@ bool core_options::parse_ini_file(util::core_file &inifile, int priority, bool i
// if we hit the end early, print a warning and continue
if (*temp == 0)
{
- error_string.append(string_format("Warning: invalid line in INI: %s", buffer));
+ condition = std::max(condition, condition_type::WARN);
+ util::stream_format(error_stream, "Warning: invalid line in INI: %s", buffer);
continue;
}
@@ -507,79 +719,73 @@ bool core_options::parse_ini_file(util::core_file &inifile, int priority, bool i
*temp = 0;
// find our entry
- auto curentry = m_entrymap.find(optionname);
- if (curentry == m_entrymap.end())
+ entry::shared_ptr curentry = get_entry(optionname);
+ if (!curentry)
{
if (!ignore_unknown_options)
- error_string.append(string_format("Warning: unknown option in INI: %s\n", optionname));
+ {
+ condition = std::max(condition, condition_type::WARN);
+ util::stream_format(error_stream, "Warning: unknown option in INI: %s\n", optionname);
+ }
continue;
}
// set the new data
std::string data = optiondata;
trim_spaces_and_quotes(data);
- validate_and_set_data(*curentry->second, std::move(data), priority, error_string);
+ do_set_value(*curentry, std::move(data), priority, error_stream, condition);
}
- return true;
+
+ // did we have any errors that may need to be aggregated?
+ throw_options_exception_if_appropriate(condition, error_stream);
}
//-------------------------------------------------
-// pluck_from_command_line - finds a specific
-// value from within a command line
+// throw_options_exception_if_appropriate
//-------------------------------------------------
-bool core_options::pluck_from_command_line(std::vector<std::string> &args, const std::string &optionname, std::string &result)
+void core_options::throw_options_exception_if_appropriate(core_options::condition_type condition, std::ostringstream &error_stream)
{
- // find this entry within the options (it is illegal to call this with a non-existant option
- // so we assert if not present)
- auto curentry = m_entrymap.find(optionname);
- assert(curentry != m_entrymap.end());
-
- // build a vector with potential targets
- std::vector<std::string> targets;
- const char *potential_target;
- int index = 0;
- while ((potential_target = curentry->second->name(index++)) != nullptr)
+ switch(condition)
{
- // not supporting unadorned options for now
- targets.push_back(std::string("-") + potential_target);
- }
+ case condition_type::NONE:
+ // do nothing
+ break;
- // find each of the targets in the argv array
- for (int i = 1; i < args.size() - 1; i++)
- {
- auto const iter = std::find_if(
- targets.begin(),
- targets.end(),
- [&args, i](const std::string &targ) { return targ == args[i]; });
- if (iter != targets.end())
- {
- // get the result
- result = std::move(args[i + 1]);
+ case condition_type::WARN:
+ throw options_warning_exception(error_stream.str());
- // remove this arguments from the list
- auto const pos = std::next(args.begin(), i);
- args.erase(pos, std::next(pos, 2));
- return true;
- }
- }
+ case condition_type::ERR:
+ throw options_error_exception(error_stream.str());
- result.clear();
- return false;
+ default:
+ // should not get here
+ throw false;
+ }
}
//-------------------------------------------------
-// revert - revert options at or below a certain
-// priority back to their defaults
+// copy_from
//-------------------------------------------------
-void core_options::revert(int priority_hi, int priority_lo)
+void core_options::copy_from(const core_options &that)
{
- // iterate over options and revert to defaults if below the given priority
- for (entry &curentry : m_entrylist)
- curentry.revert(priority_hi, priority_lo);
+ for (auto &dest_entry : m_entries)
+ {
+ if (dest_entry->names().size() > 0)
+ {
+ // identify the source entry
+ const entry::shared_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);
+ }
+ }
+ }
}
@@ -600,47 +806,34 @@ std::string core_options::output_ini(const core_options *diff) const
std::string overridden_value;
// loop over all items
- for (entry &curentry : m_entrylist)
+ for (auto &curentry : m_entries)
{
- const char *name = curentry.name();
- const char *value;
- switch (override_get_value(name, overridden_value))
+ if (curentry->type() == option_type::HEADER)
{
- case override_get_value_result::NONE:
- default:
- value = curentry.value();
- break;
-
- case override_get_value_result::SKIP:
- continue;
-
- case override_get_value_result::OVERRIDE:
- value = overridden_value.c_str();
- break;
+ // header: record description
+ last_header = curentry->description();
}
- bool is_unadorned = false;
-
- // check if it's unadorned
- if (name && strlen(name) && !strcmp(name, core_options::unadorned(unadorned_index)))
+ else
{
- unadorned_index++;
- is_unadorned = true;
- }
+ const std::string &name(curentry->name());
+ const char *value(curentry->value());
- // header: record description
- if (curentry.is_header())
- last_header = curentry.description();
+ // check if it's unadorned
+ bool is_unadorned = false;
+ if (name == core_options::unadorned(unadorned_index))
+ {
+ unadorned_index++;
+ is_unadorned = true;
+ }
- // otherwise, output entries for all non-command items
- else if (!curentry.is_command())
- {
- if (!curentry.is_internal())
+ // output entries for all non-command items (items with value)
+ if (value)
{
// look up counterpart in diff, if diff is specified
- if (diff == nullptr || strcmp(value, diff->value(name)) != 0)
+ if (!diff || strcmp(value, diff->value(name.c_str())))
{
// output header, if we have one
- if (last_header != nullptr)
+ if (last_header)
{
if (num_valid_headers++)
buffer << '\n';
@@ -651,7 +844,7 @@ std::string core_options::output_ini(const core_options *diff) const
// and finally output the data, skip if unadorned
if (!is_unadorned)
{
- if (strchr(value, ' ') != nullptr)
+ if (strchr(value, ' '))
util::stream_format(buffer, "%-25s \"%s\"\n", name, value);
else
util::stream_format(buffer, "%-25s %s\n", name, value);
@@ -674,15 +867,15 @@ std::string core_options::output_help() const
std::ostringstream buffer;
// loop over all items
- for (entry &curentry : m_entrylist)
+ for (auto &curentry : m_entries)
{
// header: just print
- if (curentry.is_header())
- util::stream_format(buffer, "\n#\n# %s\n#\n", curentry.description());
+ if (curentry->type() == option_type::HEADER)
+ util::stream_format(buffer, "\n#\n# %s\n#\n", curentry->description());
// otherwise, output entries for all non-deprecated items
- else if (curentry.description() != nullptr)
- util::stream_format(buffer, "-%-20s%s\n", curentry.name(), curentry.description());
+ else if (curentry->description() != nullptr)
+ util::stream_format(buffer, "-%-20s%s\n", curentry->name(), curentry->description());
}
return buffer.str();
}
@@ -692,10 +885,9 @@ std::string core_options::output_help() const
// value - return the raw option value
//-------------------------------------------------
-const char *core_options::value(const char *name) const
+const char *core_options::value(const char *option) const
{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second->value() : "";
+ return get_entry(option)->value();
}
@@ -703,241 +895,133 @@ const char *core_options::value(const char *name) const
// description - return description of option
//-------------------------------------------------
-const char *core_options::description(const char *name) const
+const char *core_options::description(const char *option) const
{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second->description() : "";
+ return get_entry(option)->description();
}
+//**************************************************************************
+// LEGACY
+//**************************************************************************
+
//-------------------------------------------------
-// priority - return the priority of option
+// set_value - set the raw option value
//-------------------------------------------------
-int core_options::priority(const char *name) const
+void core_options::set_value(const std::string &name, const std::string &value, int priority)
{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second->priority() : 0;
+ set_value(name, std::string(value), priority);
}
+void core_options::set_value(const std::string &name, std::string &&value, int priority)
+{
+ get_entry(name)->set_value(std::move(value), priority);
+}
-//-------------------------------------------------
-// exists - return if option exists in list
-//-------------------------------------------------
+void core_options::set_value(const std::string &name, int value, int priority)
+{
+ set_value(name, string_format("%d", value), priority);
+}
-bool core_options::exists(const char *name) const
+void core_options::set_value(const std::string &name, float value, int priority)
{
- return (m_entrymap.find(name) != m_entrymap.end());
+ set_value(name, string_format("%f", value), priority);
}
//-------------------------------------------------
-// set_value - set the raw option value
+// remove_entry - remove an entry from our list
+// and map
//-------------------------------------------------
-bool core_options::set_value(const char *name, const char *value, int priority, std::string &error_string)
-{
- // find the entry first
- auto curentry = m_entrymap.find(name);
- if (curentry == m_entrymap.end())
- {
- error_string.append(string_format("Attempted to set unknown option %s\n", name));
- return false;
- }
-
- // validate and set the item normally
- return validate_and_set_data(*curentry->second, value, priority, error_string);
-}
-
-bool core_options::set_value(const char *name, int value, int priority, std::string &error_string)
+void core_options::remove_entry(core_options::entry &delentry)
{
- return set_value(name, string_format("%d", value).c_str(), priority, error_string);
+ // find this in m_entries
+ auto iter = std::find_if(
+ m_entries.begin(),
+ m_entries.end(),
+ [&delentry](const auto &x) { return &*x == &delentry; });
+ assert(iter != m_entries.end());
+
+ // erase each of the items out of the entry map
+ for (const std::string &name : delentry.names())
+ m_entrymap.erase(name);
+
+ // finally erase it
+ m_entries.erase(iter);
}
-bool core_options::set_value(const char *name, float value, int priority, std::string &error_string)
-{
- return set_value(name, string_format("%f", value).c_str(), priority, error_string);
-}
+//-------------------------------------------------
+// do_set_value
+//-------------------------------------------------
-void core_options::set_flag(const char *name, uint32_t mask, uint32_t flag)
+void core_options::do_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition)
{
- // find the entry first
- auto curentry = m_entrymap.find(name);
- if ( curentry == m_entrymap.end())
+ // 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
{
- return;
+ curentry.set_value(std::move(data), priority);
}
- curentry->second->set_flag(mask, flag);
-}
-
-void core_options::mark_changed(const char* name)
-{
- // find the entry first
- auto curentry = m_entrymap.find(name);
- if (curentry == m_entrymap.end())
+ catch (options_warning_exception &ex)
{
- return;
+ // we want to aggregate option exceptions
+ error_stream << ex.message();
+ condition = std::max(condition, condition_type::WARN);
+ }
+ catch (options_error_exception &ex)
+ {
+ // we want to aggregate option exceptions
+ error_stream << ex.message();
+ condition = std::max(condition, condition_type::ERR);
}
- curentry->second->mark_changed();
}
+
//-------------------------------------------------
-// reset - reset the options state, removing
-// everything
+// get_entry
//-------------------------------------------------
-void core_options::reset()
+const core_options::entry::shared_ptr core_options::get_entry(const std::string &name) const
+{
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
+}
+
+core_options::entry::shared_ptr core_options::get_entry(const std::string &name)
{
- m_entrylist.reset();
- m_entrymap.clear();
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
}
//-------------------------------------------------
-// append_entry - append an entry to our list
-// and index it in the map
+// set_value_changed_handler
//-------------------------------------------------
-void core_options::append_entry(core_options::entry &newentry)
+void core_options::set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler)
{
- m_entrylist.append(newentry);
-
- // if we have names, add them to the map
- for (int name = 0; name < ARRAY_LENGTH(newentry.m_name); name++)
- if (newentry.name(name) != nullptr)
- {
- m_entrymap.insert(std::make_pair(newentry.name(name), &newentry));
- // for boolean options add a "no" variant as well
- if (newentry.type() == OPTION_BOOLEAN)
- m_entrymap.insert(std::make_pair(std::string("no").append(newentry.name(name)), &newentry));
- }
+ get_entry(name)->set_value_changed_handler(std::move(handler));
}
//-------------------------------------------------
-// remove_entry - remove an entry from our list
-// and map
+// header_exists
//-------------------------------------------------
-void core_options::remove_entry(core_options::entry &delentry)
+bool core_options::header_exists(const char *description) const
{
- // remove all names from the map
- for (int name = 0; name < ARRAY_LENGTH(delentry.m_name); name++)
- if (!delentry.m_name[name].empty())
- {
- auto entry = m_entrymap.find(delentry.m_name[name]);
- if (entry!= m_entrymap.end()) m_entrymap.erase(entry);
- }
-
- // remove the entry from the list
- m_entrylist.remove(delentry);
-}
-
-/**
- * @fn void core_options::copyfrom(const core_options &src)
- *
- * @brief -------------------------------------------------
- * copyfrom - copy options from another set
- * -------------------------------------------------.
- *
- * @param src Source for the.
- */
-
-void core_options::copyfrom(const core_options &src)
-{
- // reset ourselves first
- reset();
-
- // iterate through the src options and make our own
- for (entry &curentry : src.m_entrylist)
- append_entry(*global_alloc(entry(curentry.name(), curentry.description(), curentry.flags(), curentry.default_value())));
-}
-
-/**
- * @fn bool core_options::validate_and_set_data(core_options::entry &curentry, const char *newdata, int priority, std::string &error_string)
- *
- * @brief -------------------------------------------------
- * validate_and_set_data - make sure the data is of the appropriate type and within
- * range, then set it
- * -------------------------------------------------.
- *
- * @param [in,out] curentry The curentry.
- * @param newdata The newdata.
- * @param priority The priority.
- * @param [in,out] error_string The error string.
- *
- * @return true if it succeeds, false if it fails.
- */
-
-bool core_options::validate_and_set_data(core_options::entry &curentry, std::string &&data, int priority, std::string &error_string)
-{
- // let derived classes override how we set this data
- if (override_set_value(curentry.name(), data))
- return true;
-
- // validate the type of data and optionally the range
- float fval;
- int ival;
- switch (curentry.type())
- {
- // booleans must be 0 or 1
- case OPTION_BOOLEAN:
- if (sscanf(data.c_str(), "%d", &ival) != 1 || ival < 0 || ival > 1)
- {
- error_string.append(string_format("Illegal boolean value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value()));
- return false;
- }
- break;
-
- // integers must be integral
- case OPTION_INTEGER:
- if (sscanf(data.c_str(), "%d", &ival) != 1)
- {
- error_string.append(string_format("Illegal integer value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value()));
- return false;
- }
- if (curentry.has_range() && (ival < atoi(curentry.minimum()) || ival > atoi(curentry.maximum())))
+ auto iter = std::find_if(
+ m_entries.begin(),
+ m_entries.end(),
+ [description](const auto &entry)
{
- error_string.append(string_format("Out-of-range integer value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.c_str(), curentry.minimum(), curentry.maximum(), curentry.value()));
- return false;
- }
- break;
+ return entry->type() == option_type::HEADER
+ && entry->description()
+ && !strcmp(entry->description(), description);
+ });
- // floating-point values must be numeric
- case OPTION_FLOAT:
- if (sscanf(data.c_str(), "%f", &fval) != 1)
- {
- error_string.append(string_format("Illegal float value for %s: \"%s\"; reverting to %s\n", curentry.name(), data.c_str(), curentry.value()));
- return false;
- }
- if (curentry.has_range() && ((double) fval < atof(curentry.minimum()) || (double) fval > atof(curentry.maximum())))
- {
- error_string.append(string_format("Out-of-range float value for %s: \"%s\" (must be between %s and %s); reverting to %s\n", curentry.name(), data.c_str(), curentry.minimum(), curentry.maximum(), curentry.value()));
- return false;
- }
- break;
-
- // strings can be anything
- case OPTION_STRING:
- break;
-
- // anything else is invalid
- case OPTION_INVALID:
- case OPTION_HEADER:
- default:
- error_string.append(string_format("Attempted to set invalid option %s\n", curentry.name()));
- return false;
- }
-
- // set the data
- curentry.set_value(data.c_str(), priority);
- value_changed(curentry.name(), data);
- return true;
-}
-
-core_options::entry *core_options::get_entry(const char *name) const
-{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second : nullptr;
+ return iter != m_entries.end();
}
diff --git a/src/lib/util/options.h b/src/lib/util/options.h
index 71431ce870c..533f5cf4628 100644
--- a/src/lib/util/options.h
+++ b/src/lib/util/options.h
@@ -13,26 +13,13 @@
#include "corefile.h"
#include <unordered_map>
-
+#include <sstream>
//**************************************************************************
// CONSTANTS
//**************************************************************************
-// option types
-const uint32_t OPTION_TYPE_MASK = 0x0007; // up to 8 different types
-enum
-{
- OPTION_INVALID, // invalid
- OPTION_HEADER, // a header item
- OPTION_COMMAND, // a command
- OPTION_BOOLEAN, // boolean option
- OPTION_INTEGER, // integer option
- OPTION_FLOAT, // floating-point option
- OPTION_STRING // string option
-};
-
// option priorities
const int OPTION_PRIORITY_DEFAULT = 0; // defaults are at 0 priority
const int OPTION_PRIORITY_LOW = 50; // low priority
@@ -40,20 +27,55 @@ const int OPTION_PRIORITY_NORMAL = 100; // normal priority
const int OPTION_PRIORITY_HIGH = 150; // high priority
const int OPTION_PRIORITY_MAXIMUM = 255; // maximum priority
-const uint32_t OPTION_FLAG_INTERNAL = 0x40000000;
-
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
-// static structure describing a single option with its description and default value
-struct options_entry
+struct options_entry;
+
+// exception thrown by core_options when an illegal request is made
+class options_exception : public std::exception
{
- const char * name; // name on the command line
- const char * defvalue; // default value of this argument
- uint32_t flags; // flags to describe the option
- const char * description; // description for -showusage
+public:
+ const std::string &message() const { return m_message; }
+ virtual const char *what() const noexcept override { return message().c_str(); }
+
+protected:
+ options_exception(std::string &&message);
+
+private:
+ std::string m_message;
+};
+
+class options_warning_exception : public options_exception
+{
+public:
+ template <typename... Params> options_warning_exception(const char *fmt, Params &&...args)
+ : options_warning_exception(util::string_format(fmt, std::forward<Params>(args)...))
+ {
+ }
+
+ options_warning_exception(std::string &&message);
+ options_warning_exception(const options_warning_exception &) = default;
+ options_warning_exception(options_warning_exception &&) = default;
+ options_warning_exception& operator=(const options_warning_exception &) = default;
+ options_warning_exception& operator=(options_warning_exception &&) = default;
+};
+
+class options_error_exception : public options_exception
+{
+public:
+ template <typename... Params> options_error_exception(const char *fmt, Params &&...args)
+ : options_error_exception(util::string_format(fmt, std::forward<Params>(args)...))
+ {
+ }
+
+ options_error_exception(std::string &&message);
+ options_error_exception(const options_error_exception &) = default;
+ options_error_exception(options_error_exception &&) = default;
+ options_error_exception& operator=(const options_error_exception &) = default;
+ options_error_exception& operator=(options_error_exception &&) = default;
};
@@ -63,95 +85,97 @@ class core_options
static const int MAX_UNADORNED_OPTIONS = 16;
public:
+ enum class option_type
+ {
+ INVALID, // invalid
+ HEADER, // a header item
+ COMMAND, // a command
+ BOOLEAN, // boolean option
+ INTEGER, // integer option
+ FLOAT, // floating-point option
+ STRING // string option
+ };
+
// information about a single entry in the options
class entry
{
- friend class core_options;
- friend class simple_list<entry>;
-
- // construction/destruction
- entry(const char *name, const char *description, uint32_t flags = 0, const char *defvalue = nullptr);
-
public:
- // getters
- entry *next() const { return m_next; }
- const char *name(int index = 0) const { return (index < ARRAY_LENGTH(m_name) && !m_name[index].empty()) ? m_name[index].c_str() : nullptr; }
+ typedef std::shared_ptr<entry> shared_ptr;
+ typedef std::weak_ptr<entry> weak_ptr;
+
+ // constructor/destructor
+ entry(std::vector<std::string> &&names, option_type type = option_type::STRING, const char *description = nullptr);
+ entry(std::string &&name, option_type type = option_type::STRING, const char *description = nullptr);
+ entry(const entry &) = delete;
+ entry(entry &&) = delete;
+ entry& operator=(const entry &) = delete;
+ entry& operator=(entry &&) = delete;
+ virtual ~entry();
+
+ // accessors
+ const std::vector<std::string> &names() const { return m_names; }
+ const std::string &name() const { return m_names[0]; }
+ virtual const char *value() const;
+ int priority() const { return m_priority; }
+ void set_priority(int priority) { m_priority = priority; }
+ option_type type() const { return m_type; }
const char *description() const { return m_description; }
- const char *value() const { return m_data.c_str(); }
- const char *default_value() const { return m_defdata.c_str(); }
- const char *minimum() const { return m_minimum.c_str(); }
- const char *maximum() const { return m_maximum.c_str(); }
- int type() const { return (m_flags & OPTION_TYPE_MASK); }
- uint32_t flags() const { return m_flags; }
- bool is_header() const { return type() == OPTION_HEADER; }
- bool is_command() const { return type() == OPTION_COMMAND; }
- bool is_internal() const { return m_flags & OPTION_FLAG_INTERNAL; }
- bool has_range() const { return (!m_minimum.empty() && !m_maximum.empty()); }
- int priority() const { return m_priority; }
- bool is_changed() const { return m_changed; }
-
- // setters
- void set_value(const char *newvalue, int priority);
- void set_default_value(const char *defvalue);
- void set_description(const char *description);
- void set_flag(uint32_t mask, uint32_t flag);
- void mark_changed() { m_changed = true; }
- void revert(int priority_hi, int priority_lo);
+ virtual const std::string &default_value() const;
+ virtual const char *minimum() const;
+ virtual const char *maximum() const;
+ bool has_range() const;
+
+ // mutators
+ void set_value(std::string &&newvalue, int priority, bool always_override = 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); }
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) = 0;
private:
- // internal state
- entry * m_next; // link to the next data
- uint32_t m_flags; // flags from the entry
- bool m_error_reported; // have we reported an error on this option yet?
- int m_priority; // priority of the data set
- const char * m_description; // description for this item
- std::string m_name[4]; // up to 4 names for the item
- std::string m_data; // data for this item
- std::string m_defdata; // default data for this item
- std::string m_minimum; // minimum value
- std::string m_maximum; // maximum value
- bool m_changed; // changed flag
+ void validate(const std::string &value);
+
+ std::vector<std::string> m_names;
+ int m_priority;
+ core_options::option_type m_type;
+ const char * m_description;
+ std::function<void(const char *)> m_value_changed_handler;
};
// construction/destruction
core_options();
- core_options(const options_entry *entrylist);
- core_options(const options_entry *entrylist1, const options_entry *entrylist2);
- core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3);
- core_options(const core_options &src);
+ core_options(const core_options &) = delete;
+ core_options(core_options &&) = delete;
+ core_options& operator=(const core_options &) = delete;
+ core_options& operator=(core_options &&) = delete;
virtual ~core_options();
- // operators
- core_options &operator=(const core_options &rhs);
- bool operator==(const core_options &rhs);
- bool operator!=(const core_options &rhs);
-
// getters
- entry *first() const { return m_entrylist.first(); }
const std::string &command() const { return m_command; }
const std::vector<std::string> &command_arguments() const { assert(!m_command.empty()); return m_command_arguments; }
- entry *get_entry(const char *name) const;
-
- // range iterators
- using auto_iterator = simple_list<entry>::auto_iterator;
- auto_iterator begin() const { return m_entrylist.begin(); }
- auto_iterator end() const { return m_entrylist.end(); }
+ const entry::shared_ptr get_entry(const std::string &name) const;
+ entry::shared_ptr get_entry(const std::string &name);
+ const std::vector<entry::shared_ptr> &entries() const { return m_entries; }
+ bool exists(const std::string &name) const { return get_entry(name) != nullptr; }
+ bool header_exists(const char *description) const;
// configuration
- void add_entry(const char *name, const char *description, uint32_t flags = 0, const char *defvalue = nullptr, bool override_existing = false);
- void add_entry(const options_entry &data, bool override_existing = false) { add_entry(data.name, data.description, data.flags, data.defvalue, override_existing); }
+ void add_entry(entry::shared_ptr &&entry, const char *after_header = nullptr);
+ void add_entry(const options_entry &entry, bool override_existing = false);
+ void add_entry(std::vector<std::string> &&names, const char *description, option_type type, std::string &&default_value = "", std::string &&minimum = "", std::string &&maximum = "");
+ void add_header(const char *description);
void add_entries(const options_entry *entrylist, bool override_existing = false);
void set_default_value(const char *name, const char *defvalue);
void set_description(const char *name, const char *description);
void remove_entry(entry &delentry);
+ void set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler);
// parsing/input
- bool parse_command_line(std::vector<std::string> &args, int priority, std::string &error_string);
- bool parse_ini_file(util::core_file &inifile, int priority, bool ignore_unknown_options, std::string &error_string);
- bool pluck_from_command_line(std::vector<std::string> &args, const std::string &name, std::string &result);
-
- // reverting
- void revert(int priority_hi = OPTION_PRIORITY_MAXIMUM, int priority_lo = OPTION_PRIORITY_DEFAULT);
+ void parse_command_line(const std::vector<std::string> &args, int priority, bool ignore_unknown_options = false);
+ void parse_ini_file(util::core_file &inifile, int priority, bool ignore_unknown_options, bool always_override);
+ void copy_from(const core_options &that);
// output
std::string output_ini(const core_options *diff = nullptr) const;
@@ -160,52 +184,92 @@ public:
// reading
const char *value(const char *option) const;
const char *description(const char *option) const;
- int priority(const char *option) const;
- bool bool_value(const char *name) const { return (atoi(value(name)) != 0); }
- int int_value(const char *name) const { return atoi(value(name)); }
- float float_value(const char *name) const { return atof(value(name)); }
- bool exists(const char *name) const;
+ bool bool_value(const char *option) const { return int_value(option) != 0; }
+ int int_value(const char *option) const { return atoi(value(option)); }
+ float float_value(const char *option) const { return atof(value(option)); }
// setting
- bool set_value(const char *name, const char *value, int priority, std::string &error_string);
- bool set_value(const char *name, int value, int priority, std::string &error_string);
- bool set_value(const char *name, float value, int priority, std::string &error_string);
- void set_flag(const char *name, uint32_t mask, uint32_t flags);
- void mark_changed(const char *name);
+ void set_value(const std::string &name, const std::string &value, int priority);
+ void set_value(const std::string &name, std::string &&value, int priority);
+ void set_value(const std::string &name, int value, int priority);
+ void set_value(const std::string &name, float value, int priority);
// misc
static const char *unadorned(int x = 0) { return s_option_unadorned[std::min(x, MAX_UNADORNED_OPTIONS - 1)]; }
- int options_count() const { return m_entrylist.count(); }
protected:
- // This is a hook to allow option value retrieval to be overridden for various reasons; this is a crude
- // extensibility mechanism that should really be replaced by something better
- enum class override_get_value_result
+ virtual void command_argument_processed() { }
+
+private:
+ class simple_entry : public entry
{
- NONE,
- OVERRIDE,
- SKIP
+ public:
+ // construction/destruction
+ simple_entry(std::vector<std::string> &&names, const char *description, core_options::option_type type, std::string &&defdata, std::string &&minimum, std::string &&maximum);
+ simple_entry(const simple_entry &) = delete;
+ simple_entry(simple_entry &&) = delete;
+ simple_entry& operator=(const simple_entry &) = delete;
+ simple_entry& operator=(simple_entry &&) = delete;
+ ~simple_entry();
+
+ // getters
+ virtual const char *value() const override;
+ virtual const char *minimum() const override;
+ virtual const char *maximum() const override;
+ virtual const std::string &default_value() const override;
+
+ virtual void set_default_value(std::string &&newvalue) override;
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) override;
+
+ private:
+ // internal state
+ std::string m_data; // data for this item
+ std::string m_defdata; // default data for this item
+ std::string m_minimum; // minimum value
+ std::string m_maximum; // maximum value
};
- virtual void value_changed(const std::string &name, const std::string &value) {}
- virtual override_get_value_result override_get_value(const char *name, std::string &value) const { return override_get_value_result::NONE; }
- virtual bool override_set_value(const char *name, const std::string &value) { return false; }
+ // used internally in core_options
+ enum class condition_type
+ {
+ NONE,
+ WARN,
+ ERR
+ };
-private:
// internal helpers
- void reset();
- void append_entry(entry &newentry);
- void copyfrom(const core_options &src);
- bool validate_and_set_data(entry &curentry, std::string &&newdata, int priority, std::string &error_string);
+ void add_to_entry_map(std::string &&name, entry::shared_ptr &entry);
+ void do_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition);
+ void throw_options_exception_if_appropriate(condition_type condition, std::ostringstream &error_stream);
// internal state
- simple_list<entry> m_entrylist; // head of list of entries
- std::unordered_map<std::string,entry *> m_entrymap; // map for fast lookup
- std::string m_command; // command found
- std::vector<std::string> m_command_arguments; // command arguments
- static const char *const s_option_unadorned[]; // array of unadorned option "names"
+ std::vector<entry::shared_ptr> m_entries; // cannonical list of entries
+ std::unordered_map<std::string, entry::weak_ptr> m_entrymap; // map for fast lookup
+ std::string m_command; // command found
+ std::vector<std::string> m_command_arguments; // command arguments
+ static const char *const s_option_unadorned[]; // array of unadorned option "names"
+};
+
+
+// static structure describing a single option with its description and default value
+struct options_entry
+{
+ const char * name; // name on the command line
+ const char * defvalue; // default value of this argument
+ core_options::option_type type; // type of option
+ const char * description; // description for -showusage
};
+// legacy option types
+const core_options::option_type OPTION_INVALID = core_options::option_type::INVALID;
+const core_options::option_type OPTION_HEADER = core_options::option_type::HEADER;
+const core_options::option_type OPTION_COMMAND = core_options::option_type::COMMAND;
+const core_options::option_type OPTION_BOOLEAN = core_options::option_type::BOOLEAN;
+const core_options::option_type OPTION_INTEGER = core_options::option_type::INTEGER;
+const core_options::option_type OPTION_FLOAT = core_options::option_type::FLOAT;
+const core_options::option_type OPTION_STRING = core_options::option_type::STRING;
#endif // MAME_LIB_UTIL_OPTIONS_H