summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/util/opresolv.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/util/opresolv.c')
-rw-r--r--trunk/src/lib/util/opresolv.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/trunk/src/lib/util/opresolv.c b/trunk/src/lib/util/opresolv.c
new file mode 100644
index 00000000000..efc9306b8b5
--- /dev/null
+++ b/trunk/src/lib/util/opresolv.c
@@ -0,0 +1,565 @@
+/****************************************************************************
+
+ opresolv.h
+
+ Extensible ranged option resolution handling
+
+****************************************************************************/
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pool.h"
+#include "corestr.h"
+#include "opresolv.h"
+
+enum resolution_entry_state
+{
+ RESOLUTION_ENTRY_STATE_UNSPECIFIED,
+ RESOLUTION_ENTRY_STATE_SPECIFIED
+};
+
+struct option_resolution_entry
+{
+ const option_guide *guide_entry;
+ enum resolution_entry_state state;
+ union
+ {
+ int int_value;
+ const char *str_value;
+ } u;
+};
+
+struct _option_resolution
+{
+ object_pool *pool;
+ const char *specification;
+ size_t option_count;
+ struct option_resolution_entry *entries;
+};
+
+static optreserr_t resolve_single_param(const char *specification, int *param_value,
+ struct OptionRange *range, size_t range_count)
+{
+ int FLAG_IN_RANGE = 0x01;
+ int FLAG_IN_DEFAULT = 0x02;
+ int FLAG_DEFAULT_SPECIFIED = 0x04;
+ int FLAG_HALF_RANGE = 0x08;
+
+ int last_value = 0;
+ int value = 0;
+ int flags = 0;
+ const char *s = specification;
+
+ while(*s && !isalpha(*s))
+ {
+ if (*s == '-')
+ {
+ /* range specifier */
+ if (flags & (FLAG_IN_RANGE|FLAG_IN_DEFAULT))
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+ flags |= FLAG_IN_RANGE;
+ s++;
+
+ if (range)
+ {
+ range->max = -1;
+ if ((flags & FLAG_HALF_RANGE) == 0)
+ {
+ range->min = -1;
+ flags |= FLAG_HALF_RANGE;
+ }
+ }
+ }
+ else if (*s == '[')
+ {
+ /* begin default value */
+ if (flags & (FLAG_IN_DEFAULT|FLAG_DEFAULT_SPECIFIED))
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+ flags |= FLAG_IN_DEFAULT;
+ s++;
+ }
+ else if (*s == ']')
+ {
+ /* end default value */
+ if ((flags & FLAG_IN_DEFAULT) == 0)
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+ flags &= ~FLAG_IN_DEFAULT;
+ flags |= FLAG_DEFAULT_SPECIFIED;
+ s++;
+
+ if (param_value && *param_value == -1)
+ *param_value = value;
+ }
+ else if (*s == '/')
+ {
+ /* value separator */
+ if (flags & (FLAG_IN_DEFAULT|FLAG_IN_RANGE))
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+ s++;
+
+ /* if we are spitting out ranges, complete the range */
+ if (range && (flags & FLAG_HALF_RANGE))
+ {
+ range++;
+ flags &= ~FLAG_HALF_RANGE;
+ if (--range_count == 0)
+ range = NULL;
+ }
+ }
+ else if (*s == ';')
+ {
+ /* basic separator */
+ s++;
+ }
+ else if (isdigit(*s))
+ {
+ /* numeric value */
+ last_value = value;
+ value = 0;
+ do
+ {
+ value *= 10;
+ value += *s - '0';
+ s++;
+ }
+ while(isdigit(*s));
+
+ if (range)
+ {
+ if ((flags & FLAG_HALF_RANGE) == 0)
+ {
+ range->min = value;
+ flags |= FLAG_HALF_RANGE;
+ }
+ range->max = value;
+ }
+
+ /* if we have a value; check to see if it is out of range */
+ if (param_value && (*param_value != -1) && (*param_value != value))
+ {
+ if ((last_value < *param_value) && (*param_value < value))
+ {
+ if ((flags & FLAG_IN_RANGE) == 0)
+ return OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE;
+ }
+ }
+ flags &= ~FLAG_IN_RANGE;
+ }
+ else
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+ }
+
+ /* we can't have zero length guidelines strings */
+ if (s == specification)
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+
+ return OPTIONRESOLUTION_ERROR_SUCCESS;
+}
+
+
+
+static const char *lookup_in_specification(const char *specification, const option_guide *option)
+{
+ const char *s;
+ s = strchr(specification, option->parameter);
+ return s ? s + 1 : NULL;
+}
+
+
+
+option_resolution *option_resolution_create(const option_guide *guide, const char *specification)
+{
+ option_resolution *resolution = NULL;
+ const option_guide *guide_entry;
+ int option_count;
+ int opt = -1;
+ object_pool *pool;
+
+ assert(guide);
+
+ /* first count the number of options specified in the guide */
+ option_count = option_resolution_countoptions(guide, specification);
+
+ /* create a memory pool for this structure */
+ pool = pool_alloc_lib(NULL);
+ if (!pool)
+ goto outofmemory;
+
+ /* allocate the main structure */
+ resolution = (option_resolution *)pool_malloc_lib(pool, sizeof(option_resolution));
+ if (!resolution)
+ goto outofmemory;
+ memset(resolution, 0, sizeof(*resolution));
+ resolution->pool = pool;
+
+ /* set up the entries list */
+ resolution->option_count = option_count;
+ resolution->specification = specification;
+ resolution->entries = (option_resolution_entry *)pool_malloc_lib(resolution->pool, sizeof(struct option_resolution_entry) * option_count);
+ if (!resolution->entries)
+ goto outofmemory;
+ memset(resolution->entries, 0, sizeof(struct option_resolution_entry) * option_count);
+
+ /* initialize each of the entries */
+ opt = 0;
+ guide_entry = guide;
+ while(guide_entry->option_type != OPTIONTYPE_END)
+ {
+ switch(guide_entry->option_type) {
+ case OPTIONTYPE_INT:
+ case OPTIONTYPE_ENUM_BEGIN:
+ case OPTIONTYPE_STRING:
+ if (lookup_in_specification(specification, guide_entry))
+ resolution->entries[opt++].guide_entry = guide_entry;
+ break;
+ case OPTIONTYPE_ENUM_VALUE:
+ break;
+ default:
+ goto unexpected;
+ }
+ guide_entry++;
+ }
+ assert(opt == option_count);
+ return resolution;
+
+unexpected:
+ assert(FALSE);
+outofmemory:
+ if (resolution)
+ option_resolution_close(resolution);
+ return NULL;
+}
+
+
+
+optreserr_t option_resolution_add_param(option_resolution *resolution, const char *param, const char *value)
+{
+ int i;
+ int must_resolve;
+ optreserr_t err;
+ const char *option_specification;
+ struct option_resolution_entry *entry = NULL;
+
+ for (i = 0; i < resolution->option_count; i++)
+ {
+ if (!strcmp(param, resolution->entries[i].guide_entry->identifier))
+ {
+ entry = &resolution->entries[i];
+ break;
+ }
+ }
+ if (!entry)
+ return OPTIONRESOLUTION_ERROR_PARAMNOTFOUND;
+
+ if (entry->state != RESOLUTION_ENTRY_STATE_UNSPECIFIED)
+ return OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED;
+
+ switch(entry->guide_entry->option_type) {
+ case OPTIONTYPE_INT:
+ entry->u.int_value = atoi(value);
+ entry->state = RESOLUTION_ENTRY_STATE_SPECIFIED;
+ must_resolve = TRUE;
+ break;
+
+ case OPTIONTYPE_STRING:
+ entry->u.str_value = pool_strdup_lib(resolution->pool, value);
+ if (!entry->u.str_value)
+ {
+ err = OPTIONRESOLUTION_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ entry->state = RESOLUTION_ENTRY_STATE_SPECIFIED;
+ must_resolve = FALSE;
+ break;
+
+ case OPTIONTYPE_ENUM_BEGIN:
+ for (i = 1; entry->guide_entry[i].option_type == OPTIONTYPE_ENUM_VALUE; i++)
+ {
+ if (!core_stricmp(value, entry->guide_entry[i].identifier))
+ {
+ entry->u.int_value = entry->guide_entry[i].parameter;
+ entry->state = RESOLUTION_ENTRY_STATE_SPECIFIED;
+ break;
+ }
+ }
+ if (entry->state != RESOLUTION_ENTRY_STATE_SPECIFIED)
+ {
+ err = OPTIONRESOLUTION_ERROR_BADPARAM;
+ goto done;
+ }
+ must_resolve = TRUE;
+ break;
+
+ default:
+ err = OPTIONRESOLTUION_ERROR_INTERNAL;
+ assert(0);
+ goto done;
+ }
+
+ /* do a resolution step if necessary */
+ if (must_resolve)
+ {
+ option_specification = lookup_in_specification(resolution->specification, entry->guide_entry);
+ err = resolve_single_param(option_specification, &entry->u.int_value, NULL, 0);
+ if (err)
+ goto done;
+
+ /* did we not get a real value? */
+ if (entry->u.int_value < 0)
+ {
+ err = OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED;
+ goto done;
+ }
+ }
+
+ err = OPTIONRESOLUTION_ERROR_SUCCESS;
+
+done:
+ return err;
+}
+
+
+
+void option_resolution_close(option_resolution *resolution)
+{
+ pool_free_lib(resolution->pool);
+}
+
+
+
+optreserr_t option_resolution_finish(option_resolution *resolution)
+{
+ int i;
+ optreserr_t err;
+ struct option_resolution_entry *entry;
+ const char *option_specification;
+
+ for (i = 0; i < resolution->option_count; i++)
+ {
+ entry = &resolution->entries[i];
+
+ if (entry->state == RESOLUTION_ENTRY_STATE_UNSPECIFIED)
+ {
+ switch(entry->guide_entry->option_type) {
+ case OPTIONTYPE_INT:
+ case OPTIONTYPE_ENUM_BEGIN:
+ option_specification = lookup_in_specification(resolution->specification, entry->guide_entry);
+ assert(option_specification);
+ entry->u.int_value = -1;
+ err = resolve_single_param(option_specification, &entry->u.int_value, NULL, 0);
+ if (err)
+ return err;
+ break;
+
+ case OPTIONTYPE_STRING:
+ entry->u.str_value = "";
+ break;
+
+ default:
+ assert(FALSE);
+ return OPTIONRESOLTUION_ERROR_INTERNAL;
+ }
+ entry->state = RESOLUTION_ENTRY_STATE_SPECIFIED;
+ }
+ }
+ return OPTIONRESOLUTION_ERROR_SUCCESS;
+}
+
+
+
+static const struct option_resolution_entry *option_resolution_lookup_entry(option_resolution *resolution, int option_char)
+{
+ size_t i;
+ const struct option_resolution_entry *entry;
+
+ for (i = 0; i < resolution->option_count; i++)
+ {
+ entry = &resolution->entries[i];
+
+ switch(entry->guide_entry->option_type) {
+ case OPTIONTYPE_INT:
+ case OPTIONTYPE_STRING:
+ case OPTIONTYPE_ENUM_BEGIN:
+ if (entry->guide_entry->parameter == option_char)
+ return entry;
+ break;
+
+ default:
+ assert(FALSE);
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+
+
+int option_resolution_lookup_int(option_resolution *resolution, int option_char)
+{
+ const struct option_resolution_entry *entry;
+ entry = option_resolution_lookup_entry(resolution, option_char);
+ return entry ? entry->u.int_value : -1;
+}
+
+
+
+const char *option_resolution_lookup_string(option_resolution *resolution, int option_char)
+{
+ const struct option_resolution_entry *entry;
+ entry = option_resolution_lookup_entry(resolution, option_char);
+ return entry ? entry->u.str_value : NULL;
+}
+
+
+
+const char *option_resolution_specification(option_resolution *resolution)
+{
+ return resolution->specification;
+}
+
+
+
+const option_guide *option_resolution_find_option(option_resolution *resolution, int option_char)
+{
+ const struct option_resolution_entry *entry;
+ entry = option_resolution_lookup_entry(resolution, option_char);
+ return entry ? entry->guide_entry : NULL;
+}
+
+
+
+const option_guide *option_resolution_index_option(option_resolution *resolution, int indx)
+{
+ if ((indx < 0) || (indx >= resolution->option_count))
+ return NULL;
+ return resolution->entries[indx].guide_entry;
+}
+
+
+
+int option_resolution_countoptions(const option_guide *guide, const char *specification)
+{
+ int option_count = 0;
+
+ while(guide->option_type != OPTIONTYPE_END)
+ {
+ switch(guide->option_type) {
+ case OPTIONTYPE_INT:
+ case OPTIONTYPE_STRING:
+ case OPTIONTYPE_ENUM_BEGIN:
+ if (lookup_in_specification(specification, guide))
+ option_count++;
+ break;
+ case OPTIONTYPE_ENUM_VALUE:
+ break;
+ default:
+ assert(FALSE);
+ return 0;
+ }
+ guide++;
+ }
+ return option_count;
+}
+
+
+
+optreserr_t option_resolution_listranges(const char *specification, int option_char,
+ struct OptionRange *range, size_t range_count)
+{
+ assert(range_count > 0);
+
+ /* clear out range */
+ memset(range, -1, sizeof(*range) * range_count);
+ range_count--;
+
+ specification = strchr(specification, option_char);
+ if (!specification)
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+
+ return resolve_single_param(specification + 1, NULL, range, range_count);
+}
+
+
+
+optreserr_t option_resolution_getdefault(const char *specification, int option_char, int *val)
+{
+ assert(val);
+
+ /* clear out default */
+ *val = -1;
+
+ specification = strchr(specification, option_char);
+ if (!specification)
+ {
+ return OPTIONRESOLUTION_ERROR_SYNTAX;
+ }
+
+ return resolve_single_param(specification + 1, val, NULL, 0);
+}
+
+
+
+optreserr_t option_resolution_isvalidvalue(const char *specification, int option_char, int val)
+{
+ optreserr_t err;
+ struct OptionRange ranges[256];
+ int i;
+
+ err = option_resolution_listranges(specification, option_char, ranges, ARRAY_LENGTH(ranges));
+ if (err)
+ return err;
+
+ for (i = 0; (ranges[i].min >= 0) && (ranges[i].max >= 0); i++)
+ {
+ if ((ranges[i].min <= val) && (ranges[i].max >= val))
+ return OPTIONRESOLUTION_ERROR_SUCCESS;
+ }
+ return OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE;
+}
+
+
+
+int option_resolution_contains(const char *specification, int option_char)
+{
+ return strchr(specification, option_char) != NULL;
+}
+
+
+
+const char *option_resolution_error_string(optreserr_t err)
+{
+ static const char *const errors[] =
+ {
+ "The operation completed successfully", /* OPTIONRESOLUTION_ERROR_SUCCESS */
+ "Out of memory", /* OPTIONRESOLUTION_ERROR_OUTOFMEMORY */
+ "Parameter out of range", /* OPTIONRESOLUTION_ERROR_PARAMOUTOFRANGE */
+ "Parameter not specified", /* OPTIONRESOLUTION_ERROR_PARAMNOTSPECIFIED */
+ "Unknown parameter", /* OPTIONRESOLUTION_ERROR_PARAMNOTFOUND */
+ "Parameter specified multiple times", /* OPTIONRESOLUTION_ERROR_PARAMALREADYSPECIFIED */
+ "Invalid parameter", /* OPTIONRESOLUTION_ERROR_BADPARAM */
+ "Syntax error", /* OPTIONRESOLUTION_ERROR_SYNTAX */
+ "Internal error" /* OPTIONRESOLTUION_ERROR_INTERNAL */
+ };
+
+ if ((err < 0) || (err >= ARRAY_LENGTH(errors)))
+ return NULL;
+ return errors[err];
+}
+