simplesim-3.0/options.c

1800 lines
46 KiB
C

/* options.c - options package routines */
/* SimpleScalar(TM) Tool Suite
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
* All Rights Reserved.
*
* THIS IS A LEGAL DOCUMENT, BY USING SIMPLESCALAR,
* YOU ARE AGREEING TO THESE TERMS AND CONDITIONS.
*
* No portion of this work may be used by any commercial entity, or for any
* commercial purpose, without the prior, written permission of SimpleScalar,
* LLC (info@simplescalar.com). Nonprofit and noncommercial use is permitted
* as described below.
*
* 1. SimpleScalar is provided AS IS, with no warranty of any kind, express
* or implied. The user of the program accepts full responsibility for the
* application of the program and the use of any results.
*
* 2. Nonprofit and noncommercial use is encouraged. SimpleScalar may be
* downloaded, compiled, executed, copied, and modified solely for nonprofit,
* educational, noncommercial research, and noncommercial scholarship
* purposes provided that this notice in its entirety accompanies all copies.
* Copies of the modified software can be delivered to persons who use it
* solely for nonprofit, educational, noncommercial research, and
* noncommercial scholarship purposes provided that this notice in its
* entirety accompanies all copies.
*
* 3. ALL COMMERCIAL USE, AND ALL USE BY FOR PROFIT ENTITIES, IS EXPRESSLY
* PROHIBITED WITHOUT A LICENSE FROM SIMPLESCALAR, LLC (info@simplescalar.com).
*
* 4. No nonprofit user may place any restrictions on the use of this software,
* including as modified by the user, by any other authorized user.
*
* 5. Noncommercial and nonprofit users may distribute copies of SimpleScalar
* in compiled or executable form as set forth in Section 2, provided that
* either: (A) it is accompanied by the corresponding machine-readable source
* code, or (B) it is accompanied by a written offer, with no time limit, to
* give anyone a machine-readable copy of the corresponding source code in
* return for reimbursement of the cost of distribution. This written offer
* must permit verbatim duplication by anyone, or (C) it is distributed by
* someone who received only the executable form, and is accompanied by a
* copy of the written offer of source code.
*
* 6. SimpleScalar was developed by Todd M. Austin, Ph.D. The tool suite is
* currently maintained by SimpleScalar LLC (info@simplescalar.com). US Mail:
* 2395 Timbercrest Court, Ann Arbor, MI 48105.
*
* Copyright (C) 1994-2003 by Todd M. Austin, Ph.D. and SimpleScalar, LLC.
*/
#include <stdio.h>
#include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h>
#else /* _MSC_VER */
#define chdir _chdir
#define getcwd _getcwd
#endif
#include <string.h>
#include <ctype.h>
#include <float.h>
#include "host.h"
#include "misc.h"
#include "options.h"
/* create a new option database */
struct opt_odb_t *
opt_new(orphan_fn_t orphan_fn) /* user-specified orphan parser */
{
struct opt_odb_t *odb;
odb = (struct opt_odb_t *)calloc(1, sizeof(struct opt_odb_t));
if (!odb)
fatal("out of virtual memory");
odb->options = NULL;
odb->orphan_fn = orphan_fn;
odb->header = NULL;
odb->notes = NULL;
return odb;
}
/* free an option database */
void
opt_delete(struct opt_odb_t *odb) /* option database */
{
struct opt_opt_t *opt, *opt_next;
struct opt_note_t *note, *note_next;
/* free all options */
for (opt=odb->options; opt; opt=opt_next)
{
opt_next = opt->next;
opt->next = NULL;
free(opt);
}
/* free all notes */
for (note = odb->notes; note != NULL; note = note_next)
{
note_next = note->next;
note->next = NULL;
free(note);
}
odb->notes = NULL;
free(odb);
}
/* add option OPT to option database ODB */
static void
add_option(struct opt_odb_t *odb, /* option database */
struct opt_opt_t *opt) /* option variable */
{
struct opt_opt_t *elt, *prev;
/* sanity checks on option name */
if (opt->name[0] != '-')
panic("option `%s' must start with a `-'", opt->name);
/* add to end of option list */
for (prev=NULL, elt=odb->options; elt != NULL; prev=elt, elt=elt->next)
{
/* sanity checks on option name */
if (elt->name[0] == opt->name[0] && !strcmp(elt->name, opt->name))
panic("option `%s' is multiply defined", opt->name);
}
if (prev != NULL)
prev->next = opt;
else /* prev == NULL */
odb->options = opt;
opt->next = NULL;
}
/* register an integer option variable */
void
opt_reg_int(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
int *var, /* target variable */
int def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format) /* optional value print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12d";
opt->oc = oc_int;
opt->variant.for_int.var = var;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register an integer option list */
void
opt_reg_int_list(struct opt_odb_t *odb,/* option database */
char *name, /* option name */
char *desc, /* option description */
int *vars, /* pointer to option array */
int nvars, /* total entries in option array */
int *nelt, /* number of entries parsed */
int *def_val, /* default value of option array */
int print, /* print during `-dumpconfig'? */
char *format, /* optional user print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%d";
opt->oc = oc_int;
opt->variant.for_int.var = vars;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* register an unsigned integer option variable */
void
opt_reg_uint(struct opt_odb_t *odb, /* option database */
char *name, /* option name */
char *desc, /* option description */
unsigned int *var, /* pointer to option variable */
unsigned int def_val, /* default value of option variable */
int print, /* print during `-dumpconfig'? */
char *format) /* optional user print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12u";
opt->oc = oc_uint;
opt->variant.for_uint.var = var;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register an unsigned integer option list */
void
opt_reg_uint_list(struct opt_odb_t *odb,/* option database */
char *name, /* option name */
char *desc, /* option description */
unsigned int *vars, /* pointer to option array */
int nvars, /* total entries in option array */
int *nelt, /* number of elements parsed */
unsigned int *def_val,/* default value of option array */
int print, /* print opt during `-dumpconfig'? */
char *format, /* optional user print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%u";
opt->oc = oc_uint;
opt->variant.for_uint.var = vars;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* register a single-precision floating point option variable */
void
opt_reg_float(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
float *var, /* target option variable */
float def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format) /* optional value print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12.4f";
opt->oc = oc_float;
opt->variant.for_float.var = var;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register a single-precision floating point option array */
void
opt_reg_float_list(struct opt_odb_t *odb,/* option data base */
char *name, /* option name */
char *desc, /* option description */
float *vars, /* target array */
int nvars, /* target array size */
int *nelt, /* number of args parsed goes here */
float *def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format, /* optional value print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%.4f";
opt->oc = oc_float;
opt->variant.for_float.var = vars;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* register a double-precision floating point option variable */
void
opt_reg_double(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
double *var, /* target variable */
double def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format) /* option value print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12.4f";
opt->oc = oc_double;
opt->variant.for_double.var = var;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register a double-precision floating point option array */
void
opt_reg_double_list(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
double *vars, /* target array */
int nvars, /* target array size */
int *nelt, /* number of args parsed goes here */
double *def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format, /* option value print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%.4f";
opt->oc = oc_double;
opt->variant.for_double.var = vars;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* bind an enumeration string to an enumeration value */
static int
bind_to_enum(char *str, /* string to bind to an enum */
char **emap, /* enumeration string map */
int *eval, /* enumeration value map, optional */
int emap_sz, /* size of maps */
int *res) /* enumeration string value result */
{
int i;
/* string enumeration string map */
for (i=0; i<emap_sz; i++)
{
if (!strcmp(str, emap[i]))
{
if (eval)
{
/* bind to eval value */
*res = eval[i];
}
else
{
/* bind to string index */
*res = i;
}
return TRUE;
}
}
/* not found */
*res = -1;
return FALSE;
}
/* bind a enumeration value to an enumeration string */
static char *
bind_to_str(int val, /* enumeration value */
char **emap, /* enumeration string map */
int *eval, /* enumeration value map, optional */
int emap_sz) /* size of maps */
{
int i;
if (eval)
{
/* bind to first matching eval value */
for (i=0; i<emap_sz; i++)
{
if (eval[i] == val)
{
/* found */
return emap[i];
}
}
/* not found */
return NULL;
}
else
{
/* bind to string at index */
if (val >= emap_sz)
{
/* invalid index */
return NULL;
}
/* else, index is in range */
return emap[val];
}
}
/* register an enumeration option variable, NOTE: all enumeration option
variables must be of type `int', since true enum variables may be allocated
with variable sizes by some compilers */
void
opt_reg_enum(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
int *var, /* target variable */
char *def_val, /* default variable value */
char **emap, /* enumeration string map */
int *eval, /* enumeration value map, optional */
int emap_sz, /* size of maps */
int print, /* print during `-dumpconfig'? */
char *format) /* option value print format */
{
int enum_val;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12s";
opt->oc = oc_enum;
opt->variant.for_enum.var = var;
opt->variant.for_enum.emap = emap;
opt->variant.for_enum.eval = eval;
opt->variant.for_enum.emap_sz = emap_sz;
if (def_val)
{
if (!bind_to_enum(def_val, emap, eval, emap_sz, &enum_val))
fatal("could not bind default value for option `%s'", name);
}
else
enum_val = 0;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = enum_val;
}
/* register an enumeration option array, NOTE: all enumeration option variables
must be of type `int', since true enum variables may be allocated with
variable sizes by some compilers */
void
opt_reg_enum_list(struct opt_odb_t *odb,/* option data base */
char *name, /* option name */
char *desc, /* option description */
int *vars, /* target array */
int nvars, /* target array size */
int *nelt, /* number of args parsed goes here */
char *def_val, /* default variable value */
char **emap, /* enumeration string map */
int *eval, /* enumeration value map, optional */
int emap_sz, /* size of maps */
int print, /* print during `-dumpconfig'? */
char *format, /* option value print format */
int accrue) /* accrue list across uses */
{
int i, enum_val;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%s";
opt->oc = oc_enum;
opt->variant.for_enum.var = vars;
opt->variant.for_enum.emap = emap;
opt->variant.for_enum.eval = eval;
opt->variant.for_enum.emap_sz = emap_sz;
if (def_val)
{
if (!bind_to_enum(def_val, emap, eval, emap_sz, &enum_val))
fatal("could not bind default value for option `%s'", name);
}
else
enum_val = 0;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = enum_val;
}
/* pre-defined boolean flag operands */
#define NUM_FLAGS 28
static char *flag_emap[NUM_FLAGS] = {
"true", "t", "T", "True", "TRUE", "1", "y", "Y", "yes", "Yes", "YES",
"on", "On", "ON",
"false", "f", "F", "False", "FALSE", "0", "n", "N", "no", "No", "NO",
"off", "Off", "OFF"
};
static int flag_eval[NUM_FLAGS] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* register a boolean flag option variable */
void
opt_reg_flag(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
int *var, /* target variable */
int def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format) /* optional value print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12s";
opt->oc = oc_flag;
opt->variant.for_enum.var = var;
opt->variant.for_enum.emap = flag_emap;
opt->variant.for_enum.eval = flag_eval;
opt->variant.for_enum.emap_sz = NUM_FLAGS;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register a boolean flag option array */
void
opt_reg_flag_list(struct opt_odb_t *odb,/* option database */
char *name, /* option name */
char *desc, /* option description */
int *vars, /* pointer to option array */
int nvars, /* total entries in option array */
int *nelt, /* number of elements parsed */
int *def_val, /* default array value */
int print, /* print during `-dumpconfig'? */
char *format, /* optional value print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%s";
opt->oc = oc_flag;
opt->variant.for_enum.var = vars;
opt->variant.for_enum.emap = flag_emap;
opt->variant.for_enum.eval = flag_eval;
opt->variant.for_enum.emap_sz = NUM_FLAGS;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* register a string option variable */
void
opt_reg_string(struct opt_odb_t *odb, /* option data base */
char *name, /* option name */
char *desc, /* option description */
char **var, /* pointer to string option variable */
char *def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format) /* optional value print format */
{
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = 1;
opt->nelt = NULL;
opt->format = format ? format : "%12s";
opt->oc = oc_string;
opt->variant.for_string.var = var;
opt->print = print;
opt->accrue = FALSE;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
*var = def_val;
}
/* register a string option array */
void
opt_reg_string_list(struct opt_odb_t *odb,/* option data base */
char *name, /* option name */
char *desc, /* option description */
char **vars, /* pointer to option string array */
int nvars, /* target array size */
int *nelt, /* number of args parsed goes here */
char **def_val, /* default variable value */
int print, /* print during `-dumpconfig'? */
char *format, /* optional value print format */
int accrue) /* accrue list across uses */
{
int i;
struct opt_opt_t *opt;
opt = (struct opt_opt_t *)calloc(1, sizeof(struct opt_opt_t));
if (!opt)
fatal("out of virtual memory");
opt->name = name;
opt->desc = desc;
opt->nvars = nvars;
opt->nelt = nelt;
opt->format = format ? format : "%s";
opt->oc = oc_string;
opt->variant.for_string.var = vars;
opt->print = print;
opt->accrue = accrue;
/* place on ODB's option list */
opt->next = NULL;
add_option(odb, opt);
/* set default value */
for (i=0; i < *nelt; i++)
vars[i] = def_val[i];
}
/* process command line arguments, returns index of next argument to parse */
int
process_option(struct opt_odb_t *odb, /* option database */
int index, /* index of the first arg to parse */
int argc, /* total number of arguments */
char **argv) /* argument string array */
{
int cnt, ent, nvars;
char *endp;
double tmp;
struct opt_opt_t *opt;
/* locate the option in the option database */
for (opt=odb->options; opt != NULL; opt=opt->next)
{
if (!strcmp(opt->name, argv[index]))
break;
}
if (!opt)
{
/* no one registered this option */
fatal("option `%s' is undefined", argv[index]);
}
index++;
/* process option arguments */
switch (opt->oc)
{
case oc_int:
/* this option needs at least one argument */
if (index >= argc
|| (argv[index][0] == '-' && !isdigit((int)argv[index][1])))
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars &&
(argv[index][0] != '-' || isdigit((int)argv[index][1])))
{
opt->variant.for_int.var[ent] = strtol(argv[index], &endp, 0);
if (*endp)
{
/* could not parse entire argument */
fatal("could not parse argument `%s' of option `%s'",
argv[index], opt->name);
}
/* else, argument converted correctly */
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
case oc_uint:
/* this option needs at least one argument */
if (index >= argc || argv[index][0] == '-')
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars && argv[index][0] != '-')
{
opt->variant.for_uint.var[ent] = strtoul(argv[index], &endp, 0);
if (*endp)
{
/* could not parse entire argument */
fatal("could not parse argument `%s' of option `%s'",
argv[index], opt->name);
}
/* else, argument converted correctly */
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
case oc_float:
/* this option needs at least one argument */
if (index >= argc
|| (argv[index][0] == '-' && !isdigit((int)argv[index][1])))
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars &&
(argv[index][0] != '-' || isdigit((int)argv[index][1])))
{
tmp = strtod(argv[index], &endp);
if (*endp)
{
/* could not parse entire argument */
fatal("could not parse argument `%s' of option `%s'",
argv[index], opt->name);
}
if (tmp < FLT_MIN || tmp > FLT_MAX)
{
/* over/underflow */
fatal("FP over/underflow for argument `%s' of option `%s'",
argv[index], opt->name);
}
/* else, argument converted correctly */
opt->variant.for_float.var[ent] = (float)tmp;
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
case oc_double:
/* this option needs at least one argument */
if (index >= argc
|| (argv[index][0] == '-' && !isdigit((int)argv[index][1])))
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars &&
(argv[index][0] != '-' || isdigit((int)argv[index][1])))
{
opt->variant.for_double.var[ent] = strtod(argv[index], &endp);
if (*endp)
{
/* could not parse entire argument */
fatal("could not parse argument `%s' of option `%s'",
argv[index], opt->name);
}
/* else, argument converted correctly */
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
case oc_enum:
/* this option needs at least one argument */
if (index >= argc || argv[index][0] == '-')
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars && argv[index][0] != '-')
{
if (!bind_to_enum(argv[index],
opt->variant.for_enum.emap,
opt->variant.for_enum.eval,
opt->variant.for_enum.emap_sz,
&opt->variant.for_enum.var[ent]))
{
/* could not parse entire argument */
fatal("could not parse argument `%s' of option `%s'",
argv[index], opt->name);
}
/* else, argument converted correctly */
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
case oc_flag:
/* check if this option has at least one argument */
if (index >= argc || argv[index][0] == '-')
{
/* no arguments available, default value for flag is true */
opt->variant.for_enum.var[0] = TRUE;
break;
}
/* else, parse boolean argument(s) */
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
while (index < argc && cnt < nvars && argv[index][0] != '-')
{
if (!bind_to_enum(argv[index],
opt->variant.for_enum.emap,
opt->variant.for_enum.eval,
opt->variant.for_enum.emap_sz,
&opt->variant.for_enum.var[ent]))
{
/* could not parse entire argument, default to true */
opt->variant.for_enum.var[ent] = TRUE;
break;
}
else
{
/* else, argument converted correctly */
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
}
break;
case oc_string:
/* this option needs at least one argument */
if (index >= argc || argv[index][0] == '-')
{
/* no arguments available */
fatal("option `%s' requires an argument", opt->name);
}
cnt = 0;
if (opt->accrue)
{
ent = opt->nelt ? *opt->nelt : 0;
nvars = 1;
if (ent >= opt->nvars)
fatal("too many invocations of option `%s'", opt->name);
}
else
{
ent = 0;
if (opt->nelt)
*opt->nelt = 0;
nvars = opt->nvars;
}
/* parse all arguments */
while (index < argc && cnt < nvars && argv[index][0] != '-')
{
opt->variant.for_string.var[ent] = argv[index];
if (opt->nelt)
(*opt->nelt)++;
cnt++; index++; ent++;
}
break;
default:
panic("bogus option class");
}
return index;
}
/* forward declarations */
static void process_file(struct opt_odb_t *odb, char *fname, int depth);
static void dump_config(struct opt_odb_t *odb, char *fname);
/* process a command line, internal version that tracks `-config' depth */
static void
__opt_process_options(struct opt_odb_t *odb, /* option data base */
int argc, /* number of arguments */
char **argv, /* argument array */
int depth) /* `-config' option depth */
{
int index, do_dumpconfig;
char *dumpconfig_name;
index = 0;
do_dumpconfig = FALSE;
dumpconfig_name = NULL;
/* visit all command line arguments */
while (index < argc)
{
/* process any encountered orphans */
while (index < argc && argv[index][0] != '-')
{
if (depth > 0)
{
/* orphans are not allowed during config file processing */
fatal("orphan `%s' encountered during config file processing",
argv[index]);
}
/* else, call the user-stalled orphan handler */
if (odb->orphan_fn)
{
if (!odb->orphan_fn(index+1, argc, argv))
{
/* done processing command line */
goto done_processing;
}
}
else
{
/* no one claimed this option */
fatal("orphan argument `%s' was unclaimed", argv[index]);
}
/* go to next option */
}
/* done with command line? */
if (index == argc)
{
/* done processing command line */
goto done_processing;
}
/* when finished, argv[index] is an option to parse */
/* process builtin options */
if (!strcmp(argv[index], "-config"))
{
/* handle `-config' builtin option */
index++;
if (index >= argc || argv[index][0] == '-')
{
/* no arguments available */
fatal("option `-config' requires an argument");
}
process_file(odb, argv[index], depth);
index++;
}
else if (!strcmp(argv[index], "-dumpconfig"))
{
/* this is performed *last* */
do_dumpconfig = TRUE;
/* handle `-dumpconfig' builtin option */
index++;
if (index >= argc
|| (argv[index][0] == '-' && argv[index][1] != '\0'))
{
/* no arguments available */
fatal("option `-dumpconfig' requires an argument");
}
dumpconfig_name = argv[index];
index++;
}
else
{
/* process user-installed option */
index = process_option(odb, index, argc, argv);
}
}
done_processing:
if (do_dumpconfig)
dump_config(odb, dumpconfig_name);
}
/* process command line arguments */
void
opt_process_options(struct opt_odb_t *odb, /* option data base */
int argc, /* number of arguments */
char **argv) /* argument array */
{
/* need at least two entries to have an option */
if (argc < 2)
return;
/* process the command, starting at `-config' depth zero */
__opt_process_options(odb, argc-1, argv+1, /* top level */0);
}
/* handle `-config' builtin option */
#define MAX_LINE_ARGS 256
#define MAX_FILENAME_LEN 1024
static void
process_file(struct opt_odb_t *odb, char *fname, int depth)
{
int largc;
char *largv[MAX_LINE_ARGS];
char line[1024], *p, *q;
char cwd[MAX_FILENAME_LEN];
char *header;
FILE *fd;
fd = fopen(fname, "r");
if (!fd)
fatal("could not open configuration file `%s'", fname);
if (!getcwd(cwd, MAX_FILENAME_LEN))
fatal("can't get cwd");
header = strrchr(fname, '/');
if (header != NULL)
{
/* filename is path - get header */
*header = '\0';
if (chdir(fname) == -1)
fatal("can't chdir to %s\n", fname);
*header = '/';
}
while (!feof(fd))
{
#if 0
fprintf(stderr, "!feof(fd): %d, strlen(line): %d, line: %s\n",
!feof(fd), strlen(line), line);
#endif
line[0] = '\n';
/* read a line from the file and chop */
fgets(line, 1024, fd);
if (line[0] == '\0' || line[0] == '\n')
continue;
if (line[strlen(line)-1] == '\n')
line[strlen(line)-1] = '\0';
/* process one line from the file */
largc = 0; p = line;
while (*p)
{
/* skip whitespace */
while (*p != '\0' && (*p == ' ' || *p == '\t'))
p++;
/* ignore empty lines and comments */
if (*p == '\0' || *p == '#')
break;
/* skip to the end of the argument */
q = p;
while (*q != '\0' && *q != ' ' && *q != '\t')
q++;
if (*q)
{
*q = '\0';
q++;
}
/* marshall an option array */
largv[largc++] = mystrdup(p);
if (largc == MAX_LINE_ARGS)
{
if (chdir(cwd) == -1)
fatal("can't chdir back to %s\n", cwd);
fatal("option line too complex in file `%s'", fname);
}
/* go to next argument */
p = q;
}
/* process the line */
if (largc > 0)
__opt_process_options(odb, largc, largv, depth+1);
/* else, empty line */
}
fclose(fd);
if (chdir(cwd) == -1)
fatal("can't chdir back to %s\n", cwd);
}
/* print the value of an option */
void
opt_print_option(struct opt_opt_t *opt,/* option variable */
FILE *fd) /* output stream */
{
int i, nelt;
switch (opt->oc)
{
case oc_int:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
fprintf(fd, opt->format, opt->variant.for_int.var[i]);
fprintf(fd, " ");
}
break;
case oc_uint:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
fprintf(fd, opt->format, opt->variant.for_uint.var[i]);
fprintf(fd, " ");
}
break;
case oc_float:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
fprintf(fd, opt->format, (double)opt->variant.for_float.var[i]);
fprintf(fd, " ");
}
break;
case oc_double:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
fprintf(fd, opt->format, opt->variant.for_double.var[i]);
fprintf(fd, " ");
}
break;
case oc_enum:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
char *estr = bind_to_str(opt->variant.for_enum.var[i],
opt->variant.for_enum.emap,
opt->variant.for_enum.eval,
opt->variant.for_enum.emap_sz);
if (!estr)
panic("could not bind enum `%d' for option `%s'",
opt->variant.for_enum.var[i], opt->name);
fprintf(fd, opt->format, estr);
fprintf(fd, " ");
}
break;
case oc_flag:
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
for (i=0; i<nelt; i++)
{
char *estr = bind_to_str(opt->variant.for_enum.var[i],
opt->variant.for_enum.emap,
opt->variant.for_enum.eval,
opt->variant.for_enum.emap_sz);
if (!estr)
panic("could not bind boolean `%d' for option `%s'",
opt->variant.for_enum.var[i], opt->name);
fprintf(fd, opt->format, estr);
fprintf(fd, " ");
}
break;
case oc_string:
if (!opt->nvars)
{
fprintf(fd, "%12s ", "<null>");
break;
}
if (opt->nelt)
nelt = *(opt->nelt);
else
nelt = 1;
if (nelt == 0)
{
fprintf(fd, "%12s ", "<null>");
break;
}
else
{
for (i=0; i<nelt; i++)
{
fprintf(fd, opt->format,
(opt->variant.for_string.var[i]
? opt->variant.for_string.var[i]
: "<null>"));
fprintf(fd, " ");
}
}
break;
default:
panic("bogus option class");
}
}
/* print any options header */
static void
print_option_header(struct opt_odb_t *odb,/* options database */
FILE *fd) /* output stream */
{
if (!odb->header)
return;
fprintf(fd, "\n%s\n", odb->header);
}
/* print option notes */
static void
print_option_notes(struct opt_odb_t *odb,/* options database */
FILE *fd) /* output stream */
{
struct opt_note_t *note;
if (!odb->notes)
return;
fprintf(fd, "\n");
for (note=odb->notes; note != NULL; note=note->next)
fprintf(fd, "%s\n", note->note);
fprintf(fd, "\n");
}
/* builtin options */
static struct opt_opt_t dumpconfig_opt =
{ NULL, "-dumpconfig", "dump configuration to a file",
0, NULL, NULL, FALSE, FALSE, oc_string };
static struct opt_opt_t config_opt =
{ &dumpconfig_opt, "-config", "load configuration from a file",
0, NULL, NULL, FALSE, FALSE, oc_string };
static struct opt_opt_t *builtin_options = &config_opt;
/* return non-zero if the option is a NULL-valued string option */
int /* non-zero if null string option */
opt_null_string(struct opt_opt_t *opt)
{
return (opt != NULL
&& opt->oc == oc_string
&& (opt->nvars == 0
|| (opt->nelt != NULL && *opt->nelt == 0)
|| (opt->nelt == NULL
&& (opt->variant.for_string.var == NULL
|| opt->variant.for_string.var[0] == NULL))));
}
/* print all options and current values */
void
opt_print_options(struct opt_odb_t *odb,/* option data base */
FILE *fd, /* output stream */
int terse, /* print terse options? */
int notes) /* include notes? */
{
struct opt_opt_t *opt;
if (!odb)
{
/* no options */
return;
}
/* print any options header */
if (notes)
print_option_header(odb, fd);
/* dump out builtin options */
for (opt=builtin_options; opt != NULL; opt=opt->next)
{
if (terse)
fprintf(fd, "# %-27s # %s\n", opt->name, opt->desc);
else
{
fprintf(fd, "# %s\n", opt->desc);
fprintf(fd, "# %-22s\n\n", opt->name);
}
}
/* dump out options from options database */
for (opt=odb->options; opt != NULL; opt=opt->next)
{
if (terse)
{
if (!opt->print || opt_null_string(opt))
fprintf(fd, "# %-14s ", opt->name);
else
fprintf(fd, "%-16s ", opt->name);
opt_print_option(opt, fd);
if (opt->desc)
fprintf(fd, "# %-22s", opt->desc);
fprintf(fd, "\n");
}
else
{
if (opt->desc)
fprintf(fd, "# %s\n", opt->desc);
if (!opt->print || opt_null_string(opt))
fprintf(fd, "# %-20s ", opt->name);
else
fprintf(fd, "%-22s ", opt->name);
opt_print_option(opt, fd);
fprintf(fd, "\n\n");
}
}
/* print option notes */
if (notes)
print_option_notes(odb, fd);
}
/* print help information for an option */
static void
print_help(struct opt_opt_t *opt, /* option variable */
FILE *fd) /* output stream */
{
char *s = NULL;
fprintf(fd, "%-16s ", opt->name);
switch (opt->oc)
{
case oc_int:
if (opt->nvars == 1)
s = "<int>";
else
s = "<int list...>";
break;
case oc_uint:
if (opt->nvars == 1)
s = "<uint>";
else
s = "<uint list...>";
break;
case oc_float:
if (opt->nvars == 1)
s = "<float>";
else
s = "<float list...>";
break;
case oc_double:
if (opt->nvars == 1)
s = "<double>";
else
s = "<double list...>";
break;
case oc_enum:
if (opt->nvars == 1)
s = "<enum>";
else
s = "<enum list...>";
break;
case oc_flag:
if (opt->nvars == 1)
s = "<true|false>";
else
s = "<true|false list...>";
break;
case oc_string:
if (opt->nvars == 0 || opt->nvars == 1)
s = "<string>";
else
s = "<string list...>";
break;
default:
panic("bogus option class");
}
fprintf(fd, "%-16s # ", s);
opt_print_option(opt, fd);
fprintf(fd, "# %-22s\n", opt->desc);
}
/* print option help page with default values */
void
opt_print_help(struct opt_odb_t *odb, /* option data base */
FILE *fd) /* output stream */
{
struct opt_opt_t *opt;
/* print any options header */
print_option_header(odb, fd);
fprintf(fd, "#\n");
fprintf(fd, "%-16s %-16s # %12s # %-22s\n",
"# -option", "<args>", "<default>", "description");
fprintf(fd, "#\n");
/* print out help info for builtin options */
for (opt=builtin_options; opt != NULL; opt=opt->next)
print_help(opt, fd);
/* print out help info for options in options database */
for (opt=odb->options; opt != NULL; opt=opt->next)
print_help(opt, fd);
/* print option notes */
print_option_notes(odb, fd);
}
/* handle `-dumpconfig' builtin option, print options from file argument */
static void
dump_config(struct opt_odb_t *odb, /* option data base */
char *fname) /* output file name, "-" == stdout */
{
FILE *fd;
/* open output file stream */
if (!strcmp(fname, "-"))
fd = stderr;
else
{
fd = fopen(fname, "w");
if (!fd)
fatal("could not open file `%s'", fname);
}
/* print current option values to output stream */
opt_print_options(odb, fd, /* long */FALSE, /* !notes */FALSE);
/* close output stream, if not a standard stream */
if (fd != stdout && fd != stderr)
fclose(fd);
}
/* find an option by name in the option database, returns NULL if not found */
struct opt_opt_t *
opt_find_option(struct opt_odb_t *odb, /* option database */
char *opt_name) /* option name */
{
struct opt_opt_t *opt;
/* search builtin options */
for (opt = builtin_options; opt != NULL; opt = opt->next)
{
if (!strcmp(opt->name, opt_name))
{
/* located option */
return opt;
}
}
/* search user-installed options */
for (opt = odb->options; opt != NULL; opt = opt->next)
{
if (!strcmp(opt->name, opt_name))
{
/* located option */
return opt;
}
}
/* not found */
return NULL;
}
/* register an options header, the header is printed before the option list */
void
opt_reg_header(struct opt_odb_t *odb, /* option database */
char *header) /* options header string */
{
odb->header = header;
}
/* register an option note, notes are printed after the list of options */
void
opt_reg_note(struct opt_odb_t *odb, /* option database */
char *note_str) /* option note */
{
struct opt_note_t *note, *elt, *prev;
note = (struct opt_note_t *)calloc(1, sizeof(struct opt_note_t));
if (!note)
fatal("out of virtual memory");
/* record note */
note->next = NULL;
note->note = note_str;
/* add to end of option notes list */
for (prev=NULL, elt=odb->notes; elt != NULL; prev=elt, elt=elt->next)
/* nada */;
if (prev != NULL)
prev->next = note;
else /* prev == NULL */
odb->notes = note;
note->next = NULL;
}
#ifdef TEST
int
f_orphan_fn(int i, int argc, char **argv)
{
fprintf(stdout, "orphans detected at index %d, arg = `%s'...\n", i, argv[i]);
/* done processing */
return FALSE;
}
#define MAX_VARS 4
void
main(int argc, char **argv)
{
struct opt_odb_t *odb;
int n_int_vars, n_uint_vars, n_float_vars, n_double_vars;
int n_enum_vars, n_enum_eval_vars, n_flag_vars, n_string_vars;
int int_var, int_vars[MAX_VARS];
unsigned int uint_var, uint_vars[MAX_VARS];
float float_var, float_vars[MAX_VARS];
double double_var, double_vars[MAX_VARS];
int flag_var, flag_vars[MAX_VARS];
char *string_var, *string_vars[MAX_VARS];
enum etest_t { enum_a, enum_b, enum_c, enum_d, enum_NUM };
static char *enum_emap[enum_NUM] =
{ "enum_a", "enum_b", "enum_c", "enum_d" };
static int enum_eval[enum_NUM] =
{ enum_d, enum_c, enum_b, enum_a };
int enum_var, enum_vars[MAX_VARS];
int enum_eval_var, enum_eval_vars[MAX_VARS];
/* get an options processor */
odb = opt_new(f_orphan_fn);
opt_reg_int(odb, "-opt:int", "This is an integer option.",
&int_var, 1, /* print */TRUE, /* default format */NULL);
opt_reg_int_list(odb, "-opt:int:list", "This is an integer list option.",
int_vars, MAX_VARS, &n_int_vars, 2,
/* print */TRUE, /* default format */NULL);
opt_reg_uint(odb, "-opt:uint", "This is an unsigned integer option.",
&uint_var, 3, /* print */TRUE, /* default format */NULL);
opt_reg_uint_list(odb, "-opt:uint:list",
"This is an unsigned integer list option.",
uint_vars, MAX_VARS, &n_uint_vars, 4,
/* print */TRUE, /* default format */NULL);
opt_reg_float(odb, "-opt:float", "This is a float option.",
&float_var, 5.0, /* print */TRUE, /* default format */NULL);
opt_reg_float_list(odb, "-opt:float:list", "This is a float list option.",
float_vars, MAX_VARS, &n_float_vars, 6.0,
/* print */TRUE, /* default format */NULL);
opt_reg_double(odb, "-opt:double", "This is a double option.",
&double_var, 7.0, /* print */TRUE, /* default format */NULL);
opt_reg_double_list(odb, "-opt:double:list", "This is a double list option.",
double_vars, MAX_VARS, &n_double_vars, 8.0,
/* print */TRUE, /* default format */NULL);
opt_reg_enum(odb, "-opt:enum", "This is an enumeration option.",
&enum_var, "enum_a", enum_emap, /* index map */NULL, enum_NUM,
/* print */TRUE, /* default format */NULL);
opt_reg_enum_list(odb, "-opt:enum:list", "This is a enum list option.",
enum_vars, MAX_VARS, &n_enum_vars, "enum_b",
enum_emap, /* index map */NULL, enum_NUM,
/* print */TRUE, /* default format */NULL);
opt_reg_enum(odb, "-opt:enum:eval", "This is an enumeration option w/eval.",
&enum_eval_var, "enum_b", enum_emap, enum_eval, enum_NUM,
/* print */TRUE, /* default format */NULL);
opt_reg_enum_list(odb, "-opt:enum:eval:list",
"This is a enum list option w/eval.",
enum_eval_vars, MAX_VARS, &n_enum_eval_vars, "enum_a",
enum_emap, enum_eval, enum_NUM,
/* print */TRUE, /* default format */NULL);
opt_reg_flag(odb, "-opt:flag", "This is a boolean flag option.",
&flag_var, FALSE, /* print */TRUE, /* default format */NULL);
opt_reg_flag_list(odb, "-opt:flag:list",
"This is a boolean flag list option.",
flag_vars, MAX_VARS, &n_flag_vars, TRUE,
/* print */TRUE, /* default format */NULL);
opt_reg_string(odb, "-opt:string", "This is a string option.",
&string_var, "a:string",
/* print */TRUE, /* default format */NULL);
opt_reg_string_list(odb, "-opt:string:list", "This is a string list option.",
string_vars, MAX_VARS, &n_string_vars, "another:string",
/* print */TRUE, /* default format */NULL);
/* parse options */
opt_process_options(odb, argc, argv);
/* print options */
fprintf(stdout, "## Current Option Values ##\n");
opt_print_options(odb, stdout, /* long */FALSE, /* notes */TRUE);
/* all done */
opt_delete(odb);
exit(0);
}
#endif /* TEST */