/* -*- related-file-name: "../include/lcdf/clp.h" -*- */
/* clp.c - Complete source code for CLP.
* This file is part of CLP, the command line parser package.
*
* Copyright (c) 1997-2003 Eddie Kohler, kohler@icir.org
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file, which is available in full at
* http://www.pdos.lcs.mit.edu/click/license.html. The conditions include: you
* must preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding. */
#include <click/config.h>
#include <click/clp.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
/* By default, assume we have strtoul. */
#if !defined(HAVE_STRTOUL) && !defined(HAVE_CONFIG_H)
# define HAVE_STRTOUL 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Option types for Clp_SetOptionChar */
#define Clp_DoubledLong (Clp_LongImplicit * 2)
#define Clp_AnyArgument (Clp_Mandatory | Clp_Optional)
#define MAX_AMBIGUOUS_VALUES 4
typedef struct {
Clp_ArgParseFunc func;
int flags;
void *thunk;
} Clp_ArgType;
typedef struct {
int pos;
int neg;
} Clp_LongMinMatch;
struct Clp_Internal {
Clp_Option *opt;
Clp_LongMinMatch *long_min_match;
int nopt;
Clp_ArgType *argtype;
int nargtype;
const char * const *argv;
int argc;
unsigned char option_class[256];
int both_short_and_long;
char option_chars[3];
const char *text;
const char *program_name;
void (*error_handler)(const char *);
int is_short;
int whole_negated; /* true if negated by an option character */
int could_be_short;
int option_processing;
int ambiguous;
int ambiguous_values[MAX_AMBIGUOUS_VALUES];
Clp_Option *current_option;
int current_short;
int negated_by_no;
};
struct Clp_ParserState {
const char * const *argv;
int argc;
char option_chars[3];
const char *text;
int is_short;
int whole_negated;
};
typedef struct Clp_StringList {
Clp_Option *items;
Clp_LongMinMatch *long_min_match;
int nitems;
int allow_int;
int nitems_invalid_report;
} Clp_StringList;
#define TEST(o, f) (((o)->flags & (f)) != 0)
static int calculate_long_min_match(int, Clp_Option *, int, int, int);
static int parse_string(Clp_Parser *, const char *, int, void *);
static int parse_int(Clp_Parser *, const char *, int, void *);
static int parse_bool(Clp_Parser *, const char *, int, void *);
static int parse_double(Clp_Parser *, const char *, int, void *);
static int parse_string_list(Clp_Parser *, const char *, int, void *);
static int ambiguity_error(Clp_Parser *, int, int *, Clp_Option *,
const char *, const char *, ...);
/*******
* Clp_NewParser, etc.
**/
static void
check_duplicated_short_options(Clp_Parser *clp, int nopt, Clp_Option *opt, int negated)
{
int i;
int check[256];
for (i = 0; i < 256; i++)
check[i] = -1;
for (i = 0; i < nopt; i++)
if (opt[i].short_name > 0 && opt[i].short_name < 256
&& (negated ? TEST(&opt[i], Clp_Negate)
: !TEST(&opt[i], Clp_OnlyNegated))) {
int sh = opt[i].short_name;
if (check[sh] >= 0 && check[sh] != opt[i].option_id)
Clp_OptionError(clp, "CLP internal error: more than 1 option has short name '%c'", sh);
check[sh] = opt[i].option_id;
}
}
Clp_Parser *
Clp_NewParser(int argc, const char * const *argv, int nopt, Clp_Option *opt)
/* Creates and returns a new Clp_Parser using the options in 'opt',
or 0 on memory allocation failure */
{
int i;
Clp_Parser *clp = (Clp_Parser *)malloc(sizeof(Clp_Parser));
Clp_Internal *cli = (Clp_Internal *)malloc(sizeof(Clp_Internal));
Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nopt);
if (!clp || !cli || !lmm)
goto failed;
clp->internal = cli;
cli->long_min_match = lmm;
/* Assign arguments (need to do this now so we can call Clp_OptionError) */
cli->argc = argc;
cli->argv = argv;
{
const char *slash = strrchr(argv[0], '/');
cli->program_name = slash ? slash + 1 : argv[0];
}
cli->error_handler = 0;
/* Get rid of negative option_ids, which are internal to CLP */
for (i = 0; i < nopt; i++)
if (opt[i].option_id < 0) {
Clp_OptionError(clp, "CLP internal error: option %d has negative option_id", i);
opt[i] = opt[nopt - 1];
nopt--;
i--;
}
/* Massage the options to make them usable */
for (i = 0; i < nopt; i++) {
/* Enforce invariants */
if (opt[i].arg_type <= 0)
opt[i].flags &= ~Clp_AnyArgument;
if (opt[i].arg_type > 0 && !TEST(&opt[i], Clp_Optional))
opt[i].flags |= Clp_Mandatory;
/* Nonexistent short options have character 256. We know this won't
equal any character in an argument, even if characters are signed */
if (opt[i].short_name <= 0 || opt[i].short_name > 255)
opt[i].short_name = 256;
/* Options that start with 'no-' should be changed to OnlyNegated */
if (opt[i].long_name && strncmp(opt[i].long_name, "no-", 3) == 0) {
opt[i].long_name += 3;
opt[i].flags |= Clp_Negate | Clp_OnlyNegated;
}
}
/* Check for duplicated short options */
check_duplicated_short_options(clp, nopt, opt, 0);
check_duplicated_short_options(clp, nopt, opt, 1);
/* Calculate long options' minimum unambiguous length */
for (i = 0; i < nopt; i++)
if (opt[i].long_name && !TEST(&opt[i], Clp_OnlyNegated))
lmm[i].pos = calculate_long_min_match
(nopt, opt, i, Clp_OnlyNegated, 0);
for (i = 0; i < nopt; i++)
if (opt[i].long_name && TEST(&opt[i], Clp_Negate))
lmm[i].neg = calculate_long_min_match
(nopt, opt, i, Clp_Negate, Clp_Negate);
/* Set up clp->internal */
cli->opt = opt;
cli->nopt = nopt;
cli->argtype = (Clp_ArgType *)malloc(sizeof(Clp_ArgType) * 8);
if (!cli->argtype)
goto failed;
for (i = 0; i < 8; i++)
cli->argtype[i].func = 0;
cli->nargtype = 8;
for (i = 0; i < 256; i++)
cli->option_class[i] = 0;
cli->option_class[(unsigned char) '-'] = Clp_Short;
cli->both_short_and_long = 0;
cli->is_short = 0;
cli->whole_negated = 0;
cli->option_processing = 1;
cli->current_option = 0;
/* Add default type parsers */
Clp_AddType(clp, Clp_ArgString, 0, parse_string, 0);
Clp_AddType(clp, Clp_ArgStringNotOption, Clp_DisallowOptions, parse_string, 0);
Clp_AddType(clp, Clp_ArgInt, 0, parse_int, 0);
Clp_AddType(clp, Clp_ArgUnsigned, 0, parse_int, (void *)cli);
Clp_AddType(clp, Clp_ArgBool, 0, parse_bool, 0);
Clp_AddType(clp, Clp_ArgDouble, 0, parse_double, 0);
return clp;
failed:
if (cli && cli->argtype)
free(cli->argtype);
if (cli)
free(cli);
if (clp)
free(clp);
if (lmm)
free(lmm);
return 0;
}
void
Clp_DeleteParser(Clp_Parser *clp)
/* Destroys the Clp_Parser 'clp' and any associated memory allocated by
CLP */
{
int i;
Clp_Internal *cli;
if (!clp)
return;
cli = clp->internal;
/* get rid of any string list types */
for (i = 0; i < cli->nargtype; i++)
if (cli->argtype[i].func == parse_string_list) {
Clp_StringList *clsl = (Clp_StringList *)cli->argtype[i].thunk;
free(clsl->items);
free(clsl->long_min_match);
free(clsl);
}
free(cli->argtype);
free(cli->long_min_match);
free(cli);
free(clp);
}
int
Clp_SetOptionProcessing(Clp_Parser *clp, int option_processing)
/* Sets whether command line arguments are parsed (looking for options)
at all. Each parser starts out with OptionProcessing true. Returns old
value. */
{
Clp_Internal *cli = clp->internal;
int old = cli->option_processing;
cli->option_processing = option_processing;
return old;
}
Clp_ErrorHandler
Clp_SetErrorHandler(Clp_Parser *clp, void (*error_handler)(const char *))
/* Sets a hook function to be called before Clp_OptionError
prints anything. 0 means nothing will be called. */
{
Clp_Internal *cli = clp->internal;
Clp_ErrorHandler old = cli->error_handler;
cli->error_handler = error_handler;
return old;
}
int
Clp_SetOptionChar(Clp_Parser *clp, int c, int option_type)
/* Determines how clp will deal with short options.
option_type must be a sum of:
0 == Clp_NotOption 'c' isn't an option.
Clp_Short 'c' introduces a list of short options.
Clp_Long 'c' introduces a long option.
Clp_ShortNegated 'c' introduces a negated list of
short options.
Clp_LongNegated 'c' introduces a negated long option.
Clp_LongImplicit 'c' introduces a long option, and *is part
of the long option itself*.
Some values are not allowed (Clp_Long | Clp_LongNegated isn't allowed,
for instance). c=0 means ALL characters are that type. Returns 0 on
failure (you gave an illegal option_type), 1 on success. */
{
int i;
Clp_Internal *cli = clp->internal;
if (option_type < 0
|| option_type >= 2*Clp_LongImplicit
|| ((option_type & Clp_Short) && (option_type & Clp_ShortNegated))
|| ((option_type & Clp_Long) && (option_type & Clp_LongNegated))
|| ((option_type & Clp_LongImplicit) && (option_type & (Clp_Short | Clp_ShortNegated | Clp_Long | Clp_LongNegated))))
return 0;
if (c == 0)
for (i = 1; i < 256; i++)
cli->option_class[i] = option_type;
else
cli->option_class[(unsigned char) c] = option_type;
/* If an option character can introduce either short or long options, then
we need to fix up the long_min_match values. We may have set the
long_min_match for option 'abcde' to 1, if no other option starts
with 'a'. But if '-' can introduce either a short option or a long
option, AND a short option '-a' exists, then the long_min_match for
'abcde' must be set to 2! */
if (!cli->both_short_and_long) {
int either_short = option_type & (Clp_Short | Clp_ShortNegated);
int either_long = option_type & (Clp_Long | Clp_LongNegated);
if (either_short && either_long) {
unsigned char have_short[257];
for (i = 0; i < 256; i++)
have_short[i] = 0;
for (i = 0; i < cli->nopt; i++)
have_short[cli->opt[i].short_name] = 1;
for (i = 0; i < cli->nopt; i++)
if (cli->opt[i].long_name && cli->long_min_match[i].pos == 1) {
/* if '--Cxxxx's short name is '-C', keep long_min_match
== 1 */
unsigned char first = (unsigned char)cli->opt[i].long_name[0];
if (have_short[first] && first != cli->opt[i].short_name)
cli->long_min_match[i].pos++;
}
cli->both_short_and_long = 1;
}
}
return 1;
}
/*******
* functions for Clp_Option lists
**/
static int
min_different_chars(const char *s, const char *t)
/* Returns the minimum number of characters required to distinguish
s from t.
If s is shorter than t, returns strlen(s). */
{
const char *sfirst = s;
while (*s && *t && *s == *t)
s++, t++;
if (!*s)
return s - sfirst;
else
return s - sfirst + 1;
}
static int
calculate_long_min_match(int nopt, Clp_Option *opt, int which,
int flags, int flags_value)
{
int j, lmm = 1;
int preferred = (opt[which].flags & Clp_PreferredMatch);
for (j = 0; j < nopt; j++)
if (opt[j].long_name
&& (opt[j].flags & flags) == flags_value
&& opt[which].option_id != opt[j].option_id
&& strncmp(opt[which].long_name, opt[j].long_name, lmm) == 0
&& (!preferred || strncmp(opt[which].long_name, opt[j].long_name, strlen(opt[which].long_name)) != 0))
lmm = min_different_chars(opt[which].long_name, opt[j].long_name);
return lmm;
}
/* the ever-glorious argcmp */
static int
argcmp(const char *ref, const char *arg, int min_match, int fewer_dashes)
/* Returns 0 if ref and arg don't match.
Returns -1 if ref and arg match, but fewer than min_match characters.
Returns len if ref and arg match min_match or more characters;
len is the number of characters that matched in arg.
Allows arg to contain fewer dashes than ref iff fewer_dashes != 0.
Examples:
argcmp("x", "y", 1, 0) --> 0 / just plain wrong
argcmp("a", "ax", 1, 0) --> 0 / ...even though min_match == 1
and the 1st chars match
argcmp("box", "bo", 3, 0) --> -1 / ambiguous
argcmp("cat", "c=3", 1, 0) --> 1 / handles = arguments
*/
{
const char *refstart = ref;
const char *argstart = arg;
assert(min_match > 0);
compare:
while (*ref && *arg && *arg != '=' && *ref == *arg)
ref++, arg++;
/* Allow arg to contain fewer dashes than ref */
if (fewer_dashes && *ref == '-' && ref[1] && ref[1] == *arg) {
ref++;
goto compare;
}
if (*arg && *arg != '=')
return 0;
else if (ref - refstart < min_match)
return -1;
else
return arg - argstart;
}
static int
find_prefix_opt(const char *arg, int nopt, Clp_Option *opt,
Clp_LongMinMatch *lmmvec,
int *ambiguous, int *ambiguous_values, int negated)
/* Looks for an unambiguous match of 'arg' against one of the long
options in 'opt'. Returns positive if it finds one; otherwise, returns
-1 and possibly changes 'ambiguous' and 'ambiguous_values' to keep
track of at most MAX_AMBIGUOUS_VALUES possibilities. */
{
int i, fewer_dashes = 0, first_ambiguous = *ambiguous;
retry:
for (i = 0; i < nopt; i++) {
int len, lmm;
if (!opt[i].long_name
|| (negated && !TEST(&opt[i], Clp_Negate))
|| (!negated && TEST(&opt[i], Clp_OnlyNegated)))
continue;
lmm = (negated ? lmmvec[i].neg : lmmvec[i].pos);
len = argcmp(opt[i].long_name, arg, lmm, fewer_dashes);
if (len > 0)
return i;
else if (len < 0) {
if (*ambiguous < MAX_AMBIGUOUS_VALUES)
ambiguous_values[*ambiguous] = i;
(*ambiguous)++;
}
}
/* If there were no partial matches, try again with fewer_dashes true */
if (*ambiguous == first_ambiguous && !fewer_dashes) {
fewer_dashes = 1;
goto retry;
}
return -1;
}
/*****
* Argument parsing
**/
int
Clp_AddType(Clp_Parser *clp, int type_id, int flags,
Clp_ArgParseFunc func, void *thunk)
/* Add a argument parser for type_id to the Clp_Parser. When an argument
arg for Clp_Option opt is being processed, the parser routines will
call (*func)(clp, opt, arg, thunk, complain)
where complain is 1 if the routine should report errors, or 0 if it
should fail silently.
Returns 1 on success, 0 if memory allocation fails or the arguments
are bad. */
{
int i;
Clp_Internal *cli = clp->internal;
Clp_ArgType *new_argtype;
int nargtype = cli->nargtype;
assert(nargtype);
if (type_id <= 0 || !func)
return 0;
while (nargtype <= type_id)
nargtype *= 2;
new_argtype = (Clp_ArgType *)
realloc(cli->argtype, sizeof(Clp_ArgType) * nargtype);
if (!new_argtype)
return 0;
cli->argtype = new_argtype;
for (i = cli->nargtype; i < nargtype; i++)
cli->argtype[i].func = 0;
cli->nargtype = nargtype;
cli->argtype[type_id].func = func;
cli->argtype[type_id].flags = flags;
cli->argtype[type_id].thunk = thunk;
return 1;
}
/*******
* Default argument parsers
**/
static int
parse_string(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
(void)complain, (void)thunk;
clp->val.s = arg;
return 1;
}
static int
parse_int(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
char *val;
int base = 10;
if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
base = 16;
arg += 2;
}
if (thunk != 0) { /* unsigned */
#if HAVE_STRTOUL
clp->val.u = strtoul(arg, &val, base);
#else
/* don't bother really trying to do it right */
if (arg[0] == '-')
val = (char *) arg;
else
clp->val.u = strtol(arg, &val, base);
#endif
} else
clp->val.i = strtol(arg, &val, base);
if (*arg != 0 && *val == 0)
return 1;
else if (complain) {
const char *message = thunk != 0
? "'%O' expects a nonnegative integer, not '%s'"
: "'%O' expects an integer, not '%s'";
if (base == 16) arg -= 2;
return Clp_OptionError(clp, message, arg);
} else
return 0;
}
static int
parse_double(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
char *val;
(void)thunk;
clp->val.d = strtod(arg, &val);
if (*arg != 0 && *val == 0)
return 1;
else if (complain)
return Clp_OptionError(clp, "'%O' expects a real number, not '%s'", arg);
else
return 0;
}
static int
parse_bool(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
int i;
char lcarg[6];
(void)thunk;
if (strlen(arg) > 5 || strchr(arg, '=') != 0)
goto error;
for (i = 0; arg[i] != 0; i++)
lcarg[i] = tolower(arg[i]);
lcarg[i] = 0;
if (argcmp("yes", lcarg, 1, 0) > 0
|| argcmp("true", lcarg, 1, 0) > 0
|| argcmp("1", lcarg, 1, 0) > 0) {
clp->val.i = 1;
return 1;
} else if (argcmp("no", lcarg, 1, 0) > 0
|| argcmp("false", lcarg, 1, 0) > 0
|| argcmp("1", lcarg, 1, 0) > 0) {
clp->val.i = 0;
return 1;
}
error:
if (complain)
Clp_OptionError(clp, "'%O' expects a true-or-false value, not '%s'", arg);
return 0;
}
/*****
* Clp_AddStringListType
**/
static int
parse_string_list(Clp_Parser *clp, const char *arg, int complain, void *thunk)
{
Clp_StringList *sl = (Clp_StringList *)thunk;
int idx, ambiguous = 0;
int ambiguous_values[MAX_AMBIGUOUS_VALUES + 1];
/* actually look for a string value */
idx = find_prefix_opt
(arg, sl->nitems, sl->items, sl->long_min_match,
&ambiguous, ambiguous_values, 0);
if (idx >= 0) {
clp->val.i = sl->items[idx].option_id;
return 1;
}
if (sl->allow_int) {
if (parse_int(clp, arg, 0, 0))
return 1;
}
if (complain) {
const char *complaint = (ambiguous ? "an ambiguous" : "not a valid");
if (!ambiguous) {
ambiguous = sl->nitems_invalid_report;
for (idx = 0; idx < ambiguous; idx++)
ambiguous_values[idx] = idx;
}
return ambiguity_error
(clp, ambiguous, ambiguous_values, sl->items, "",
"'%s' is %s argument to '%O'", arg, complaint);
} else
return 0;
}
int
finish_string_list(Clp_Parser *clp, int type_id, int flags,
Clp_Option *items, int nitems, int itemscap)
{
int i;
Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
Clp_LongMinMatch *lmm = (Clp_LongMinMatch *)malloc(sizeof(Clp_LongMinMatch) * nitems);
if (!clsl || !lmm)
goto error;
clsl->items = items;
clsl->long_min_match = lmm;
clsl->nitems = nitems;
clsl->allow_int = (flags & Clp_AllowNumbers) != 0;
if (nitems < MAX_AMBIGUOUS_VALUES && nitems < itemscap && clsl->allow_int) {
items[nitems].long_name = "any integer";
clsl->nitems_invalid_report = nitems + 1;
} else if (nitems > MAX_AMBIGUOUS_VALUES + 1)
clsl->nitems_invalid_report = MAX_AMBIGUOUS_VALUES + 1;
else
clsl->nitems_invalid_report = nitems;
for (i = 0; i < nitems; i++)
lmm[i].pos = calculate_long_min_match(nitems, items, i, 0, 0);
if (Clp_AddType(clp, type_id, 0, parse_string_list, clsl))
return 1;
error:
if (clsl)
free(clsl);
if (lmm)
free(lmm);
return 0;
}
int
Clp_AddStringListType(Clp_Parser *clp, int type_id, int flags, ...)
/* An easy way to add new types to clp.
Call like this:
Clp_AddStringListType
(clp, type_id, flags,
char *s_1, int value_1, ..., char *s_n, int value_n, 0);
Defines type_id as a type in clp.
This type accepts any of the strings s_1, ..., s_n
(or unambiguous abbreviations thereof);
if argument s_i is given, value_i is stored in clp->val.i.
If Clp_AllowNumbers is set in flags,
explicit integers are also allowed.
Returns 1 on success, 0 on memory allocation errors. */
{
int nitems = 0;
int itemscap = 5;
Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
va_list val;
va_start(val, flags);
if (!items)
goto error;
/* slurp up the arguments */
while (1) {
int value;
char *name = va_arg(val, char *);
if (!name)
break;
value = va_arg(val, int);
if (nitems >= itemscap) {
Clp_Option *new_items;
itemscap *= 2;
new_items = (Clp_Option *)realloc(items, sizeof(Clp_Option) * itemscap);
if (!new_items)
goto error;
items = new_items;
}
items[nitems].long_name = name;
items[nitems].option_id = value;
items[nitems].flags = 0;
nitems++;
}
va_end(val);
if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
return 1;
error:
va_end(val);
if (items)
free(items);
return 0;
}
int
Clp_AddStringListTypeVec(Clp_Parser *clp, int type_id, int flags,
int nitems, char **strings, int *values)
/* An alternate way to make a string list type. See Clp_AddStringListType
for the basics; this coalesces the strings and values into two arrays,
rather than spreading them out into a variable argument list. */
{
int i;
int itemscap = (nitems < 5 ? 5 : nitems);
Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
if (!items)
return 0;
/* copy over items */
for (i = 0; i < nitems; i++) {
items[i].long_name = strings[i];
items[i].option_id = values[i];
items[i].flags = 0;
}
if (finish_string_list(clp, type_id, flags, items, nitems, itemscap))
return 1;
else {
free(items);
return 0;
}
}
/*******
* Returning information
**/
const char *
Clp_ProgramName(Clp_Parser *clp)
{
return clp->internal->program_name;
}
/******
* Clp_ParserStates
**/
Clp_ParserState *
Clp_NewParserState(void)
{
return (Clp_ParserState *)malloc(sizeof(Clp_ParserState));
}
void
Clp_DeleteParserState(Clp_ParserState *save)
{
free(save);
}
void
Clp_SaveParser(Clp_Parser *clp, Clp_ParserState *save)
/* Saves parser position in save */
{
Clp_Internal *cli = clp->internal;
save->argv = cli->argv;
save->argc = cli->argc;
memcpy(save->option_chars, cli->option_chars, 3);
save->text = cli->text;
save->is_short = cli->is_short;
save->whole_negated = cli->whole_negated;
}
void
Clp_RestoreParser(Clp_Parser *clp, Clp_ParserState *save)
/* Restores parser position from save */
{
Clp_Internal *cli = clp->internal;
cli->argv = save->argv;
cli->argc = save->argc;
memcpy(cli->option_chars, save->option_chars, 3);
cli->text = save->text;
cli->is_short = save->is_short;
cli->whole_negated = save->whole_negated;
}
/*******
* Clp_Next and its helpers
**/
static void
set_option_text(Clp_Internal *cli, const char *text, int n_option_chars)
{
char *option_chars = cli->option_chars;
assert(n_option_chars < 3);
while (n_option_chars-- > 0)
*option_chars++ = *text++;
*option_chars = 0;
cli->text = text;
}
static int
next_argument(Clp_Parser *clp, int want_argument)
/* Moves clp to the next argument.
Returns 1 if it finds another option.
Returns 0 if there aren't any more arguments.
Returns 0, sets clp->have_arg = 1, and sets clp->arg to the argument
if the next argument isn't an option.
If want_argument > 0, it'll look for an argument.
want_argument == 1: Accept arguments that start with Clp_NotOption
or Clp_LongImplicit.
want_argument == 2: Accept ALL arguments.
Where is the option stored when this returns?
Well, cli->argv[0] holds the whole of the next command line argument.
cli->option_chars holds a string: what characters began the option?
It is generally "-" or "--".
cli->text holds the text of the option:
for short options, cli->text[0] is the relevant character;
for long options, cli->text holds the rest of the option. */
{
Clp_Internal *cli = clp->internal;
const char *text;
int option_class;
/* clear relevant flags */
clp->have_arg = 0;
clp->arg = 0;
cli->could_be_short = 0;
/* if we're in a string of short options, move up one char in the string */
if (cli->is_short) {
++cli->text;
if (cli->text[0] == 0)
cli->is_short = 0;
else if (want_argument > 0) {
/* handle -O[=]argument case */
clp->have_arg = 1;
if (cli->text[0] == '=')
clp->arg = cli->text + 1;
else
clp->arg = cli->text;
cli->is_short = 0;
return 0;
}
}
/* if in short options, we're all set */
if (cli->is_short)
return 1;
/** if not in short options, move to the next argument **/
cli->whole_negated = 0;
cli->text = 0;
if (cli->argc <= 1)
return 0;
cli->argc--;
cli->argv++;
text = cli->argv[0];
if (want_argument > 1)
goto not_option;
option_class = cli->option_class[ (unsigned char)text[0] ];
if (text[0] == '-' && text[1] == '-')
option_class = Clp_DoubledLong;
/* If this character could introduce either a short or a long option,
try a long option first, but remember that short's a possibility for
later. */
if ((option_class & (Clp_Short | Clp_ShortNegated))
&& (option_class & (Clp_Long | Clp_LongNegated))) {
option_class &= ~(Clp_Short | Clp_ShortNegated);
if (text[1] != 0)
cli->could_be_short = 1;
}
switch (option_class) {
case Clp_Short:
cli->is_short = 1;
goto check_singleton;
case Clp_ShortNegated:
cli->is_short = 1;
cli->whole_negated = 1;
goto check_singleton;
case Clp_Long:
goto check_singleton;
case Clp_LongNegated:
cli->whole_negated = 1;
goto check_singleton;
check_singleton:
/* For options introduced with one character, option-char,
'[option-char]' alone is NOT an option. */
if (text[1] == 0)
goto not_option;
set_option_text(cli, text, 1);
break;
case Clp_LongImplicit:
/* LongImplict: option_chars == "" (since all chars are part of the
option); restore head -> text of option */
if (want_argument > 0)
goto not_option;
set_option_text(cli, text, 0);
break;
case Clp_DoubledLong:
set_option_text(cli, text, 2);
break;
not_option:
case Clp_NotOption:
cli->is_short = 0;
clp->have_arg = 1;
clp->arg = text;
return 0;
default:
assert(0 /* CLP misconfiguration: bad option type */);
}
return 1;
}
static void
switch_to_short_argument(Clp_Parser *clp)
{
Clp_Internal *cli = clp->internal;
const char *text = cli->argv[0];
int option_class = cli->option_class[ (unsigned char)text[0] ];
cli->is_short = 1;
cli->whole_negated = (option_class & Clp_ShortNegated ? 1 : 0);
set_option_text(cli, cli->argv[0], 1);
assert(cli->could_be_short);
}
static Clp_Option *
find_long(Clp_Parser *clp, const char *arg)
/* If arg corresponds to one of clp's options, finds that option &
returns it. If any argument is given after an = sign in arg, sets
clp->have_arg = 1 and clp->arg to that argument. Sets cli->ambiguous
to 1 iff there was no match because the argument was ambiguous. */
{
Clp_Internal *cli = clp->internal;
int value, len, min_match;
Clp_Option *opt = cli->opt;
int first_negative_ambiguous;
/* Look for a normal option. */
value = find_prefix_opt
(arg, cli->nopt, opt, cli->long_min_match,
&cli->ambiguous, cli->ambiguous_values, clp->negated);
if (value >= 0)
goto worked;
/* If we can't find it, look for a negated option. */
/* I know this is silly, but it makes me happy to accept
--no-no-option as a double negative synonym for --option. :) */
first_negative_ambiguous = cli->ambiguous;
while (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-') {
arg += 3;
clp->negated = !clp->negated;
value = find_prefix_opt
(arg, cli->nopt, opt, cli->long_min_match,
&cli->ambiguous, cli->ambiguous_values, clp->negated);
if (value >= 0)
goto worked;
}
/* No valid option was found; return 0. Mark the ambiguous values found
through '--no' by making them negative. */
{
int i, max = cli->ambiguous;
if (max > MAX_AMBIGUOUS_VALUES) max = MAX_AMBIGUOUS_VALUES;
for (i = first_negative_ambiguous; i < max; i++)
cli->ambiguous_values[i] = -cli->ambiguous_values[i] - 1;
}
return 0;
worked:
min_match = (clp->negated ? cli->long_min_match[value].neg : cli->long_min_match[value].pos);
len = argcmp(opt[value].long_name, arg, min_match, 1); /* XXX 1? */
assert(len > 0);
if (arg[len] == '=') {
clp->have_arg = 1;
clp->arg = arg + len + 1;
}
return &opt[value];
}
static Clp_Option *
find_short(Clp_Parser *clp, int short_name)
/* If short_name corresponds to one of clp's options, returns it. */
{
Clp_Internal *cli = clp->internal;
Clp_Option *opt = cli->opt;
int i;
for (i = 0; i < cli->nopt; i++)
if (opt[i].short_name == short_name
&& (clp->negated
? TEST(&opt[i], Clp_Negate)
: !TEST(&opt[i], Clp_OnlyNegated)))
return &opt[i];
return 0;
}
int
Clp_Next(Clp_Parser *clp)
/* Gets and parses the next argument from the argument list.
If there are no more arguments, returns Clp_Done.
If the next argument isn't an option, returns Clp_NotOption;
the argument is stored in clp->arg.
If the next argument is an option, returns that option's option_id.
If the next argument is an unrecognizable or ambiguous option,
an error message is given and Clp_BadOption is returned.
If an option has an argument, that argument is stored in clp->arg
and clp->have_arg is set to 1.
Furthermore, that argument's parsed value (according to its type)
is stored in the clp->val union.
If an option needs an argument but isn't given one;
if it doesn't need an argument but IS given one;
or if the argument is the wrong type,
an error message is given and Clp_BadOption is returned. */
{
Clp_Internal *cli = clp->internal;
Clp_Option *opt;
Clp_ParserState clpsave;
int complain;
/** Set up clp **/
cli->current_option = 0;
cli->ambiguous = 0;
/** Get the next argument or option **/
if (!next_argument(clp, cli->option_processing ? 0 : 2))
return clp->have_arg ? Clp_NotOption : Clp_Done;
clp->negated = cli->whole_negated;
if (cli->is_short)
opt = find_short(clp, cli->text[0]);
else
opt = find_long(clp, cli->text);
/** If there's ambiguity between long & short options, and we couldn't
find a long option, look for a short option **/
if (!opt && cli->could_be_short) {
switch_to_short_argument(clp);
opt = find_short(clp, cli->text[0]);
}
/** If we didn't find an option... **/
if (!opt || (clp->negated && !TEST(opt, Clp_Negate))) {
/* default processing for the "--" option: turn off option processing
and return the next argument */
if (strcmp(cli->argv[0], "--") == 0) {
Clp_SetOptionProcessing(clp, 0);
return Clp_Next(clp);
}
/* otherwise, report some error or other */
if (cli->ambiguous)
ambiguity_error(clp, cli->ambiguous, cli->ambiguous_values,
cli->opt, cli->option_chars,
"option '%s%s' is ambiguous",
cli->option_chars, cli->text);
else if (cli->is_short && !cli->could_be_short)
Clp_OptionError(clp, "unrecognized option '%s%c'",
cli->option_chars, cli->text[0]);
else
Clp_OptionError(clp, "unrecognized option '%s%s'",
cli->option_chars, cli->text);
return Clp_BadOption;
}
/** Set the current option **/
cli->current_option = opt;
cli->current_short = cli->is_short;
cli->negated_by_no = clp->negated && !cli->whole_negated;
/** The no-argument (or should-have-no-argument) case **/
if (clp->negated || !TEST(opt, Clp_AnyArgument)) {
if (clp->have_arg) {
Clp_OptionError(clp, "'%O' can't take an argument");
return Clp_BadOption;
} else
return opt->option_id;
}
/** Get an argument if we need one, or if it's optional **/
/* Sanity-check the argument type. */
if (opt->arg_type <= 0 || opt->arg_type >= cli->nargtype
|| cli->argtype[ opt->arg_type ].func == 0)
return Clp_Error;
/* complain == 1 only if the argument was explicitly given,
or it is mandatory. */
complain = (clp->have_arg != 0) || TEST(opt, Clp_Mandatory);
Clp_SaveParser(clp, &clpsave);
if (TEST(opt, Clp_Mandatory) && !clp->have_arg) {
/* Mandatory argument case */
/* Allow arguments to options to start with a dash, but only if the
argument type allows it by not setting Clp_DisallowOptions */
int disallow = TEST(&cli->argtype[opt->arg_type], Clp_DisallowOptions);
next_argument(clp, disallow ? 1 : 2);
if (!clp->have_arg) {
int got_option = cli->text != 0;
Clp_RestoreParser(clp, &clpsave);
if (got_option)
Clp_OptionError(clp, "'%O' requires a non-option argument");
else
Clp_OptionError(clp, "'%O' requires an argument");
return Clp_BadOption;
}
} else if (cli->is_short && !clp->have_arg && cli->text[1] != 0)
/* The -[option]argument case:
Assume that the rest of the current string is the argument. */
next_argument(clp, 1);
/** Parse the argument **/
if (clp->have_arg) {
Clp_ArgType *atr = &cli->argtype[ opt->arg_type ];
if (atr->func(clp, clp->arg, complain, atr->thunk) <= 0) {
/* parser failed */
clp->have_arg = 0;
if (TEST(opt, Clp_Mandatory))
return Clp_BadOption;
else
Clp_RestoreParser(clp, &clpsave);
}
}
return opt->option_id;
}
const char *
Clp_Shift(Clp_Parser *clp, int allow_dashes)
/* Returns the next argument from the argument list without parsing it.
If there are no more arguments, returns 0. */
{
Clp_ParserState clpsave;
Clp_SaveParser(clp, &clpsave);
next_argument(clp, allow_dashes ? 2 : 1);
if (!clp->have_arg)
Clp_RestoreParser(clp, &clpsave);
return clp->arg;
}
/*******
* Clp_OptionError
**/
typedef struct Clp_BuildString {
char *text;
char *pos;
int capacity;
int bad;
} Clp_BuildString;
static Clp_BuildString *
new_build_string(void)
{
Clp_BuildString *bs = (Clp_BuildString *)malloc(sizeof(Clp_BuildString));
if (!bs) goto bad;
bs->text = (char *)malloc(256);
if (!bs->text) goto bad;
bs->pos = bs->text;
bs->capacity = 256;
bs->bad = 0;
return bs;
bad:
if (bs) free(bs);
return 0;
}
static void
free_build_string(Clp_BuildString *bs)
{
if (bs) free(bs->text);
free(bs);
}
static int
grow_build_string(Clp_BuildString *bs, int want)
{
char *new_text;
int ipos = bs->pos - bs->text;
int new_capacity = bs->capacity;
while (want >= new_capacity)
new_capacity *= 2;
new_text = (char *)realloc(bs->text, new_capacity);
if (!new_text) {
bs->bad = 1;
return 0;
} else {
bs->text = new_text;
bs->pos = bs->text + ipos;
bs->capacity = new_capacity;
return 1;
}
}
#define ENSURE_BUILD_STRING(bs, space) \
((((bs)->pos - (bs)->text) + (space) >= (bs)->capacity) \
|| grow_build_string((bs), ((bs)->pos - (bs)->text) + (space)))
static void
append_build_string(Clp_BuildString *bs, const char *s, int l)
{
if (l < 0)
l = strlen(s);
if (ENSURE_BUILD_STRING(bs, l)) {
memcpy(bs->pos, s, l);
bs->pos += l;
}
}
static Clp_BuildString *
Clp_VaOptionError(Clp_Parser *clp, Clp_BuildString *bs,
const char *fmt, va_list val)
/* Reports an error for parser clp. Allowable % format characters are:
s Print a string from the argument list.
c Print an int from the argument list as a character.
d Print an int from the argument list.
O Print the name of the current option;
take nothing from the argument list.
No field specifications or flags are allowed. Always returns 0. */
{
Clp_Internal *cli = clp->internal;
const char *percent;
if (!bs)
bs = new_build_string();
if (!bs)
return 0;
append_build_string(bs, cli->program_name, -1);
append_build_string(bs, ": ", 2);
for (percent = strchr(fmt, '%'); percent; percent = strchr(fmt, '%')) {
append_build_string(bs, fmt, percent - fmt);
switch (*++percent) {
case 's': {
char *s = va_arg(val, char *);
if (s) append_build_string(bs, s, -1);
else append_build_string(bs, "(null)", 6);
break;
}
case 'c': {
int c = va_arg(val, int);
if (ENSURE_BUILD_STRING(bs, 4)) {
if (c >= 32 && c <= 126)
*bs->pos++ = c;
else if (c < 32) {
*bs->pos++ = '^';
*bs->pos++ = c + 64;
} else {
sprintf(bs->pos, "\\%03o", c);
bs->pos += 4;
}
}
break;
}
case 'd': {
int d = va_arg(val, int);
if (ENSURE_BUILD_STRING(bs, 32)) {
sprintf(bs->pos, "%d", d);
bs->pos = strchr(bs->pos, 0);
}
break;
}
case 'O': {
Clp_Option *opt = cli->current_option;
if (!opt)
append_build_string(bs, "(no current option!)", -1);
else if (cli->current_short) {
append_build_string(bs, cli->option_chars, -1);
if (ENSURE_BUILD_STRING(bs, 1))
*bs->pos++ = opt->short_name;
} else if (cli->negated_by_no) {
append_build_string(bs, cli->option_chars, -1);
append_build_string(bs, "no-", 3);
append_build_string(bs, opt->long_name, -1);
} else {
append_build_string(bs, cli->option_chars, -1);
append_build_string(bs, opt->long_name, -1);
}
break;
}
case '%':
if (ENSURE_BUILD_STRING(bs, 1))
*bs->pos++ = '%';
break;
default:
if (ENSURE_BUILD_STRING(bs, 2)) {
*bs->pos++ = '%';
*bs->pos++ = *percent;
}
break;
}
fmt = ++percent;
}
append_build_string(bs, fmt, -1);
append_build_string(bs, "\n", 1);
return bs;
}
static void
do_error(Clp_Parser *clp, Clp_BuildString *bs)
{
const char *text;
if (bs && !bs->bad) {
*bs->pos = 0;
text = bs->text;
} else
text = "out of memory\n";
if (clp->internal->error_handler != 0)
(*clp->internal->error_handler)(text);
else
fputs(text, stderr);
}
int
Clp_OptionError(Clp_Parser *clp, const char *fmt, ...)
{
Clp_BuildString *bs;
va_list val;
va_start(val, fmt);
bs = Clp_VaOptionError(clp, 0, fmt, val);
va_end(val);
do_error(clp, bs);
free_build_string(bs);
return 0;
}
static int
ambiguity_error(Clp_Parser *clp, int ambiguous, int *ambiguous_values,
Clp_Option *opt, const char *prefix,
const char *fmt, ...)
{
Clp_BuildString *bs;
int i;
va_list val;
va_start(val, fmt);
bs = Clp_VaOptionError(clp, 0, fmt, val);
if (!bs) goto done;
append_build_string(bs, clp->internal->program_name, -1);
append_build_string(bs, ": (Possibilities are", -1);
for (i = 0; i < ambiguous && i < MAX_AMBIGUOUS_VALUES; i++) {
int value = ambiguous_values[i];
const char *no_dash = "";
if (value < 0)
value = -(value + 1), no_dash = "no-";
if (i == 0)
append_build_string(bs, " ", 1);
else if (i == ambiguous - 1)
append_build_string(bs, (i == 1 ? " and " : ", and "), -1);
else
append_build_string(bs, ", ", 2);
append_build_string(bs, prefix, -1);
append_build_string(bs, no_dash, -1);
append_build_string(bs, opt[value].long_name, -1);
}
if (ambiguous > MAX_AMBIGUOUS_VALUES)
append_build_string(bs, ", and others", -1);
append_build_string(bs, ".)\n", -1);
va_end(val);
done:
do_error(clp, bs);
free_build_string(bs);
return 0;
}
static int
copy_string(char *buf, int buflen, int bufpos, const char *what)
{
int l = strlen(what);
if (l > buflen - bufpos - 1)
l = buflen - bufpos - 1;
memcpy(buf + bufpos, what, l);
return l;
}
int
Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int buflen)
{
Clp_Internal *cli = clp->internal;
Clp_Option *opt = cli->current_option;
int pos = 0;
if (!opt)
pos += copy_string(buf, buflen, pos, "(no current option!)");
else if (cli->current_short) {
pos += copy_string(buf, buflen, pos, cli->option_chars);
if (pos < buflen - 1)
buf[pos++] = opt->short_name;
} else if (cli->negated_by_no) {
pos += copy_string(buf, buflen, pos, cli->option_chars);
pos += copy_string(buf, buflen, pos, "no-");
pos += copy_string(buf, buflen, pos, opt->long_name);
} else {
pos += copy_string(buf, buflen, pos, cli->option_chars);
pos += copy_string(buf, buflen, pos, opt->long_name);
}
buf[pos] = 0;
return pos;
}
const char *
Clp_CurOptionName(Clp_Parser *clp)
{
static char buf[256];
Clp_CurOptionNameBuf(clp, buf, 256);
return buf;
}
#ifdef __cplusplus
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1