mame/src/lib/util/opresolv.c

566 lines
12 KiB
C

/****************************************************************************
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];
}