summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/options.cpp
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2017-05-07 14:40:12 +1000
committer Vas Crabb <vas@vastheman.com>2017-05-07 14:40:12 +1000
commita400c011a96197281e205d265c267ecd915fb597 (patch)
tree68bb8b132d9c8dea4e587c22db6b32c6ece08b73 /src/lib/util/options.cpp
parent1970bcfab8103d41ce65bffa7933d19eda1f1b19 (diff)
Revert "Overhaul to how MAME handles options (#2260)"
This reverts commit 536990e77b49ccc50ef275bfbf1018cc29c16154. Conflicts: src/frontend/mame/mame.cpp Sorry, but this change was half-baked. It breaks a lot of existing functionality and clearly hasn't been tested in more than a tiny subset of use cases. Please play this work back onto your own branch, and test it before submitting another PR.
Diffstat (limited to 'src/lib/util/options.cpp')
-rw-r--r--src/lib/util/options.cpp968
1 files changed, 433 insertions, 535 deletions
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp
index 8889107eba1..8500eda327d 100644
--- a/src/lib/util/options.cpp
+++ b/src/lib/util/options.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- options.cpp
+ options.c
Core options code code
@@ -44,335 +44,155 @@ const char *const core_options::s_option_unadorned[MAX_UNADORNED_OPTIONS] =
};
-//**************************************************************************
-// 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 BASE CLASS
+// CORE OPTIONS ENTRY
//**************************************************************************
//-------------------------------------------------
// entry - constructor
//-------------------------------------------------
-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)
-{
- if (type == option_type::HEADER)
- assert(m_names.size() == 0);
- else
- assert(m_names.size() > 0);
-}
-
-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()
+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)
{
-}
-
-
-//-------------------------------------------------
-// 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())
- {
- 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;
- int minimum_integer, maximum_integer;
- float minimum_float, maximum_float;
-
- switch (type())
+ // copy in the name(s) as appropriate
+ if (name != nullptr)
{
- 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())
+ // 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)
{
- minimum_integer = atoi(minimum());
- 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());
+ 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);
}
- break;
-
- 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())
+ // then chop up any semicolon-separated names
+ int semi;
+ int nameindex = 0;
+ while ((semi = namestr.find_first_of(';')) != -1 && nameindex < ARRAY_LENGTH(m_name))
{
- minimum_float = atof(minimum());
- 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());
+ m_name[nameindex++].assign(namestr.substr(0, semi));
+ namestr.erase(0, semi + 1);
}
- break;
- 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());
+ // finally add the last item
+ if (nameindex < ARRAY_LENGTH(m_name))
+ m_name[nameindex++] = namestr;
}
-}
-
-//-------------------------------------------------
-// entry::minimum
-//-------------------------------------------------
-
-const char *core_options::entry::minimum() const
-{
- return nullptr;
+ // set the default value
+ if (defvalue != nullptr)
+ m_defdata = defvalue;
+ m_data = m_defdata;
}
//-------------------------------------------------
-// entry::maximum
+// set_value - update our data value
//-------------------------------------------------
-const char *core_options::entry::maximum() const
+void core_options::entry::set_value(const char *newdata, int priority)
{
- return nullptr;
-}
-
-
-//-------------------------------------------------
-// entry::has_range
-//-------------------------------------------------
+ // ignore if we don't have priority
+ if (priority < m_priority)
+ return;
-bool core_options::entry::has_range() const
-{
- return minimum() && maximum();
+ // set the data and priority, then bump the sequence
+ m_data = newdata;
+ m_priority = priority;
}
//-------------------------------------------------
-// entry::default_value
+// set_default_value - set the default value of
+// an option, and reset the current value to it
//-------------------------------------------------
-const std::string &core_options::entry::default_value() const
+void core_options::entry::set_default_value(const char *defvalue)
{
- // I don't really want this generally available, but MewUI seems to need it. Please
- // do not use
- throw false;
+ m_data = defvalue;
+ m_defdata = defvalue;
+ m_priority = OPTION_PRIORITY_DEFAULT;
}
-//**************************************************************************
-// CORE OPTIONS SIMPLE ENTRYCLASS
-//**************************************************************************
-
//-------------------------------------------------
-// simple_entry - constructor
+// set_description - set the description of
+// an option
//-------------------------------------------------
-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))
+void core_options::entry::set_description(const char *description)
{
- m_data = m_defdata;
+ m_description = description;
}
-//-------------------------------------------------
-// simple_entry - destructor
-//-------------------------------------------------
-
-core_options::simple_entry::~simple_entry()
+void core_options::entry::set_flag(uint32_t mask, uint32_t flag)
{
+ m_flags = ( m_flags & mask ) | flag;
}
//-------------------------------------------------
-// simple_entry::value
+// revert - revert back to our default if we are
+// within the given priority range
//-------------------------------------------------
-const char *core_options::simple_entry::value() const
+void core_options::entry::revert(int priority_hi, int priority_lo)
{
- const char *result;
- switch (type())
+ // if our priority is within the range, revert to the default
+ if (m_priority <= priority_hi && m_priority >= priority_lo)
{
- 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:
- result = nullptr;
- break;
+ m_data = m_defdata;
+ m_priority = OPTION_PRIORITY_DEFAULT;
}
- return result;
}
-//-------------------------------------------------
-// simple_entry::default_value
-//-------------------------------------------------
-
-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
+//**************************************************************************
//-------------------------------------------------
-// internal_set_value
+// core_options - constructor
//-------------------------------------------------
-void core_options::simple_entry::internal_set_value(std::string &&newvalue)
+core_options::core_options()
{
- m_data = std::move(newvalue);
}
-
-//-------------------------------------------------
-// set_default_value
-//-------------------------------------------------
-
-void core_options::simple_entry::set_default_value(std::string &&newvalue)
+core_options::core_options(const options_entry *entrylist)
{
- m_defdata = std::move(newvalue);
+ add_entries(entrylist);
}
-
-//-------------------------------------------------
-// minimum
-//-------------------------------------------------
-
-const char *core_options::simple_entry::minimum() const
+core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2)
{
- return m_minimum.c_str();
+ add_entries(entrylist1);
+ add_entries(entrylist2);
}
-
-//-------------------------------------------------
-// maximum
-//-------------------------------------------------
-
-const char *core_options::simple_entry::maximum() const
+core_options::core_options(const options_entry *entrylist1, const options_entry *entrylist2, const options_entry *entrylist3)
{
- return m_maximum.c_str();
+ add_entries(entrylist1);
+ add_entries(entrylist2);
+ add_entries(entrylist3);
}
-
-//**************************************************************************
-// CORE OPTIONS
-//**************************************************************************
-
-//-------------------------------------------------
-// core_options - constructor
-//-------------------------------------------------
-
-core_options::core_options()
+core_options::core_options(const core_options &src)
{
+ copyfrom(src);
}
@@ -386,140 +206,82 @@ core_options::~core_options()
//-------------------------------------------------
-// add_entry - adds an entry
+// operator= - assignment operator
//-------------------------------------------------
-void core_options::add_entry(entry::shared_ptr &&entry, const char *after_header)
+core_options &core_options::operator=(const core_options &rhs)
{
- // 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);
- }
-
- // and add the entry to the vector
- m_entries.emplace_back(std::move(entry));
+ // ignore self-assignment
+ if (this != &rhs)
+ copyfrom(rhs);
+ return *this;
}
//-------------------------------------------------
-// add_to_entry_map - adds an entry to the entry
-// map
+// operator== - compare two sets of options
//-------------------------------------------------
-void core_options::add_to_entry_map(std::string &&name, entry::shared_ptr &entry)
+bool core_options::operator==(const core_options &rhs)
{
- // it is illegal to call this method for something that already ex0ists
- assert(m_entrymap.find(name) == m_entrymap.end());
+ // 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;
+ }
- // append the entry
- m_entrymap.emplace(std::make_pair(name, entry::weak_ptr(entry)));
+ return true;
}
//-------------------------------------------------
-// add_entry - adds an entry based on an
-// options_entry
+// operator!= - compare two sets of options
//-------------------------------------------------
-void core_options::add_entry(const options_entry &opt, bool override_existing)
+bool core_options::operator!=(const core_options &rhs)
{
- std::vector<std::string> names;
- std::string minimum, maximum;
-
- // copy in the name(s) as appropriate
- if (opt.name)
- {
- // 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)
- {
- 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);
- }
+ return !operator==(rhs);
+}
- // 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));
- }
+//-------------------------------------------------
+// add_entry - add an entry to the current
+// options set
+//-------------------------------------------------
- // we might be called with an existing entry
- entry::shared_ptr existing_entry;
- do
+void core_options::add_entry(const char *name, const char *description, uint32_t flags, const char *defvalue, bool override_existing)
+{
+ // allocate a new entry
+ auto newentry = global_alloc(entry(name, description, flags, defvalue));
+ if (newentry->name() != nullptr)
{
- for (const std::string &name : names)
- {
- existing_entry = get_entry(name.c_str());
- if (existing_entry)
- break;
- }
-
- if (existing_entry)
+ // see if we match an existing entry
+ auto checkentry = m_entrymap.find(newentry->name());
+ if (checkentry != m_entrymap.end())
{
+ entry *existing = checkentry->second;
+ // if we're overriding existing entries, then remove the old one
if (override_existing)
- remove_entry(*existing_entry);
+ m_entrylist.remove(*existing);
+
+ // otherwise, just override the default and current values and throw out the new 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_header
-//-------------------------------------------------
-
-void core_options::add_header(const char *description)
-{
- add_entry(std::vector<std::string>(), description, option_type::HEADER);
+ // add us to the list and maps
+ append_entry(*newentry);
}
@@ -531,8 +293,8 @@ void core_options::add_header(const char *description)
void core_options::add_entries(const options_entry *entrylist, bool override_existing)
{
// loop over entries until we hit a nullptr name
- for (int i = 0; entrylist[i].name || entrylist[i].type == option_type::HEADER; i++)
- add_entry(entrylist[i], override_existing);
+ for ( ; entrylist->name != nullptr || (entrylist->flags & OPTION_HEADER) != 0; entrylist++)
+ add_entry(*entrylist, override_existing);
}
@@ -543,8 +305,13 @@ 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
- get_entry(name)->set_default_value(defvalue);
+ curentry->second->set_default_value(defvalue);
}
@@ -555,8 +322,13 @@ 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
- get_entry(name)->set_description(description);
+ curentry->second->set_description(description);
}
@@ -565,12 +337,10 @@ void core_options::set_description(const char *name, const char *description)
// command line arguments
//-------------------------------------------------
-void core_options::parse_command_line(std::vector<std::string> &args, int priority)
+bool core_options::parse_command_line(std::vector<std::string> &args, int priority, std::string &error_string)
{
- std::ostringstream error_stream;
- condition_type condition = condition_type::NONE;
-
// reset the errors and the command
+ error_string.clear();
m_command.clear();
// iterate through arguments
@@ -584,8 +354,8 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori
const char *optionname = is_unadorned ? core_options::unadorned(unadorned_index++) : &curarg[1];
// find our entry; if not found, continue
- auto curentry = get_entry(optionname);
- if (!curentry)
+ auto curentry = m_entrymap.find(optionname);
+ if (curentry == m_entrymap.end())
{
// we need to relocate this option
if (new_argc != arg)
@@ -606,19 +376,21 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori
}
// process commands first
- if (curentry->type() == option_type::COMMAND)
+ if (curentry->second->type() == OPTION_COMMAND)
{
// can only have one command
if (!m_command.empty())
- throw options_error_exception("Error: multiple commands specified -%s and %s\n", m_command, curarg);
-
- m_command = curentry->name();
+ {
+ error_string.append(string_format("Error: multiple commands specified -%s and %s\n", m_command, curarg));
+ return false;
+ }
+ m_command = curentry->second->name();
continue;
}
// get the data for this argument, special casing booleans
std::string newdata;
- if (curentry->type() == option_type::BOOLEAN)
+ if (curentry->second->type() == OPTION_BOOLEAN)
{
newdata = (strncmp(&curarg[1], "no", 2) == 0) ? "0" : "1";
}
@@ -633,18 +405,17 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori
}
else
{
- throw options_error_exception("Error: option %s expected a parameter\n", curarg);
+ error_string.append(string_format("Error: option %s expected a parameter\n", curarg));
+ return false;
}
args[arg].clear();
// set the new data
- prettify_and_set_value(*curentry, std::move(newdata), priority, error_stream, condition);
+ validate_and_set_data(*curentry->second, std::move(newdata), priority, error_string);
}
args.resize(new_argc);
-
- // did we have any errors that may need to be aggregated?
- throw_options_exception_if_appropriate(condition, error_stream);
+ return true;
}
@@ -653,11 +424,8 @@ void core_options::parse_command_line(std::vector<std::string> &args, int priori
// an INI file
//-------------------------------------------------
-void core_options::parse_ini_file(util::core_file &inifile, int priority, bool always_override)
+bool core_options::parse_ini_file(util::core_file &inifile, int priority, int ignore_priority, std::string &error_string)
{
- 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)
@@ -681,8 +449,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool a
// if we hit the end early, print a warning and continue
if (*temp == 0)
{
- condition = std::max(condition, condition_type::WARN);
- util::stream_format(error_stream, "Warning: invalid line in INI: %s", buffer);
+ error_string.append(string_format("Warning: invalid line in INI: %s", buffer));
continue;
}
@@ -702,68 +469,77 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool a
*temp = 0;
// find our entry
- entry::shared_ptr curentry = get_entry(optionname);
- if (!curentry)
+ auto curentry = m_entrymap.find(optionname);
+ if (curentry == m_entrymap.end())
{
- condition = std::max(condition, condition_type::WARN);
- util::stream_format(error_stream, "Warning: unknown option in INI: %s\n", optionname);
+ if (priority >= ignore_priority)
+ error_string.append(string_format("Warning: unknown option in INI: %s\n", optionname));
continue;
}
// set the new data
- prettify_and_set_value(*curentry, optiondata, priority, error_stream, condition);
+ validate_and_set_data(*curentry->second, optiondata, priority, error_string);
}
-
- // did we have any errors that may need to be aggregated?
- throw_options_exception_if_appropriate(condition, error_stream);
+ return true;
}
//-------------------------------------------------
-// throw_options_exception_if_appropriate
+// pluck_from_command_line - finds a specific
+// value from within a command line
//-------------------------------------------------
-void core_options::throw_options_exception_if_appropriate(core_options::condition_type condition, std::ostringstream &error_stream)
+bool core_options::pluck_from_command_line(std::vector<std::string> &args, const std::string &optionname, std::string &result)
{
- switch(condition)
- {
- case condition_type::NONE:
- // do nothing
- break;
+ // 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());
- case condition_type::WARN:
- throw options_warning_exception(error_stream.str());
+ // 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)
+ {
+ // not supporting unadorned options for now
+ targets.push_back(std::string("-") + potential_target);
+ }
- case condition_type::ERR:
- throw options_warning_exception(error_stream.str());
+ // 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]);
- default:
- // should not get here
- throw false;
+ // remove this arguments from the list
+ auto const pos = std::next(args.begin(), i);
+ args.erase(pos, std::next(pos, 2));
+ return true;
+ }
}
+
+ result.clear();
+ return false;
}
//-------------------------------------------------
-// copy_from
+// revert - revert options at or below a certain
+// priority back to their defaults
//-------------------------------------------------
-void core_options::copy_from(const core_options &that)
+void core_options::revert(int priority_hi, int 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);
- }
- }
- }
+ // iterate over options and revert to defaults if below the given priority
+ for (entry &curentry : m_entrylist)
+ curentry.revert(priority_hi, priority_lo);
}
@@ -784,34 +560,47 @@ std::string core_options::output_ini(const core_options *diff) const
std::string overridden_value;
// loop over all items
- for (auto &curentry : m_entries)
+ for (entry &curentry : m_entrylist)
{
- if (curentry->type() == option_type::HEADER)
+ const char *name = curentry.name();
+ const char *value;
+ switch (override_get_value(name, overridden_value))
{
- // header: record description
- last_header = curentry->description();
+ 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;
}
- else
+ bool is_unadorned = false;
+
+ // check if it's unadorned
+ if (name && strlen(name) && !strcmp(name, core_options::unadorned(unadorned_index)))
{
- const std::string &name(curentry->name());
- const char *value(curentry->value());
+ unadorned_index++;
+ is_unadorned = true;
+ }
- // check if it's unadorned
- bool is_unadorned = false;
- if (name == core_options::unadorned(unadorned_index))
- {
- unadorned_index++;
- is_unadorned = true;
- }
+ // header: record description
+ if (curentry.is_header())
+ last_header = curentry.description();
- // output entries for all non-command items (items with value)
- if (value)
+ // otherwise, output entries for all non-command items
+ else if (!curentry.is_command())
+ {
+ if (!curentry.is_internal())
{
// look up counterpart in diff, if diff is specified
- if (!diff || strcmp(value, diff->value(name.c_str())))
+ if (diff == nullptr || strcmp(value, diff->value(name)) != 0)
{
// output header, if we have one
- if (last_header)
+ if (last_header != nullptr)
{
if (num_valid_headers++)
buffer << '\n';
@@ -822,7 +611,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, ' '))
+ if (strchr(value, ' ') != nullptr)
util::stream_format(buffer, "%-25s \"%s\"\n", name, value);
else
util::stream_format(buffer, "%-25s %s\n", name, value);
@@ -845,15 +634,15 @@ std::string core_options::output_help() const
std::ostringstream buffer;
// loop over all items
- for (auto &curentry : m_entries)
+ for (entry &curentry : m_entrylist)
{
// header: just print
- if (curentry->type() == option_type::HEADER)
- util::stream_format(buffer, "\n#\n# %s\n#\n", curentry->description());
+ if (curentry.is_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();
}
@@ -863,9 +652,10 @@ std::string core_options::output_help() const
// value - return the raw option value
//-------------------------------------------------
-const char *core_options::value(const char *option) const
+const char *core_options::value(const char *name) const
{
- return get_entry(option)->value();
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second->value() : "";
}
@@ -873,143 +663,251 @@ const char *core_options::value(const char *option) const
// description - return description of option
//-------------------------------------------------
-const char *core_options::description(const char *option) const
+const char *core_options::description(const char *name) const
{
- return get_entry(option)->description();
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second->description() : "";
}
-//**************************************************************************
-// LEGACY
-//**************************************************************************
-
//-------------------------------------------------
-// set_value - set the raw option value
+// priority - return the priority of option
//-------------------------------------------------
-void core_options::set_value(const std::string &name, const std::string &value, int priority)
+int core_options::priority(const char *name) const
{
- set_value(name, std::string(value), priority);
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second->priority() : 0;
}
-void core_options::set_value(const std::string &name, std::string &&value, int priority)
-{
- get_entry(name)->set_value(std::move(value), priority);
-}
-void core_options::set_value(const std::string &name, int value, int priority)
-{
- set_value(name, string_format("%d", value), priority);
-}
+//-------------------------------------------------
+// exists - return if option exists in list
+//-------------------------------------------------
-void core_options::set_value(const std::string &name, float value, int priority)
+bool core_options::exists(const char *name) const
{
- set_value(name, string_format("%f", value), priority);
+ return (m_entrymap.find(name) != m_entrymap.end());
}
//-------------------------------------------------
-// remove_entry - remove an entry from our list
-// and map
+// set_value - set the raw option value
//-------------------------------------------------
-void core_options::remove_entry(core_options::entry &delentry)
+bool core_options::set_value(const char *name, const char *value, int priority, std::string &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());
+ // 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;
+ }
- // erase each of the items out of the entry map
- for (const std::string &name : delentry.names())
- m_entrymap.erase(name);
+ // validate and set the item normally
+ return validate_and_set_data(*curentry->second, value, priority, error_string);
+}
- // finally erase it
- m_entries.erase(iter);
+bool core_options::set_value(const char *name, int value, int priority, std::string &error_string)
+{
+ return set_value(name, string_format("%d", value).c_str(), priority, error_string);
}
+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);
+}
-//-------------------------------------------------
-// prettify_and_set_value
-//-------------------------------------------------
-void core_options::prettify_and_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition)
+void core_options::set_flag(const char *name, uint32_t mask, uint32_t flag)
{
- // trim any whitespace
- strtrimspace(data);
-
- // trim quotes
- if (data.find_first_of('"') == 0 && data.find_last_of('"') == data.length() - 1)
+ // find the entry first
+ auto curentry = m_entrymap.find(name);
+ if ( curentry == m_entrymap.end())
{
- data.erase(0, 1);
- data.erase(data.length() - 1, 1);
+ return;
}
+ curentry->second->set_flag(mask, flag);
+}
- // 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::move(data), priority);
- }
- catch (options_warning_exception &ex)
- {
- // we want to aggregate option exceptions
- error_stream << ex.message();
- condition = std::max(condition, condition_type::WARN);
- }
- catch (options_error_exception &ex)
+void core_options::mark_changed(const char* name)
+{
+ // find the entry first
+ auto curentry = m_entrymap.find(name);
+ if (curentry == m_entrymap.end())
{
- // we want to aggregate option exceptions
- error_stream << ex.message();
- condition = std::max(condition, condition_type::ERR);
+ return;
}
+ curentry->second->mark_changed();
}
-
//-------------------------------------------------
-// get_entry
+// reset - reset the options state, removing
+// everything
//-------------------------------------------------
-const core_options::entry::shared_ptr core_options::get_entry(const std::string &name) const
+void core_options::reset()
{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
+ m_entrylist.reset();
+ m_entrymap.clear();
}
-core_options::entry::shared_ptr core_options::get_entry(const std::string &name)
+
+//-------------------------------------------------
+// append_entry - append an entry to our list
+// and index it in the map
+//-------------------------------------------------
+
+void core_options::append_entry(core_options::entry &newentry)
{
- auto curentry = m_entrymap.find(name);
- return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
+ 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));
+ }
}
//-------------------------------------------------
-// set_value_changed_handler
+// remove_entry - remove an entry from our list
+// and map
//-------------------------------------------------
-void core_options::set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler)
+void core_options::remove_entry(core_options::entry &delentry)
{
- get_entry(name)->set_value_changed_handler(std::move(handler));
+ // 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.
+ */
-//-------------------------------------------------
-// header_exists
-//-------------------------------------------------
+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())));
+}
-bool core_options::header_exists(const char *description) const
+/**
+ * @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)
{
- auto iter = std::find_if(
- m_entries.begin(),
- m_entries.end(),
- [description](const auto &entry)
+ // trim any whitespace
+ strtrimspace(data);
+
+ // trim quotes
+ if (data.find_first_of('"') == 0 && data.find_last_of('"') == data.length() - 1)
+ {
+ data.erase(0, 1);
+ data.erase(data.length() - 1, 1);
+ }
+
+ // 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())))
{
- return entry->type() == option_type::HEADER
- && entry->description()
- && !strcmp(entry->description(), description);
- });
+ 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 iter != m_entries.end();
+ // 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;
}