summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/options.c')
-rw-r--r--src/lib/util/options.c1133
1 files changed, 1133 insertions, 0 deletions
diff --git a/src/lib/util/options.c b/src/lib/util/options.c
new file mode 100644
index 00000000000..44534e2247d
--- /dev/null
+++ b/src/lib/util/options.c
@@ -0,0 +1,1133 @@
+/***************************************************************************
+
+ options.c
+
+ Core options code code
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include <stdarg.h>
+#include <ctype.h>
+#include "options.h"
+#include "astring.h"
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define MAX_ENTRY_NAMES 4
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* forward references */
+typedef struct _options_data options_data;
+
+
+/* range parameter for minimum/maximum values */
+typedef union _options_range_parameter options_range_parameter;
+union _options_range_parameter
+{
+ float f;
+ int i;
+};
+
+
+/* hash table entries */
+typedef struct _options_hash_entry options_hash_entry;
+struct _options_hash_entry
+{
+ options_hash_entry * next; /* link to the next entry */
+ astring * name; /* name under the current link */
+ options_data * data; /* link to associated options_data */
+};
+
+
+/* information about a single entry in the options */
+struct _options_data
+{
+ options_hash_entry links[MAX_ENTRY_NAMES]; /* array of hash table entries (one per name) */
+ options_data * next; /* link to the next data */
+ UINT32 flags; /* flags from the entry */
+ UINT32 seqid; /* sequence ID; bumped on each change */
+ int error_reported; /* have we reported an error on this option yet? */
+ int priority; /* priority of the data set */
+ astring * data; /* data for this item */
+ astring * defdata; /* default data for this item */
+ const char * description; /* description for this item */
+ options_range_type range_type; /* the type of range to apply to this item */
+ options_range_parameter range_minimum; /* the minimum of the range */
+ options_range_parameter range_maximum; /* the maximum of the range */
+ void (*callback)(core_options *opts, const char *arg); /* callback to be invoked when parsing */
+};
+
+
+/* structure holding information about a collection of options */
+struct _core_options
+{
+ void (*output[OPTMSG_COUNT])(const char *s);/* output callbacks */
+
+ /* linked list, for sequential iteration */
+ options_data * datalist; /* linked list for sequential iteration */
+ options_data ** datalist_nextptr; /* pointer to pointer to tail entry */
+
+ /* hashtable, for fast lookup */
+ options_hash_entry * hashtable[101]; /* hash table for fast lookup */
+};
+
+
+/* information about an in-progress options enumeration */
+struct _options_enumerator
+{
+ options_data * current;
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static options_data *find_entry_data(core_options *opts, const char *string, int is_command_line);
+static void update_data(core_options *opts, options_data *data, const char *newdata, int priority);
+static int parse_option_name(core_options *opts, const char *srcstring, options_data *data);
+
+static void message(core_options *opts, options_message msgtype, const char *format, ...);
+static UINT32 hash_value(core_options *opts, const char *str);
+static void output_printf(void (*output)(const char *s), const char *format, ...);
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+const char *option_unadorned[MAX_UNADORNED_OPTIONS] =
+{
+ "<UNADORNED0>",
+ "<UNADORNED1>",
+ "<UNADORNED2>",
+ "<UNADORNED3>",
+ "<UNADORNED4>",
+ "<UNADORNED5>",
+ "<UNADORNED6>",
+ "<UNADORNED7>",
+ "<UNADORNED8>",
+ "<UNADORNED9>",
+ "<UNADORNED10>",
+ "<UNADORNED11>",
+ "<UNADORNED12>",
+ "<UNADORNED13>",
+ "<UNADORNED14>",
+ "<UNADORNED15>"
+};
+
+
+
+/***************************************************************************
+ OPTIONS COLLECTION MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_create - creates a new instance of
+ core options
+-------------------------------------------------*/
+
+core_options *options_create(void (*fail)(const char *message))
+{
+ /* allocate memory for the option block */
+ core_options *opts = (core_options *)malloc(sizeof(*opts));
+ if (opts == NULL)
+ goto error;
+
+ /* and set up the structure */
+ memset(opts, 0, sizeof(*opts));
+ opts->datalist_nextptr = &opts->datalist;
+ return opts;
+
+error:
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ options_free - frees an options object
+-------------------------------------------------*/
+
+void options_free(core_options *opts)
+{
+ options_data *data, *next;
+
+ /* loop over data items and free them */
+ for (data = opts->datalist; data != NULL; data = next)
+ {
+ int linknum;
+
+ next = data->next;
+
+ /* free names */
+ for (linknum = 0; linknum < ARRAY_LENGTH(data->links); linknum++)
+ if (data->links[linknum].name != NULL)
+ astring_free(data->links[linknum].name);
+
+ /* free strings */
+ astring_free(data->data);
+ astring_free(data->defdata);
+
+ /* free the data itself */
+ free(data);
+ }
+
+ /* free the options itself */
+ free(opts);
+}
+
+
+/*-------------------------------------------------
+ options_set_output_callback - installs a callback
+ to be invoked when data is outputted in the
+ process of outputting options
+-------------------------------------------------*/
+
+void options_set_output_callback(core_options *opts, options_message msgtype, void (*callback)(const char *s))
+{
+ opts->output[msgtype] = callback;
+}
+
+
+/*-------------------------------------------------
+ options_revert - revert options at or below
+ a certain priority back to their defaults
+-------------------------------------------------*/
+
+void options_revert(core_options *opts, int priority)
+{
+ options_data *data;
+
+ /* iterate over options and revert to defaults if below the given priority */
+ for (data = opts->datalist; data != NULL; data = data->next)
+ if (data->priority <= priority)
+ {
+ astring_cpy(data->data, data->defdata);
+ data->priority = OPTION_PRIORITY_DEFAULT;
+ }
+}
+
+
+/*-------------------------------------------------
+ options_copy - copy options from one core_options
+ to another
+-------------------------------------------------*/
+
+int options_copy(core_options *dest_opts, core_options *src_opts)
+{
+ options_data *data;
+
+ /* iterate over options in the destination */
+ for (data = dest_opts->datalist; data != NULL; data = data->next)
+ if (!(data->flags & OPTION_HEADER))
+ {
+ options_data *srcdata = find_entry_data(src_opts, astring_c(data->links[0].name), FALSE);
+
+ /* if the option exists in the source, set it in the destination */
+ if (srcdata != NULL)
+ options_set_string(dest_opts, astring_c(srcdata->links[0].name), astring_c(srcdata->data), srcdata->priority);
+ }
+
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ options_equal - compare two sets of options
+-------------------------------------------------*/
+
+int options_equal(core_options *opts1, core_options *opts2)
+{
+ options_data *data;
+
+ /* iterate over options in the first list */
+ for (data = opts1->datalist; data != NULL; data = data->next)
+ if (!(data->flags & OPTION_HEADER))
+ {
+ const char *value1 = options_get_string(opts1, astring_c(data->links[0].name));
+ const char *value2 = options_get_string(opts2, astring_c(data->links[0].name));
+
+ /* if the values differ, return false */
+ if (strcmp(value1, value2) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+/***************************************************************************
+ OPTION DEFINITIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_add_entries - add entries to the
+ current options sets
+-------------------------------------------------*/
+
+int options_add_entries(core_options *opts, const options_entry *entrylist)
+{
+ /* loop over entries until we hit a NULL name */
+ for ( ; entrylist->name != NULL || (entrylist->flags & OPTION_HEADER); entrylist++)
+ {
+ options_data *match = NULL;
+ int i;
+
+ /* allocate a new item */
+ options_data *data = malloc(sizeof(*data));
+ if (data == NULL)
+ return FALSE;
+ memset(data, 0, sizeof(*data));
+
+ /* parse the option name */
+ if (entrylist->name != NULL)
+ parse_option_name(opts, entrylist->name, data);
+
+ /* do we match an existing entry? */
+ for (i = 0; i < ARRAY_LENGTH(data->links) && match == NULL; i++)
+ if (data->links[i].name != NULL)
+ match = find_entry_data(opts, astring_c(data->links[i].name), FALSE);
+
+ /* if so, throw away this entry and replace the data */
+ if (match != NULL)
+ {
+ /* free what we've allocated so far */
+ for (i = 0; i < ARRAY_LENGTH(data->links); i++)
+ if (data->links[i].name != NULL)
+ astring_free(data->links[i].name);
+ free(data);
+
+ /* use the matching entry as our data */
+ data = match;
+ }
+
+ /* otherwise, finish making the new entry */
+ else
+ {
+ /* allocate strings */
+ data->data = astring_alloc();
+ data->defdata = astring_alloc();
+
+ /* copy the flags, and set the value equal to the default */
+ data->flags = entrylist->flags;
+ data->description = entrylist->description;
+
+ /* add us to the end of the sequential list */
+ *opts->datalist_nextptr = data;
+ opts->datalist_nextptr = &data->next;
+
+ /* add each name to the appropriate hash table with a link back to us */
+ for (i = 0; i < ARRAY_LENGTH(data->links); i++)
+ if (data->links[i].name != NULL)
+ {
+ int hash_entry = hash_value(opts, astring_c(data->links[i].name));
+
+ /* set up link */
+ data->links[i].data = data;
+ data->links[i].next = opts->hashtable[hash_entry];
+ opts->hashtable[hash_entry] = &data->links[i];
+ }
+ }
+
+ /* copy in the data and default data values */
+ if (entrylist->defvalue != NULL)
+ {
+ astring_cpyc(data->data, entrylist->defvalue);
+ astring_cpyc(data->defdata, entrylist->defvalue);
+ }
+ data->priority = OPTION_PRIORITY_DEFAULT;
+ }
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ options_set_option_default_value - change the
+ default value of an option
+-------------------------------------------------*/
+
+int options_set_option_default_value(core_options *opts, const char *name, const char *defvalue)
+{
+ options_data *data = find_entry_data(opts, name, TRUE);
+
+ /* if we don't have an entry for this, fail */
+ if (data == NULL)
+ return FALSE;
+
+ /* update the data and default data; note that we assume that data == defdata */
+ astring_cpyc(data->data, defvalue);
+ astring_cpyc(data->defdata, defvalue);
+ data->priority = OPTION_PRIORITY_DEFAULT;
+ return TRUE;
+}
+
+
+/*-------------------------------------------------
+ options_set_option_callback - specifies a
+ callback to be invoked when parsing options
+-------------------------------------------------*/
+
+int options_set_option_callback(core_options *opts, const char *name, void (*callback)(core_options *opts, const char *arg))
+{
+ options_data *data = find_entry_data(opts, name, TRUE);
+
+ /* if we don't find an entry, fail */
+ if (data == NULL)
+ return FALSE;
+
+ /* set the callback */
+ data->callback = callback;
+ return TRUE;
+}
+
+
+
+/***************************************************************************
+ OPTION DATA EXTRACTION
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_parse_command_line - parse a series
+ of command line arguments
+-------------------------------------------------*/
+
+int options_parse_command_line(core_options *opts, int argc, char **argv, int priority)
+{
+ int unadorned_index = 0;
+ int arg;
+
+ /* loop over commands, looking for options */
+ for (arg = 1; arg < argc; arg++)
+ {
+ const char *optionname, *newdata;
+ options_data *data;
+ int is_unadorned;
+
+ /* determine the entry name to search for */
+ is_unadorned = (argv[arg][0] != '-');
+ if (!is_unadorned)
+ optionname = &argv[arg][1];
+ else
+ optionname = OPTION_UNADORNED(unadorned_index);
+
+ /* find our entry */
+ data = find_entry_data(opts, optionname, TRUE);
+ if (data == NULL)
+ {
+ message(opts, OPTMSG_ERROR, "Error: unknown option: %s\n", argv[arg]);
+ return 1;
+ }
+
+ /* if unadorned, we have to bump the count (unless if the option repeats) */
+ if (is_unadorned && !(data->flags & OPTION_REPEATS))
+ unadorned_index++;
+
+ /* get the data for this argument, special casing booleans */
+ if ((data->flags & (OPTION_BOOLEAN | OPTION_COMMAND)) != 0)
+ newdata = (strncmp(&argv[arg][1], "no", 2) == 0) ? "0" : "1";
+ else if (argv[arg][0] != '-')
+ newdata = argv[arg];
+ else if (arg + 1 < argc)
+ newdata = argv[++arg];
+ else
+ {
+ message(opts, OPTMSG_ERROR, "Error: option %s expected a parameter\n", argv[arg]);
+ return 1;
+ }
+
+ /* if the option is deprecated or internal, don't process further */
+ if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) != 0)
+ continue;
+
+ /* invoke callback, if present */
+ if (data->callback != NULL)
+ (*data->callback)(opts, newdata);
+
+ /* allocate a new copy of data for this */
+ update_data(opts, data, newdata, priority);
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ options_parse_ini_file - parse a series
+ of entries in an INI file
+-------------------------------------------------*/
+
+int options_parse_ini_file(core_options *opts, core_file *inifile, int priority)
+{
+ char buffer[4096];
+
+ /* loop over data */
+ while (core_fgets(buffer, ARRAY_LENGTH(buffer), inifile) != NULL)
+ {
+ char *optionname, *optiondata, *temp;
+ options_data *data;
+ int inquotes = FALSE;
+
+ /* find the name */
+ for (optionname = buffer; *optionname != 0; optionname++)
+ if (!isspace(*optionname))
+ break;
+
+ /* skip comments */
+ if (*optionname == 0 || *optionname == '#')
+ continue;
+
+ /* scan forward to find the first space */
+ for (temp = optionname; *temp != 0; temp++)
+ if (isspace(*temp))
+ break;
+
+ /* if we hit the end early, print a warning and continue */
+ if (*temp == 0)
+ {
+ message(opts, OPTMSG_WARNING, "Warning: invalid line in INI: %s", buffer);
+ continue;
+ }
+
+ /* NULL-terminate */
+ *temp++ = 0;
+ optiondata = temp;
+
+ /* scan the data, stopping when we hit a comment */
+ for (temp = optiondata; *temp != 0; temp++)
+ {
+ if (*temp == '"')
+ inquotes = !inquotes;
+ if (*temp == '#' && !inquotes)
+ break;
+ }
+ *temp = 0;
+
+ /* find our entry */
+ data = find_entry_data(opts, optionname, FALSE);
+ if (data == NULL)
+ {
+ message(opts, OPTMSG_WARNING, "Warning: unknown option in INI: %s\n", optionname);
+ continue;
+ }
+ if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) != 0)
+ continue;
+
+ /* allocate a new copy of data for this */
+ update_data(opts, data, optiondata, priority);
+ }
+ return 0;
+}
+
+
+
+/***************************************************************************
+ OPTIONS OUTPUT
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_output_diff_ini_file - output the diff
+ of the current state from a base state to an
+ INI file
+-------------------------------------------------*/
+
+void options_output_diff_ini_file(core_options *opts, core_options *baseopts, core_file *inifile)
+{
+ options_data *data;
+ const char *last_header = NULL;
+ const char *name;
+ const char *value;
+ options_data *basedata;
+
+ /* loop over all items */
+ for (data = opts->datalist; data != NULL; data = data->next)
+ {
+ /* header: record description */
+ if ((data->flags & OPTION_HEADER) != 0)
+ last_header = data->description;
+
+ /* otherwise, output entries for all non-deprecated and non-command items (if not in baseopts) */
+ else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL | OPTION_COMMAND)) == 0)
+ {
+ /* get name and data of this value */
+ name = astring_c(data->links[0].name);
+ value = astring_c(data->data);
+
+ /* look up counterpart in baseopts, if baseopts is specified */
+ basedata = (baseopts != NULL) ? find_entry_data(baseopts, name, FALSE) : NULL;
+
+ /* is our data different, or not in baseopts? */
+ if ((basedata == NULL) || (strcmp(value, astring_c(basedata->data)) != 0))
+ {
+ /* output header, if we have one */
+ if (last_header != NULL)
+ {
+ core_fprintf(inifile, "\n#\n# %s\n#\n", last_header);
+ last_header = NULL;
+ }
+
+ /* and finally output the data */
+ if (strchr(value, ' ') != NULL)
+ core_fprintf(inifile, "%-25s \"%s\"\n", name, value);
+ else
+ core_fprintf(inifile, "%-25s %s\n", name, value);
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ options_output_ini_file - output the current
+ state to an INI file
+-------------------------------------------------*/
+
+void options_output_ini_file(core_options *opts, core_file *inifile)
+{
+ options_output_diff_ini_file(opts, NULL, inifile);
+}
+
+
+/*-------------------------------------------------
+ options_output_ini_file - output the current
+ state to an INI file
+-------------------------------------------------*/
+
+void options_output_ini_stdfile(core_options *opts, FILE *inifile)
+{
+ options_data *data;
+
+ /* loop over all items */
+ for (data = opts->datalist; data != NULL; data = data->next)
+ {
+ /* header: just print */
+ if ((data->flags & OPTION_HEADER) != 0)
+ fprintf(inifile, "\n#\n# %s\n#\n", data->description);
+
+ /* otherwise, output entries for all non-deprecated and non-command items */
+ else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL | OPTION_COMMAND)) == 0)
+ {
+ if (astring_chr(data->data, 0, ' ') != -1)
+ fprintf(inifile, "%-25s \"%s\"\n", astring_c(data->links[0].name), astring_c(data->data));
+ else
+ fprintf(inifile, "%-25s %s\n", astring_c(data->links[0].name), astring_c(data->data));
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ options_output_help - output option help to
+ a file
+-------------------------------------------------*/
+
+void options_output_help(core_options *opts, void (*output)(const char *))
+{
+ options_data *data;
+
+ /* loop over all items */
+ for (data = opts->datalist; data != NULL; data = data->next)
+ {
+ /* header: just print */
+ if ((data->flags & OPTION_HEADER) != 0)
+ output_printf(output, "\n#\n# %s\n#\n", data->description);
+
+ /* otherwise, output entries for all non-deprecated items */
+ else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) == 0 && data->description != NULL)
+ output_printf(output, "-%-20s%s\n", astring_c(data->links[0].name), data->description);
+ }
+}
+
+
+
+/***************************************************************************
+ OPTIONS READING
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_get_string - return data formatted
+ as a string
+-------------------------------------------------*/
+
+const char *options_get_string(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ const char *value = "";
+
+ /* error if not found */
+ if (data == NULL)
+ message(opts, OPTMSG_ERROR, "Unexpected option %s queried\n", name);
+
+ /* copy if non-NULL */
+ else
+ value = astring_c(data->data);
+
+ return value;
+}
+
+
+/*-------------------------------------------------
+ options_get_bool - return data formatted as
+ a boolean
+-------------------------------------------------*/
+
+int options_get_bool(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ int value = FALSE;
+
+ /* error if not found */
+ if (data == NULL)
+ message(opts, OPTMSG_ERROR, "Unexpected boolean option %s queried\n", name);
+
+ /* also error if we don't have a valid boolean value */
+ else if (sscanf(astring_c(data->data), "%d", &value) != 1 || value < 0 || value > 1)
+ {
+ options_set_string(opts, name, astring_c(data->defdata), 0);
+ sscanf(astring_c(data->data), "%d", &value);
+ if (!data->error_reported)
+ {
+ message(opts, OPTMSG_ERROR, "Illegal boolean value for %s; reverting to %d\n", astring_c(data->links[0].name), value);
+ data->error_reported = TRUE;
+ }
+ }
+ return value;
+}
+
+
+/*-------------------------------------------------
+ options_get_int - return data formatted as
+ an integer
+-------------------------------------------------*/
+
+int options_get_int(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ int value = 0;
+
+ /* error if not found */
+ if (data == NULL)
+ message(opts, OPTMSG_ERROR, "Unexpected integer option %s queried\n", name);
+
+ /* also error if we don't have a valid integer value */
+ else if (sscanf(astring_c(data->data), "%d", &value) != 1)
+ {
+ options_set_string(opts, name, astring_c(data->defdata), 0);
+ sscanf(astring_c(data->data), "%d", &value);
+ if (!data->error_reported)
+ {
+ message(opts, OPTMSG_ERROR, "Illegal integer value for %s; reverting to %d\n", astring_c(data->links[0].name), value);
+ data->error_reported = TRUE;
+ }
+ }
+ return value;
+}
+
+
+/*-------------------------------------------------
+ options_get_float - return data formatted as
+ a float
+-------------------------------------------------*/
+
+float options_get_float(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ float value = 0;
+
+ /* error if not found */
+ if (data == NULL)
+ message(opts, OPTMSG_ERROR, "Unexpected float option %s queried\n", name);
+
+ /* also error if we don't have a valid floating point value */
+ else if (sscanf(astring_c(data->data), "%f", &value) != 1)
+ {
+ options_set_string(opts, name, astring_c(data->defdata), 0);
+ sscanf(astring_c(data->data), "%f", &value);
+ if (!data->error_reported)
+ {
+ message(opts, OPTMSG_ERROR, "Illegal float value for %s; reverting to %f\n", astring_c(data->links[0].name), (double)value);
+ data->error_reported = TRUE;
+ }
+ }
+ return value;
+}
+
+
+/*-------------------------------------------------
+ options_get_seqid - return the seqid for an
+ entry
+-------------------------------------------------*/
+
+UINT32 options_get_seqid(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ return (data == NULL) ? 0 : data->seqid;
+}
+
+
+
+/***************************************************************************
+ OPTIONS SETTING
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_set_string - set a string value
+-------------------------------------------------*/
+
+void options_set_string(core_options *opts, const char *name, const char *value, int priority)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ update_data(opts, data, value, priority);
+}
+
+
+/*-------------------------------------------------
+ options_set_bool - set a boolean value
+-------------------------------------------------*/
+
+void options_set_bool(core_options *opts, const char *name, int value, int priority)
+{
+ char temp[4];
+ sprintf(temp, "%d", value ? 1 : 0);
+ options_set_string(opts, name, temp, priority);
+}
+
+
+/*-------------------------------------------------
+ options_set_int - set an integer value
+-------------------------------------------------*/
+
+void options_set_int(core_options *opts, const char *name, int value, int priority)
+{
+ char temp[20];
+ sprintf(temp, "%d", value);
+ options_set_string(opts, name, temp, priority);
+}
+
+
+/*-------------------------------------------------
+ options_set_float - set a float value
+-------------------------------------------------*/
+
+void options_set_float(core_options *opts, const char *name, float value, int priority)
+{
+ char temp[100];
+ sprintf(temp, "%f", value);
+ options_set_string(opts, name, temp, priority);
+}
+
+
+
+/***************************************************************************
+ OPTION DEFINITION QUERIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ options_enumerator_begin - retrieve the range of
+ a float option
+-------------------------------------------------*/
+
+options_enumerator *options_enumerator_begin(core_options *opts)
+{
+ options_enumerator *enumerator;
+
+ /* allocate memory for the enumerator */
+ enumerator = malloc(sizeof(*enumerator));
+ if (enumerator == NULL)
+ return NULL;
+
+ /* start at the head of the list */
+ enumerator->current = opts->datalist;
+ return enumerator;
+}
+
+
+
+/*-------------------------------------------------
+ options_enumerator_next - returns the current
+ option and advances the enumerator
+-------------------------------------------------*/
+
+const char *options_enumerator_next(options_enumerator *enumerator)
+{
+ astring *option_name = NULL;
+
+ /* be sure to skip over false options */
+ while (option_name == NULL && enumerator->current != NULL)
+ {
+ /* retrieve the current option name and advance the enumerator */
+ option_name = enumerator->current->links[0].name;
+ enumerator->current = enumerator->current->next;
+ }
+ return (option_name != NULL) ? astring_c(option_name) : NULL;
+}
+
+
+
+/*-------------------------------------------------
+ options_enumerator_free - disposes an options
+ enumerator
+-------------------------------------------------*/
+
+void options_enumerator_free(options_enumerator *enumerator)
+{
+ free(enumerator);
+}
+
+
+/*-------------------------------------------------
+ options_get_range_type - determine the type
+ of range for a particular option
+-------------------------------------------------*/
+
+options_range_type options_get_range_type(core_options *opts, const char *name)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ return data->range_type;
+}
+
+
+/*-------------------------------------------------
+ options_get_range_int - retrieve the range of
+ an integer option
+-------------------------------------------------*/
+
+void options_get_range_int(core_options *opts, const char *name, int *minval, int *maxval)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ *minval = data->range_minimum.i;
+ *maxval = data->range_maximum.i;
+}
+
+
+
+/*-------------------------------------------------
+ options_get_range_float - retrieve the range of
+ a float option
+-------------------------------------------------*/
+
+void options_get_range_float(core_options *opts, const char *name, float *minval, float *maxval)
+{
+ options_data *data = find_entry_data(opts, name, FALSE);
+ *minval = data->range_minimum.f;
+ *maxval = data->range_maximum.f;
+}
+
+
+
+/***************************************************************************
+ INTERNAL UTILITIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ find_entry_data - locate an entry whose name
+ matches the given string
+-------------------------------------------------*/
+
+static options_data *find_entry_data(core_options *opts, const char *string, int is_command_line)
+{
+ int hash_entry = hash_value(opts, string);
+ options_hash_entry *link;
+
+ /* scan all entries */
+ for (link = opts->hashtable[hash_entry]; link != NULL; link = link->next)
+ if (!(link->data->flags & OPTION_HEADER) && link->name != NULL && astring_cmpc(link->name, string) == 0)
+ return link->data;
+
+ /* haven't found it? if we are prefixed with "no", then try to search for that */
+ if (is_command_line && string[0] == 'n' && string[1] == 'o')
+ {
+ options_data *data = find_entry_data(opts, &string[2], FALSE);
+ if (data != NULL && (data->flags & OPTION_BOOLEAN))
+ return data;
+ }
+
+ /* didn't find it at all */
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ update_data - update the data value for a
+ given entry
+-------------------------------------------------*/
+
+static void update_data(core_options *opts, options_data *data, const char *newdata, int priority)
+{
+ const char *dataend = newdata + strlen(newdata) - 1;
+ const char *datastart = newdata;
+ float f;
+ int i;
+
+ /* strip off leading/trailing spaces */
+ while (isspace(*datastart) && datastart <= dataend)
+ datastart++;
+ while (isspace(*dataend) && datastart <= dataend)
+ dataend--;
+
+ /* strip off quotes */
+ if (datastart != dataend && *datastart == '"' && *dataend == '"')
+ datastart++, dataend--;
+
+ /* check against range */
+ switch (data->range_type)
+ {
+ case OPTION_RANGE_NONE:
+ /* do nothing */
+ break;
+
+ case OPTION_RANGE_INT:
+ /* check against integer range */
+ i = 0;
+ if (sscanf(datastart, "%d", &i) != 1)
+ {
+ message(opts, OPTMSG_ERROR, "Illegal integer value for %s; keeping value of %s\n", astring_c(data->links[0].name), astring_c(data->data));
+ data->error_reported = TRUE;
+ return;
+ }
+ if (i < data->range_minimum.i || i > data->range_maximum.i)
+ {
+ message(opts, OPTMSG_ERROR, "Invalid %s value (must be between %i and %i); keeping value of %s\n",
+ astring_c(data->links[0].name), data->range_minimum.i, data->range_maximum.i, astring_c(data->data));
+ data->error_reported = TRUE;
+ return;
+ }
+ break;
+
+ case OPTION_RANGE_FLOAT:
+ /* check against float range */
+ f = 0;
+ if (sscanf(datastart, "%f", &f) != 1)
+ {
+ message(opts, OPTMSG_ERROR, "Illegal float value for %s; keeping value of %s\n", astring_c(data->links[0].name), astring_c(data->data));
+ data->error_reported = TRUE;
+ return;
+ }
+ if (f < data->range_minimum.f || f > data->range_maximum.f)
+ {
+ message(opts, OPTMSG_ERROR, "Invalid %s value (must be between %f and %f); keeping value of %s\n",
+ astring_c(data->links[0].name), data->range_minimum.f, data->range_maximum.f, astring_c(data->data));
+ data->error_reported = TRUE;
+ return;
+ }
+ break;
+ }
+
+ /* ignore if we don't have priority */
+ if (priority < data->priority)
+ return;
+
+ /* allocate a copy of the data */
+ astring_cpych(data->data, datastart, dataend + 1 - datastart);
+ data->priority = priority;
+
+ /* bump the seqid and clear the error reporting */
+ data->seqid++;
+ data->error_reported = FALSE;
+}
+
+
+/*-------------------------------------------------
+ parse_option_name - read data from an option_entry
+ name into an option_data structure
+-------------------------------------------------*/
+
+static int parse_option_name(core_options *opts, const char *srcstring, options_data *data)
+{
+ const char *start;
+ const char *end = NULL;
+ int curentry;
+
+ /* start with the original string and loop over entries */
+ start = srcstring;
+ for (curentry = 0; curentry < ARRAY_LENGTH(data->links); curentry++)
+ {
+ /* find the end of this entry and copy the string */
+ for (end = start; *end != 0 && *end != ';' && *end != '('; end++)
+ ;
+ data->links[curentry].name = astring_dupch(start, end - start);
+
+ /* if we hit the end of the source, stop */
+ if (*end != ';')
+ break;
+ start = end + 1;
+ }
+
+ /* have we found a range? */
+ if (end != NULL && *end == '(')
+ {
+ if (sscanf(end, "(%d-%d)", &data->range_minimum.i, &data->range_maximum.i) == 2)
+ data->range_type = OPTION_RANGE_INT;
+ else if (sscanf(end, "(%f-%f)", &data->range_minimum.f, &data->range_maximum.f) == 2)
+ data->range_type = OPTION_RANGE_FLOAT;
+ }
+ return curentry;
+}
+
+
+/*-------------------------------------------------
+ message - outputs a message to a listener
+-------------------------------------------------*/
+
+static void message(core_options *opts, options_message msgtype, const char *format, ...)
+{
+ char buf[1024];
+ va_list argptr;
+
+ /* output a message if there is a non-NULL handler for it */
+ if (opts->output[msgtype] != NULL)
+ {
+ va_start(argptr, format);
+ vsprintf(buf, format, argptr);
+ va_end(argptr);
+
+ (*opts->output[msgtype])(buf);
+ }
+}
+
+
+/*-------------------------------------------------
+ hash_value - computes the hash value for a string
+-------------------------------------------------*/
+
+static UINT32 hash_value(core_options *opts, const char *str)
+{
+ UINT32 hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c;
+
+ return hash % ARRAY_LENGTH(opts->hashtable);
+}
+
+
+/*-------------------------------------------------
+ output_printf - outputs an arbitrary message
+ to a callback
+-------------------------------------------------*/
+
+static void output_printf(void (*output)(const char *s), const char *format, ...)
+{
+ char buf[1024];
+ va_list argptr;
+
+ va_start(argptr, format);
+ vsprintf(buf, format, argptr);
+ va_end(argptr);
+
+ output(buf);
+}