mirror of
https://github.com/holub/mame
synced 2025-05-12 09:08:53 +03:00
566 lines
12 KiB
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];
|
|
}
|
|
|