Logo Search packages:      
Sourcecode: t1utils version File versions  Download package

clp.c

/* -*- 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. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <lcdf/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) * 5);
    if (!cli->argtype)
      goto failed;
    cli->nargtype = 5;
  
    for (i = 0; i < 256; i++)
      cli->option_class[i] = 0;
    cli->option_class[(int) '-'] = 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[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

Generated by  Doxygen 1.6.0   Back to index