/* -*- 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-2023 Eddie Kohler, ekohler@gmail.com
 *
 * CLP is free software. It is distributed under the GNU General Public
 * License, Version 2, or, alternatively and at your discretion, under the
 * more permissive (BSD-like) Click LICENSE file as described below.
 *
 * 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://github.com/kohler/click/blob/master/LICENSE. 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 binding. */

#if 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>
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_INTTYPES_H || !defined(HAVE_CONFIG_H)
# include <inttypes.h>
#endif

/* By default, assume we have inttypes.h, strtoul, and uintptr_t. */
#if !defined(HAVE_STRTOUL) && !defined(HAVE_CONFIG_H)
# define HAVE_STRTOUL 1
#endif
#if defined(HAVE_INTTYPES_H) || !defined(HAVE_CONFIG_H)
# include <inttypes.h>
#endif
#if !defined(HAVE_UINTPTR_T) && defined(HAVE_CONFIG_H)
typedef unsigned long uintptr_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif


/** @file clp.h
 * @brief Functions for parsing command line options.
 *
 * The CLP functions are used to parse command line arguments into options.
 * It automatically handles value parsing, error messages, long options with
 * minimum prefix matching, short options, and negated options.
 *
 * The CLP model works like this.
 *
 * <ol>
 * <li>The user declares an array of Clp_Option structures that define the
 * options their program accepts.</li>
 * <li>The user creates a Clp_Parser object using Clp_NewParser(), passing in
 * the command line arguments to parse and the Clp_Option structures.</li>
 * <li>A loop repeatedly calls Clp_Next() to parse the arguments.</li>
 * </ol>
 *
 * Unlike many command line parsing libraries, CLP steps through all arguments
 * one at a time, rather than slurping up all options at once.  This makes it
 * meaningful to give an option more than once.
 *
 * Here's an example.
 *
 * @code
 * #define ANIMAL_OPT 1
 * #define VEGETABLE_OPT 2
 * #define MINERALS_OPT 3
 * #define USAGE_OPT 4
 *
 * static const Clp_Option options[] = {
 *     { "animal", 'a', ANIMAL_OPT, Clp_ValString, 0 },
 *     { "vegetable", 'v', VEGETABLE_OPT, Clp_ValString, Clp_Negate | Clp_Optional },
 *     { "minerals", 'm', MINERALS_OPT, Clp_ValInt, 0 },
 *     { "usage", 0, USAGE_OPT, 0, 0 }
 * };
 *
 * int main(int argc, char *argv[]) {
 *     Clp_Parser *clp = Clp_NewParser(argc, argv,
 *               sizeof(options) / sizeof(options[0]), options);
 *     int opt;
 *     while ((opt = Clp_Next(clp)) != Clp_Done)
 *         switch (opt) {
 *         case ANIMAL_OPT:
 *             fprintf(stderr, "animal is %s\n", clp->val.s);
 *             break;
 *         case VEGETABLE_OPT:
 *             if (clp->negated)
 *                 fprintf(stderr, "no vegetables!\n");
 *             else if (clp->have_val)
 *                 fprintf(stderr, "vegetable is %s\n", clp->val.s);
 *             else
 *                 fprintf(stderr, "vegetables OK\n");
 *             break;
 *         case MINERALS_OPT:
 *             fprintf(stderr, "%d minerals\n", clp->val.i);
 *             break;
 *         case USAGE_OPT:
 *             fprintf(stderr, "Usage: 20q [--animal=ANIMAL] [--vegetable[=VEGETABLE]] [--minerals=N]\n");
 *             break;
 *         case Clp_NotOption:
 *             fprintf(stderr, "non-option %s\n", clp->vstr);
 *             break;
 *         }
 *     }
 * }
 * @endcode
 *
 * Here are a couple of executions.
 *
 * <pre>
 * % ./20q --animal=cat
 * animal is cat
 * % ./20q --animal=cat -a dog -afish --animal bird --an=snake
 * animal is cat
 * animal is dog
 * animal is fish
 * animal is bird
 * animal is snake
 * % ./20q --no-vegetables
 * no vegetables!
 * % ./20q -v
 * vegetables OK
 * % ./20q -vkale
 * vegetable is kale
 * % ./20q -m10
 * 10 minerals
 * % ./20q -m foo
 * '-m' expects an integer, not 'foo'
 * </pre>
 */


/* Option types for Clp_SetOptionChar */
#define Clp_DoubledLong         (Clp_LongImplicit * 2)

#define Clp_InitialValType      8
#define MAX_AMBIGUOUS_VALUES    4

typedef struct {
    int val_type;
    Clp_ValParseFunc func;
    int flags;
    void *user_data;
} Clp_ValType;

typedef struct {
    unsigned ilong : 1;
    unsigned ishort : 1;
    unsigned imandatory : 1;
    unsigned ioptional : 1;
    unsigned ipos : 1;
    unsigned ineg : 1;
    unsigned iprefmatch : 1;
    unsigned lmmpos_short : 1;
    unsigned lmmneg_short : 1;
    unsigned unquoted : 1;
    unsigned char ilongoff;
    int lmmpos;
    int lmmneg;
} Clp_InternOption;


#define Clp_OptionCharsSize     5

typedef struct {
    int c;
    int type;
} Clp_Oclass;
#define Clp_OclassSize          10

typedef struct Clp_Internal {
    const Clp_Option *opt;
    Clp_InternOption *iopt;
    int nopt;
    unsigned opt_generation;

    Clp_ValType *valtype;
    int nvaltype;

    const char * const *argv;
    int argc;

    Clp_Oclass oclass[Clp_OclassSize];
    int noclass;
    int long1pos;
    int long1neg;
    int utf8;

    char option_chars[Clp_OptionCharsSize];
    const char *xtext;

    const char *program_name;
    void (*error_handler)(Clp_Parser *, const char *);

    int option_processing;
    int current_option;

    unsigned char is_short;
    unsigned char whole_negated; /* true if negated by an option character */
    unsigned char could_be_short;
    unsigned char current_short;
    unsigned char negated_by_no;

    int ambiguous;
    int ambiguous_values[MAX_AMBIGUOUS_VALUES];
} Clp_Internal;


struct Clp_ParserState {
    const char * const *argv;
    int argc;

    char option_chars[Clp_OptionCharsSize];
    const char *xtext;

    int option_processing;

    unsigned opt_generation;
    int current_option;
    unsigned char is_short;
    unsigned char whole_negated;
    unsigned char current_short;
    unsigned char negated_by_no;
};


typedef struct Clp_StringList {
    Clp_Option *items;
    Clp_InternOption *iopt;
    int nitems;

    unsigned char allow_int;
    unsigned char val_long;
    int nitems_invalid_report;
} Clp_StringList;


static const Clp_Option clp_option_sentinel[] = {
    {"", 0, Clp_NotOption, 0, 0},
    {"", 0, Clp_Done, 0, 0},
    {"", 0, Clp_BadOption, 0, 0},
    {"", 0, Clp_Error, 0, 0}
};


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 *, const Clp_Option *,
                           const Clp_InternOption *, const char *, const char *,
                           ...);


/*******
 * utf8
 **/

#define U_REPLACEMENT 0xFFFD

static char *
encode_utf8(char *s, int n, int c)
{
    if (c < 0 || c >= 0x110000 || (c >= 0xD800 && c <= 0xDFFF))
        c = U_REPLACEMENT;
    if (c <= 0x7F && n >= 1)
        *s++ = c;
    else if (c <= 0x7FF && n >= 2) {
        *s++ = 0xC0 | (c >> 6);
        goto char1;
    } else if (c <= 0xFFFF && n >= 3) {
        *s++ = 0xE0 | (c >> 12);
        goto char2;
    } else if (n >= 4) {
        *s++ = 0xF0 | (c >> 18);
        *s++ = 0x80 | ((c >> 12) & 0x3F);
      char2:
        *s++ = 0x80 | ((c >> 6) & 0x3F);
      char1:
        *s++ = 0x80 | (c & 0x3F);
    }
    return s;
}

static int
decode_utf8(const char *s, const char **cp)
{
    int c;
    if ((unsigned char) *s <= 0x7F)             /* 1 byte:  0x000000-0x00007F */
        c = *s++;
    else if ((unsigned char) *s <= 0xC1)        /*   bad/overlong encoding */
        goto replacement;
    else if ((unsigned char) *s <= 0xDF) {      /* 2 bytes: 0x000080-0x0007FF */
        if ((s[1] & 0xC0) != 0x80)              /*   bad encoding */
            goto replacement;
        c = (*s++ & 0x1F) << 6;
        goto char1;
    } else if ((unsigned char) *s <= 0xEF) {    /* 3 bytes: 0x000800-0x00FFFF */
        if ((s[1] & 0xC0) != 0x80               /*   bad encoding */
            || (s[2] & 0xC0) != 0x80            /*   bad encoding */
            || ((unsigned char) *s == 0xE0      /*   overlong encoding */
                && (s[1] & 0xE0) == 0x80)
            || ((unsigned char) *s == 0xED      /*   encoded surrogate */
                && (s[1] & 0xE0) == 0xA0))
            goto replacement;
        c = (*s++ & 0x0F) << 12;
        goto char2;
    } else if ((unsigned char) *s <= 0xF4) {    /* 4 bytes: 0x010000-0x10FFFF */
        if ((s[1] & 0xC0) != 0x80               /*   bad encoding */
            || (s[2] & 0xC0) != 0x80            /*   bad encoding */
            || (s[3] & 0xC0) != 0x80            /*   bad encoding */
            || ((unsigned char) *s == 0xF0      /*   overlong encoding */
                && (s[1] & 0xF0) == 0x80)
            || ((unsigned char) *s == 0xF4      /*   encoded value > 0x10FFFF */
                && (unsigned char) s[1] >= 0x90))
            goto replacement;
        c = (*s++ & 0x07) << 18;
        c += (*s++ & 0x3F) << 12;
      char2:
        c += (*s++ & 0x3F) << 6;
      char1:
        c += (*s++ & 0x3F);
    } else {
      replacement:
        c = U_REPLACEMENT;
        for (s++; (*s & 0xC0) == 0x80; s++)
            /* nothing */;
    }
    if (cp)
        *cp = s;
    return c;
}

static int
utf8_charlen(const char *s)
{
    const char *sout;
    (void) decode_utf8(s, &sout);
    return sout - s;
}

static int
clp_utf8_charlen(const Clp_Internal *cli, const char *s)
{
    return (cli->utf8 ? utf8_charlen(s) : 1);
}


/*******
 * Clp_NewParser, etc.
 **/

static int
min_different_chars(const char *s, const char *t)
     /* Returns the minimum number of bytes 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
long_as_short(const Clp_Internal *cli, const Clp_Option *o,
              Clp_InternOption *io, int failure)
{
    if ((cli->long1pos || cli->long1neg) && io->ilong) {
        const char *name = o->long_name + io->ilongoff;
        if (cli->utf8) {
            int c = decode_utf8(name, &name);
            if (!*name && c && c != U_REPLACEMENT)
                return c;
        } else if (name[0] && !name[1])
            return (unsigned char) name[0];
    }
    return failure;
}

static void
compare_options(Clp_Parser *clp, const Clp_Option *o1, Clp_InternOption *io1,
                const Clp_Option *o2, Clp_InternOption *io2)
{
    Clp_Internal *cli = clp->internal;
    int short1, shortx1;

    /* ignore meaningless combinations */
    if ((!io1->ishort && !io1->ilong) || (!io2->ishort && !io2->ilong)
        || !((io1->ipos && io2->ipos) || (io1->ineg && io2->ineg))
        || o1->option_id == o2->option_id)
        return;

    /* look for duplication of short options */
    short1 = (io1->ishort ? o1->short_name : -1);
    shortx1 = long_as_short(cli, o1, io1, -2);
    if (short1 >= 0 || shortx1 >= 0) {
        int short2 = (io2->ishort ? o2->short_name : -3);
        int shortx2 = long_as_short(cli, o2, io2, -4);
        if (short1 == short2)
            Clp_OptionError(clp, "CLP internal error: more than 1 option has short name %<%c%>", short1);
        else if ((short1 == shortx2 || shortx1 == short2 || shortx1 == shortx2)
                 && ((io1->ipos && io2->ipos && cli->long1pos)
                     || (io1->ineg && io2->ineg && cli->long1neg)))
            Clp_OptionError(clp, "CLP internal error: 1-char long name conflicts with short name %<%c%>", (short1 == shortx2 ? shortx2 : shortx1));
    }

    /* analyze longest minimum match */
    if (io1->ilong) {
        const char *name1 = o1->long_name + io1->ilongoff;

        /* long name's first character matches short name */
        if (io2->ishort && !io1->iprefmatch) {
            int name1char = (cli->utf8 ? decode_utf8(name1, 0) : (unsigned char) *name1);
            if (name1char == o2->short_name) {
                if (io1->ipos && io2->ipos)
                    io1->lmmpos_short = 1;
                if (io1->ineg && io2->ineg)
                    io1->lmmneg_short = 1;
            }
        }

        /* match long name to long name */
        if (io2->ilong) {
            const char *name2 = o2->long_name + io2->ilongoff;
            if (strcmp(name1, name2) == 0)
                Clp_OptionError(clp, "CLP internal error: duplicate long name %<%s%>", name1);
            if (io1->ipos && io2->ipos && !strncmp(name1, name2, io1->lmmpos)
                && (!io1->iprefmatch || strncmp(name1, name2, strlen(name1))))
                io1->lmmpos = min_different_chars(name1, name2);
            if (io1->ineg && io2->ineg && !strncmp(name1, name2, io1->lmmneg)
                && (!io1->iprefmatch || strncmp(name1, name2, strlen(name1))))
                io1->lmmneg = min_different_chars(name1, name2);
        }
    }
}

static void
calculate_lmm(Clp_Parser *clp, const Clp_Option *opt, Clp_InternOption *iopt, int nopt)
{
    int i, j;
    for (i = 0; i < nopt; ++i) {
        iopt[i].lmmpos = iopt[i].lmmneg = 1;
        iopt[i].lmmpos_short = iopt[i].lmmneg_short = 0;
        for (j = 0; j < nopt; ++j)
            compare_options(clp, &opt[i], &iopt[i], &opt[j], &iopt[j]);
    }
}

/** @param argc number of arguments
 * @param argv argument array
 * @param nopt number of option definitions
 * @param opt option definition array
 * @return the parser
 *
 * The new Clp_Parser that will parse the arguments in @a argv according to
 * the option definitions in @a opt.
 *
 * The Clp_Parser is created with the following characteristics:
 *
 * <ul>
 * <li>The "-" character introduces short options (<tt>Clp_SetOptionChar(clp,
 * '-', Clp_Short)</tt>).</li>
 * <li>Clp_ProgramName is set from the first argument in @a argv, if any.  The
 * first argument returned by Clp_Next() will be the second argument in @a
 * argv.  Note that this behavior differs from Clp_SetArguments.</li>
 * <li>UTF-8 support is on iff the <tt>LANG</tt> environment variable contains
 * one of the substrings "UTF-8", "UTF8", or "utf8".  Override this with
 * Clp_SetUTF8().</li>
 * <li>The Clp_ValString, Clp_ValStringNotOption, Clp_ValInt, Clp_ValUnsigned,
 * Clp_ValLong, Clp_ValUnsignedLong, Clp_ValBool, and Clp_ValDouble types are
 * installed.</li>
 * <li>Errors are reported to standard error.</li>
 * </ul>
 *
 * You may also create a Clp_Parser with no arguments or options
 * (<tt>Clp_NewParser(0, 0, 0, 0)</tt>) and set the arguments and options
 * later.
 *
 * Returns NULL if there isn't enough memory to construct the parser.
 *
 * @note The CLP library will not modify the contents of @a argv or @a opt.
 * The calling program must not modify @a opt.  It may modify @a argv in
 * limited cases.
 */
Clp_Parser *
Clp_NewParser(int argc, const char * const *argv, int nopt, const Clp_Option *opt)
{
    Clp_Parser *clp = (Clp_Parser *)malloc(sizeof(Clp_Parser));
    Clp_Internal *cli = (Clp_Internal *)malloc(sizeof(Clp_Internal));
    Clp_InternOption *iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nopt);
    if (cli)
        cli->valtype = (Clp_ValType *)malloc(sizeof(Clp_ValType) * Clp_InitialValType);
    if (!clp || !cli || !iopt || !cli->valtype)
        goto failed;

    clp->option = &clp_option_sentinel[-Clp_Done];
    clp->negated = 0;
    clp->have_val = 0;
    clp->vstr = 0;
    clp->user_data = 0;
    clp->internal = cli;

    cli->opt = opt;
    cli->nopt = nopt;
    cli->iopt = iopt;
    cli->opt_generation = 0;
    cli->error_handler = 0;

    /* Assign program name (now so we can call Clp_OptionError) */
    if (argc > 0) {
        const char *slash = strrchr(argv[0], '/');
        cli->program_name = slash ? slash + 1 : argv[0];
    } else
        cli->program_name = 0;

    /* Assign arguments, skipping program name */
    Clp_SetArguments(clp, argc - 1, argv + 1);

    /* Initialize UTF-8 status and option classes */
    {
        char *s = getenv("LANG");
        cli->utf8 = (s && (strstr(s, "UTF-8") != 0 || strstr(s, "UTF8") != 0
                           || strstr(s, "utf8") != 0));
    }
    cli->oclass[0].c = '-';
    cli->oclass[0].type = Clp_Short;
    cli->noclass = 1;
    cli->long1pos = cli->long1neg = 0;

    /* Add default type parsers */
    cli->nvaltype = 0;
    Clp_AddType(clp, Clp_ValString, 0, parse_string, 0);
    Clp_AddType(clp, Clp_ValStringNotOption, Clp_DisallowOptions, parse_string, 0);
    Clp_AddType(clp, Clp_ValInt, 0, parse_int, (void*) (uintptr_t) 0);
    Clp_AddType(clp, Clp_ValUnsigned, 0, parse_int, (void*) (uintptr_t) 1);
    Clp_AddType(clp, Clp_ValLong, 0, parse_int, (void*) (uintptr_t) 2);
    Clp_AddType(clp, Clp_ValUnsignedLong, 0, parse_int, (void*) (uintptr_t) 3);
    Clp_AddType(clp, Clp_ValBool, 0, parse_bool, 0);
    Clp_AddType(clp, Clp_ValDouble, 0, parse_double, 0);

    /* Set options */
    Clp_SetOptions(clp, nopt, opt);

    return clp;

  failed:
    if (cli && cli->valtype)
        free(cli->valtype);
    if (cli)
        free(cli);
    if (clp)
        free(clp);
    if (iopt)
        free(iopt);
    return 0;
}

/** @param clp the parser
 *
 * All memory associated with @a clp is freed. */
void
Clp_DeleteParser(Clp_Parser *clp)
{
    int i;
    Clp_Internal *cli;
    if (!clp)
        return;

    cli = clp->internal;

    /* get rid of any string list types */
    for (i = 0; i < cli->nvaltype; i++)
        if (cli->valtype[i].func == parse_string_list) {
            Clp_StringList *clsl = (Clp_StringList *)cli->valtype[i].user_data;
            free(clsl->items);
            free(clsl->iopt);
            free(clsl);
        }

    free(cli->valtype);
    free(cli->iopt);
    free(cli);
    free(clp);
}


/** @param clp the parser
 * @param errh error handler function
 * @return previous error handler function
 *
 * The error handler function is called when CLP encounters an error while
 * parsing the command line.  It is called with the arguments "<tt>(*errh)(@a
 * clp, s)</tt>", where <tt>s</tt> is a description of the error terminated by
 * a newline.  The <tt>s</tt> descriptions produced by CLP itself are prefixed
 * by the program name, if any. */
Clp_ErrorHandler
Clp_SetErrorHandler(Clp_Parser *clp, void (*errh)(Clp_Parser *, const char *))
{
    Clp_Internal *cli = clp->internal;
    Clp_ErrorHandler old = cli->error_handler;
    cli->error_handler = errh;
    return old;
}

/** @param clp the parser
 * @param utf8 does the parser support UTF-8?
 * @return previous UTF-8 mode
 *
 * In UTF-8 mode, all input strings (arguments and long names for options) are
 * assumed to be encoded via UTF-8, and all character names
 * (Clp_SetOptionChar() and short names for options) may cover the whole
 * Unicode range.  Out of UTF-8 mode, all input strings are treated as binary,
 * and all character names must be unsigned char values.
 *
 * Furthermore, error messages in UTF-8 mode may contain Unicode quote
 * characters. */
int
Clp_SetUTF8(Clp_Parser *clp, int utf8)
{
    Clp_Internal *cli = clp->internal;
    int old_utf8 = cli->utf8;
    cli->utf8 = utf8;
    calculate_lmm(clp, cli->opt, cli->iopt, cli->nopt);
    return old_utf8;
}

/** @param clp the parser
 * @param c character
 * @return option character treatment
 *
 * Returns an integer specifying how CLP treats arguments that begin
 * with character @a c.  See Clp_SetOptionChar for possibilities.
 */
int
Clp_OptionChar(Clp_Parser *clp, int c)
{
    Clp_Internal *cli = clp->internal;
    int i, oclass = 0;
    if (cli->noclass > 0 && cli->oclass[0].c == 0)
        oclass = cli->oclass[0].type;
    for (i = 0; i < cli->noclass; ++i)
        if (cli->oclass[i].c == c)
            oclass = cli->oclass[i].type;
    return oclass;
}

/** @param clp the parser
 * @param c character
 * @param type option character treatment
 * @return previous option character treatment, or -1 on error
 *
 * @a type specifies how CLP treats arguments that begin with character @a c.
 * Possibilities are:
 *
 * <dl>
 * <dt>Clp_NotOption (or 0)</dt>
 * <dd>The argument cannot be an option.</dd>
 * <dt>Clp_Long</dt>
 * <dd>The argument is a long option.</dd>
 * <dt>Clp_Short</dt>
 * <dd>The argument is a set of short options.</dd>
 * <dt>Clp_Short|Clp_Long</dt>
 * <dd>The argument is either a long option or, if no matching long option is
 * found, a set of short options.</dd>
 * <dt>Clp_LongNegated</dt>
 * <dd>The argument is a negated long option.  For example, after
 * Clp_SetOptionChar(@a clp, '^', Clp_LongNegated), the argument "^foo" is
 * equivalent to "--no-foo".</dd>
 * <dt>Clp_ShortNegated</dt>
 * <dd>The argument is a set of negated short options.</dd>
 * <dt>Clp_ShortNegated|Clp_LongNegated</dt>
 * <dd>The argument is either a negated long option or, if no matching long
 * option is found, a set of negated short options.</dd>
 * <dt>Clp_LongImplicit</dt>
 * <dd>The argument may be a long option, where the character @a c is actually
 * part of the long option name.  For example, after Clp_SetOptionChar(@a clp,
 * 'f', Clp_LongImplicit), the argument "foo" may be equivalent to
 * "--foo".</dd>
 * </dl>
 *
 * In UTF-8 mode, @a c may be any Unicode character.  Otherwise, @a c must be
 * an unsigned char value.  The special character 0 assigns @a type to @em
 * every character.
 *
 * It is an error if @a c is out of range, @a type is illegal, or there are
 * too many character definitions stored in @a clp already.  The function
 * returns -1 on error.
 *
 * A double hyphen "--" always introduces a long option.  This behavior cannot
 * currently be changed with Clp_SetOptionChar().
 */
int
Clp_SetOptionChar(Clp_Parser *clp, int c, int type)
{
    int i, long1pos, long1neg;
    int old = Clp_OptionChar(clp, c);
    Clp_Internal *cli = clp->internal;

    if (type != Clp_NotOption && type != Clp_Short && type != Clp_Long
        && type != Clp_ShortNegated && type != Clp_LongNegated
        && type != Clp_LongImplicit && type != (Clp_Short | Clp_Long)
        && type != (Clp_ShortNegated | Clp_LongNegated))
        return -1;
    if (c < 0 || c >= (cli->utf8 ? 0x110000 : 256))
        return -1;

    if (c == 0)
        cli->noclass = 0;
    for (i = 0; i < cli->noclass; ++i)
        if (cli->oclass[i].c == c)
            break;
    if (i == Clp_OclassSize)
        return -1;

    cli->oclass[i].c = c;
    cli->oclass[i].type = type;
    if (cli->noclass == i)
        cli->noclass = i + 1;

    long1pos = long1neg = 0;
    for (i = 0; i < cli->noclass; ++i) {
        if ((cli->oclass[i].type & Clp_Short)
            && (cli->oclass[i].type & Clp_Long))
            long1pos = 1;
        if ((cli->oclass[i].type & Clp_ShortNegated)
            && (cli->oclass[i].type & Clp_LongNegated))
            long1neg = 1;
    }

    if (long1pos != cli->long1pos || long1neg != cli->long1neg) {
        /* Must recheck option set */
        cli->long1pos = long1pos;
        cli->long1neg = long1neg;
        calculate_lmm(clp, cli->opt, cli->iopt, cli->nopt);
    }

    return old;
}

/** @param clp the parser
 * @param nopt number of option definitions
 * @param opt option definition array
 * @return 0 on success, -1 on failure
 *
 * Installs the option definitions in @a opt.  Future option parsing will
 * use @a opt to search for options.
 *
 * Also checks @a opt's option definitions for validity.  "CLP internal
 * errors" are reported via Clp_OptionError() if:
 *
 * <ul>
 * <li>An option has a negative ID.</li>
 * <li>Two different short options have the same name.</li>
 * <li>Two different long options have the same name.</li>
 * <li>A short and a long option are ambiguous, in that some option character
 * might introduce either a short or a long option (e.g., Clp_SetOptionChar(@a
 * clp, '-', Clp_Long|Clp_Short)), and a short name equals a long name.</li>
 * </ul>
 *
 * If necessary memory cannot be allocated, this function returns -1 without
 * modifying the parser.
 *
 * @note The CLP library will not modify the contents of @a argv or @a opt.
 * The calling program must not modify @a opt either until another call to
 * Clp_SetOptions() or the parser is destroyed.
 */
int
Clp_SetOptions(Clp_Parser *clp, int nopt, const Clp_Option *opt)
{
    Clp_Internal *cli = clp->internal;
    Clp_InternOption *iopt;
    int i;
    static unsigned opt_generation = 0;

    if (nopt > cli->nopt) {
        iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nopt);
        if (!iopt)
            return -1;
        free(cli->iopt);
        cli->iopt = iopt;
    }

    cli->opt = opt;
    cli->nopt = nopt;
    cli->opt_generation = ++opt_generation;
    iopt = cli->iopt;
    cli->current_option = -1;

    /* Massage the options to make them usable */
    for (i = 0; i < nopt; ++i) {
        memset(&iopt[i], 0, sizeof(iopt[i]));

        /* Ignore negative option_ids, which are internal to CLP */
        if (opt[i].option_id < 0) {
            Clp_OptionError(clp, "CLP internal error: option %d has negative option_id", i);
            iopt[i].ilong = iopt[i].ishort = iopt[i].ipos = iopt[i].ineg = iopt[i].unquoted = 0;
            continue;
        }

        /* Set flags based on input flags */
        iopt[i].ilong = (opt[i].long_name != 0 && opt[i].long_name[0] != 0);
        iopt[i].ishort = (opt[i].short_name > 0
                          && opt[i].short_name < (cli->utf8 ? 0x110000 : 256));
        iopt[i].ipos = 1;
        iopt[i].ineg = (opt[i].flags & Clp_Negate) != 0;
        iopt[i].imandatory = (opt[i].flags & Clp_Mandatory) != 0;
        iopt[i].ioptional = (opt[i].flags & Clp_Optional) != 0;
        iopt[i].iprefmatch = (opt[i].flags & Clp_PreferredMatch) != 0;
        iopt[i].unquoted = 0;
        iopt[i].ilongoff = 0;

        /* Enforce invariants */
        if (opt[i].val_type <= 0)
            iopt[i].imandatory = iopt[i].ioptional = 0;
        if (opt[i].val_type > 0 && !iopt[i].ioptional)
            iopt[i].imandatory = 1;

        /* Options that start with 'no-' should be changed to OnlyNegated */
        if (iopt[i].ilong && strncmp(opt[i].long_name, "no-", 3) == 0) {
            iopt[i].ipos = 0;
            iopt[i].ineg = 1;
            iopt[i].ilongoff = 3;
            if (strncmp(opt[i].long_name + 3, "no-", 3) == 0)
                Clp_OptionError(clp, "CLP internal error: option %d begins with \"no-no-\"", i);
        } else if (opt[i].flags & Clp_OnlyNegated) {
            iopt[i].ipos = 0;
            iopt[i].ineg = 1;
        }
    }

    /* Check option set */
    calculate_lmm(clp, opt, iopt, nopt);

    return 0;
}

/** @param clp the parser
 * @param argc number of arguments
 * @param argv argument array
 *
 * Installs the arguments in @a argv for parsing.  Future option parsing will
 * analyze @a argv.
 *
 * Unlike Clp_NewParser(), this function does not treat @a argv[0] specially.
 * The first subsequent call to Clp_Next() will analyze @a argv[0].
 *
 * This function also sets option processing to on, as by
 * Clp_SetOptionProcessing(@a clp, 1).
 *
 * @note The CLP library will not modify the contents of @a argv.  The calling
 * program should not generally modify the element of @a argv that CLP is
 * currently analyzing.
 */
void
Clp_SetArguments(Clp_Parser *clp, int argc, const char * const *argv)
{
    Clp_Internal *cli = clp->internal;

    cli->argc = argc + 1;
    cli->argv = argv - 1;

    cli->is_short = 0;
    cli->whole_negated = 0;
    cli->option_processing = 1;
    cli->current_option = -1;
}


/** @param clp the parser
 * @param on whether to search for options
 * @return previous option processing setting
 *
 * When option processing is off, every call to Clp_Next() returns
 * Clp_NotOption.  By default the option <tt>"--"</tt> turns off option
 * processing and is otherwise ignored.
 */
int
Clp_SetOptionProcessing(Clp_Parser *clp, int on)
{
    Clp_Internal *cli = clp->internal;
    int old = cli->option_processing;
    cli->option_processing = on;
    return old;
}


/*******
 * functions for Clp_Option lists
 **/

/* 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(Clp_Parser *clp, const char *arg,
                int nopt, const Clp_Option *opt,
                const Clp_InternOption *iopt,
                int *ambiguous, int *ambiguous_values)
     /* 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;
    int negated = clp && clp->negated;
    int first_charlen = (clp ? clp_utf8_charlen(clp->internal, arg) : 1);

  retry:
    for (i = 0; i < nopt; i++) {
        int len, lmm;
        if (!iopt[i].ilong || (negated ? !iopt[i].ineg : !iopt[i].ipos))
            continue;

        lmm = (negated ? iopt[i].lmmneg : iopt[i].lmmpos);
        if (clp && clp->internal->could_be_short
            && (negated ? iopt[i].lmmneg_short : iopt[i].lmmpos_short))
            lmm = (first_charlen >= lmm ? first_charlen + 1 : lmm);
        len = argcmp(opt[i].long_name + iopt[i].ilongoff, 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
 **/

static int
val_type_binsearch(Clp_Internal *cli, int val_type)
{
    unsigned l = 0, r = cli->nvaltype;
    while (l < r) {
        unsigned m = l + (r - l) / 2;
        if (cli->valtype[m].val_type == val_type)
            return m;
        else if (cli->valtype[m].val_type < val_type)
            l = m + 1;
        else
            r = m;
    }
    return l;
}

/** @param clp the parser
 * @param val_type value type ID
 * @param flags value type flags
 * @param parser parser function
 * @param user_data user data for @a parser function
 * @return 0 on success, -1 on failure
 *
 * Defines argument type @a val_type in parser @a clp.  The parsing function
 * @a parser will be passed argument values for type @a val_type.  It should
 * parse the argument into values (usually in @a clp->val, but sometimes
 * elsewhere), report errors if necessary, and return whether the parse was
 * successful.
 *
 * Any prior argument parser match @a val_type is removed.  @a val_type must
 * be greater than zero.
 *
 * @a flags specifies additional parsing flags.  At the moment the only
 * relevant flag is Clp_DisallowOptions, which means that separated values
 * must not look like options.  For example, assume argument
 * <tt>--a</tt>/<tt>-a</tt> has mandatory value type Clp_ValStringNotOption
 * (which has Clp_DisallowOptions).  Then:
 *
 * <ul>
 * <li><tt>--a=--b</tt> will parse with value <tt>--b</tt>.</li>
 * <li><tt>-a--b</tt> will parse with value <tt>--b</tt>.</li>
 * <li><tt>--a --b</tt> will not parse, since the mandatory value looks like
 * an option.</li>
 * <li><tt>-a --b</tt> will not parse, since the mandatory value looks like
 * an option.</li>
 * </ul>
 */
int
Clp_AddType(Clp_Parser *clp, int val_type, int flags,
            Clp_ValParseFunc parser, void *user_data)
{
    Clp_Internal *cli = clp->internal;
    int vtpos;

    if (val_type <= 0 || !parser)
        return -1;

    vtpos = val_type_binsearch(cli, val_type);

    if (vtpos == cli->nvaltype || cli->valtype[vtpos].val_type != val_type) {
        if (cli->nvaltype != 0 && (cli->nvaltype % Clp_InitialValType) == 0) {
            Clp_ValType *new_valtype =
                (Clp_ValType *) realloc(cli->valtype, sizeof(Clp_ValType) * (cli->nvaltype + Clp_InitialValType));
            if (!new_valtype)
                return -1;
            cli->valtype = new_valtype;
        }
        memmove(&cli->valtype[vtpos + 1], &cli->valtype[vtpos],
                sizeof(Clp_ValType) * (cli->nvaltype - vtpos));
        cli->nvaltype++;
        cli->valtype[vtpos].func = 0;
    }

    if (cli->valtype[vtpos].func == parse_string_list) {
        Clp_StringList *clsl = (Clp_StringList *) cli->valtype[vtpos].user_data;
        free(clsl->items);
        free(clsl->iopt);
        free(clsl);
    }

    cli->valtype[vtpos].val_type = val_type;
    cli->valtype[vtpos].func = parser;
    cli->valtype[vtpos].flags = flags;
    cli->valtype[vtpos].user_data = user_data;
    return 0;
}


/*******
 * Default argument parsers
 **/

static int
parse_string(Clp_Parser *clp, const char *arg, int complain, void *user_data)
{
    (void)complain, (void)user_data;
    clp->val.s = arg;
    return 1;
}

static int
parse_int(Clp_Parser* clp, const char* arg, int complain, void* user_data)
{
    const char *val;
    uintptr_t type = (uintptr_t) user_data;
    if (*arg == 0 || isspace((unsigned char) *arg)
        || ((type & 1) && *arg == '-'))
        val = arg;
    else if (type & 1) { /* unsigned */
#if HAVE_STRTOUL
        clp->val.ul = strtoul(arg, (char **) &val, 0);
#else
        /* don't bother really trying to do it right */
        if (arg[0] == '-')
            val = arg;
        else
            clp->val.l = strtol(arg, (char **) &val, 0);
#endif
    } else
        clp->val.l = strtol(arg, (char **) &val, 0);
    if (type <= 1)
        clp->val.u = (unsigned) clp->val.ul;
    if (*arg != 0 && *val == 0)
        return 1;
    else {
        if (complain) {
            const char *message = (type & 1)
                ? "%<%O%> expects a nonnegative integer, not %<%s%>"
                : "%<%O%> expects an integer, not %<%s%>";
            Clp_OptionError(clp, message, arg);
        }
        return 0;
    }
}

static int
parse_double(Clp_Parser *clp, const char *arg, int complain, void *user_data)
{
    const char *val;
    (void)user_data;
    if (*arg == 0 || isspace((unsigned char) *arg))
        val = arg;
    else
        clp->val.d = strtod(arg, (char **) &val);
    if (*arg != 0 && *val == 0)
        return 1;
    else {
        if (complain)
            Clp_OptionError(clp, "%<%O%> expects a real number, not %<%s%>", arg);
        return 0;
    }
}

static int
parse_bool(Clp_Parser *clp, const char *arg, int complain, void *user_data)
{
    int i;
    char lcarg[6];
    (void)user_data;
    if (strlen(arg) > 5 || strchr(arg, '=') != 0)
        goto error;

    for (i = 0; arg[i] != 0; i++)
        lcarg[i] = tolower((unsigned char) 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 *user_data)
{
    Clp_StringList *sl = (Clp_StringList *)user_data;
    int idx, ambiguous = 0;
    int ambiguous_values[MAX_AMBIGUOUS_VALUES + 1];

    /* actually look for a string value */
    idx = find_prefix_opt
        (0, arg, sl->nitems, sl->items, sl->iopt,
         &ambiguous, ambiguous_values);
    if (idx >= 0) {
        clp->val.i = sl->items[idx].option_id;
        if (sl->val_long)
            clp->val.l = clp->val.i;
        return 1;
    }

    if (sl->allow_int) {
        if (parse_int(clp, arg, 0, (void*) (uintptr_t) (sl->val_long ? 2 : 0)))
            return 1;
    }

    if (complain) {
        const char *complaint = (ambiguous ? "ambiguous" : "invalid");
        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, sl->iopt,
             "", "option %<%V%> is %s", complaint);
    } else
        return 0;
}


static int
finish_string_list(Clp_Parser *clp, int val_type, int flags,
                   Clp_Option *items, int nitems, int itemscap)
{
    int i, niitems = nitems + (flags & Clp_AllowNumbers ? 1 : 0);
    Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
    Clp_InternOption *iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * niitems);
    if (!clsl || !iopt)
        goto error;

    clsl->items = items;
    clsl->iopt = iopt;
    clsl->nitems = nitems;
    clsl->allow_int = (flags & Clp_AllowNumbers) != 0;
    clsl->val_long = (flags & Clp_StringListLong) != 0;

    memset(iopt, 0, sizeof(Clp_InternOption) * niitems);
    for (i = 0; i < niitems; i++) {
        iopt[i].ilong = iopt[i].ipos = 1;
        iopt[i].ishort = iopt[i].ineg = iopt[i].ilongoff = iopt[i].iprefmatch = iopt[i].unquoted = 0;
    }
    calculate_lmm(clp, items, iopt, nitems);

    if (nitems < MAX_AMBIGUOUS_VALUES && nitems < itemscap && clsl->allow_int) {
        items[nitems].long_name = "any integer";
        iopt[nitems].unquoted = 1;
        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;

    if (Clp_AddType(clp, val_type, 0, parse_string_list, clsl) >= 0)
        return 0;

  error:
    if (clsl)
        free(clsl);
    if (iopt)
        free(iopt);
    return -1;
}

/** @param clp the parser
 * @param val_type value type ID
 * @param flags string list flags
 * @return 0 on success, -1 on failure
 *
 * Defines argument type @a val_type in parser @a clp.  The parsing function
 * sets @a clp->val.i to an integer.  The value string is matched against
 * strings provided in the ellipsis arguments.  For example, the
 * Clp_AddStringListType() call below has the same effect as the
 * Clp_AddStringListTypeVec() call:
 *
 * For example:
 * @code
 * Clp_AddStringListType(clp, 100, Clp_AllowNumbers, "cat", 1,
 *                       "cattle", 2, "dog", 3, (const char *) NULL);
 *
 * const char * const strs[] = { "cat", "cattle", "dog" };
 * const int vals[]          = { 1,     2,        3     };
 * Clp_AddStringListTypeVec(clp, 100, Clp_AllowNumbers, 3, strs, vals);
 * @endcode
 *
 * @note The CLP library will not modify any of the passed-in strings.  The
 * calling program must not modify or free them either until the parser is
 * destroyed.
 */
int
Clp_AddStringListType(Clp_Parser *clp, int val_type, int flags, ...)
{
    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;
        if (flags & Clp_StringListLong) {
            long lvalue = va_arg(val, long);
            value = (int) lvalue;
            assert(value == lvalue);
        } else
            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, val_type, flags, items, nitems, itemscap) >= 0)
        return 0;

  error:
    va_end(val);
    if (items)
        free(items);
    return -1;
}

/** @param clp the parser
 * @param val_type value type ID
 * @param flags string list flags
 * @param nstrs number of strings in list
 * @param strs array of strings
 * @param vals array of values
 * @return 0 on success, -1 on failure
 *
 * Defines argument type @a val_type in parser @a clp.  The parsing function
 * sets @a clp->val.i to an integer.  The value string is matched against the
 * @a strs.  If there's a unique match, the corresponding entry from @a vals
 * is returned.  Unique prefix matches also work.  Finally, if @a flags
 * contains the Clp_AllowNumbers flag, then integers are also accepted.
 *
 * For example:
 * @code
 * const char * const strs[] = { "cat", "cattle", "dog" };
 * const int vals[]          = { 1,     2,        3     };
 * Clp_AddStringListTypeVec(clp, 100, Clp_AllowNumbers, 3, strs, vals);
 * @endcode
 *
 * Say that option <tt>--animal</tt> takes value type 100.  Then:
 *
 * <ul>
 * <li><tt>--animal=cat</tt> will succeed and set @a clp->val.i = 1.</li>
 * <li><tt>--animal=cattle</tt> will succeed and set @a clp->val.i = 2.</li>
 * <li><tt>--animal=dog</tt> will succeed and set @a clp->val.i = 3.</li>
 * <li><tt>--animal=d</tt> will succeed and set @a clp->val.i = 3.</li>
 * <li><tt>--animal=c</tt> will fail, since <tt>c</tt> is ambiguous.</li>
 * <li><tt>--animal=4</tt> will succeed and set @a clp->val.i = 4.</li>
 * </ul>
 *
 * @note The CLP library will not modify the contents of @a strs or @a vals.
 * The calling program can modify the @a strs array, but the actual strings
 * (for instance, @a strs[0] and @a strs[1]) must not be modified or freed
 * until the parser is destroyed.
 */
int
Clp_AddStringListTypeVec(Clp_Parser *clp, int val_type, int flags,
                         int nstrs, const char * const *strs,
                         const int *vals)
     /* 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 = (nstrs < 5 ? 5 : nstrs);
    Clp_Option *items = (Clp_Option *)malloc(sizeof(Clp_Option) * itemscap);
    if (!items)
        return -1;

    /* copy over items */
    for (i = 0; i < nstrs; i++) {
        items[i].long_name = strs[i];
        items[i].option_id = vals[i];
        items[i].flags = 0;
    }

    if (finish_string_list(clp, val_type, flags, items, nstrs, itemscap) >= 0)
        return 0;
    else {
        free(items);
        return -1;
    }
}


/*******
 * Returning information
 **/

const char *
Clp_ProgramName(Clp_Parser *clp)
{
    return clp->internal->program_name;
}

/** @param clp the parser
 * @param name new program name
 * @return previous program name
 *
 * The calling program should not modify or free @a name until @a clp itself
 * is destroyed. */
const char *
Clp_SetProgramName(Clp_Parser *clp, const char *name)
{
    const char *old = clp->internal->program_name;
    clp->internal->program_name = name;
    return old;
}


/******
 * Clp_ParserStates
 **/

/** @return the parser state
 *
 * A Clp_ParserState object can store a parsing state of a Clp_Parser object.
 * This state specifies exactly how far the Clp_Parser has gotten in parsing
 * an argument list.  The Clp_SaveParser() and Clp_RestoreParser() functions
 * can be used to save this state and then restore it later, allowing a
 * Clp_Parser to switch among argument lists.
 *
 * The initial state is empty, in that after Clp_RestoreParser(clp, state),
 * Clp_Next(clp) would return Clp_Done.
 *
 * Parser states can be saved and restored among different parser objects.
 *
 * @sa Clp_DeleteParserState, Clp_SaveParser, Clp_RestoreParser
 */
Clp_ParserState *
Clp_NewParserState(void)
{
    Clp_ParserState *state = (Clp_ParserState *)malloc(sizeof(Clp_ParserState));
    if (state) {
        state->argv = 0;
        state->argc = 0;
        state->option_chars[0] = 0;
        state->xtext = 0;
        state->option_processing = 0;
        state->opt_generation = 0;
        state->current_option = -1;
        state->is_short = 0;
        state->whole_negated = 0;
        state->current_short = 0;
        state->negated_by_no = 0;
    }
    return state;
}

/** @param state parser state
 *
 * The memory associated with @a state is freed.
 */
void
Clp_DeleteParserState(Clp_ParserState *state)
{
    free(state);
}

/** @param clp the parser
 * @param state parser state
 * @sa Clp_NewParserState, Clp_RestoreParser
 */
void
Clp_SaveParser(const Clp_Parser *clp, Clp_ParserState *state)
{
    Clp_Internal *cli = clp->internal;
    state->argv = cli->argv;
    state->argc = cli->argc;
    memcpy(state->option_chars, cli->option_chars, Clp_OptionCharsSize);
    state->xtext = cli->xtext;

    state->option_processing = cli->option_processing;
    state->opt_generation = cli->opt_generation;
    state->current_option = cli->current_option;
    state->is_short = cli->is_short;
    state->whole_negated = cli->whole_negated;
    state->current_short = cli->current_short;
    state->negated_by_no = cli->negated_by_no;
}


/** @param clp the parser
 * @param state parser state
 *
 * The parser state in @a state is restored into @a clp.  The next call to
 * Clp_Next() will return the same result as it would have at the time @a
 * state was saved (probably by Clp_SaveParser(@a clp, @a state)).
 *
 * A parser state contains information about arguments (argc and argv; see
 * Clp_SetArguments()) and option processing (Clp_SetOptionProcessing()), but
 * not about options (Clp_SetOptions()).  Changes to options and value types
 * are preserved across Clp_RestoreParser().
 *
 * @sa Clp_NewParserState, Clp_SaveParser
 */
void
Clp_RestoreParser(Clp_Parser *clp, const Clp_ParserState *state)
{
    Clp_Internal *cli = clp->internal;
    cli->argv = state->argv;
    cli->argc = state->argc;
    memcpy(cli->option_chars, state->option_chars, Clp_OptionCharsSize);
    cli->xtext = state->xtext;
    cli->option_processing = state->option_processing;
    cli->is_short = state->is_short;
    cli->whole_negated = state->whole_negated;
    cli->current_short = state->current_short;
    cli->negated_by_no = state->negated_by_no;
    if (cli->opt_generation == state->opt_generation)
        cli->current_option = state->current_option;
    else
        cli->current_option = -1;
}


/*******
 * Clp_Next and its helpers
 **/

static void
set_option_text(Clp_Internal *cli, const char *text, int n_option_chars)
{
    assert(n_option_chars < Clp_OptionCharsSize);
    memcpy(cli->option_chars, text, n_option_chars);
    cli->option_chars[n_option_chars] = 0;
    cli->xtext = text + n_option_chars;
}

static int
get_oclass(Clp_Parser *clp, const char *text, int *ocharskip)
{
    int c;
    if (clp->internal->utf8) {
        const char *s;
        c = decode_utf8(text, &s);
        *ocharskip = s - text;
    } else {
        c = (unsigned char) text[0];
        *ocharskip = 1;
    }
    return Clp_OptionChar(clp, c);
}

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_val = 1, and sets clp->vstr 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 oclass, ocharskip;

    /* clear relevant flags */
    clp->have_val = 0;
    clp->vstr = 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->xtext += clp_utf8_charlen(cli, cli->xtext);
        if (cli->xtext[0] == 0)
            cli->is_short = 0;
        else if (want_argument > 0) {
            /* handle -O[=]argument case */
            clp->have_val = 1;
            if (cli->xtext[0] == '=')
                clp->vstr = cli->xtext + 1;
            else
                clp->vstr = cli->xtext;
            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->xtext = 0;

    if (cli->argc <= 1)
        return 0;

    cli->argc--;
    cli->argv++;
    text = cli->argv[0];

    if (want_argument > 1)
        goto not_option;

    if (text[0] == '-' && text[1] == '-') {
        oclass = Clp_DoubledLong;
        ocharskip = 2;
    } else
        oclass = get_oclass(clp, text, &ocharskip);

    /* 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 ((oclass & (Clp_Short | Clp_ShortNegated))
        && (oclass & (Clp_Long | Clp_LongNegated))) {
        oclass &= ~(Clp_Short | Clp_ShortNegated);
        if (text[ocharskip])
            cli->could_be_short = 1;
    }

    switch (oclass) {

      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[ocharskip])
            goto not_option;
        set_option_text(cli, text, ocharskip);
        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, ocharskip);
        break;

      not_option:
      case Clp_NotOption:
        cli->is_short = 0;
        clp->have_val = 1;
        clp->vstr = 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 ocharskip, oclass = get_oclass(clp, text, &ocharskip);
    assert(cli->could_be_short);
    cli->is_short = 1;
    cli->whole_negated = !!(oclass & Clp_ShortNegated);
    set_option_text(cli, cli->argv[0], ocharskip);
}


static int
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_val = 1 and clp->vstr to that argument. Sets cli->ambiguous
        to 1 iff there was no match because the argument was ambiguous. */
{
    Clp_Internal *cli = clp->internal;
    int optno, len, lmm;
    const Clp_Option *opt = cli->opt;
    const Clp_InternOption *iopt;
    int first_negative_ambiguous;

    /* Look for a normal option. */
    optno = find_prefix_opt
        (clp, arg, cli->nopt, opt, cli->iopt,
         &cli->ambiguous, cli->ambiguous_values);
    if (optno >= 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;
        optno = find_prefix_opt
            (clp, arg, cli->nopt, opt, cli->iopt,
             &cli->ambiguous, cli->ambiguous_values);
        if (optno >= 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 -1;

  worked:
    iopt = &cli->iopt[optno];
    lmm = (clp->negated ? iopt->lmmneg : iopt->lmmpos);
    if (cli->could_be_short
        && (clp->negated ? iopt->lmmneg_short : iopt->lmmpos_short)) {
        int first_charlen = clp_utf8_charlen(cli, arg);
        lmm = (first_charlen >= lmm ? first_charlen + 1 : lmm);
    }
    len = argcmp(opt[optno].long_name + iopt->ilongoff, arg, lmm, 1);
    assert(len > 0);
    if (arg[len] == '=') {
        clp->have_val = 1;
        clp->vstr = arg + len + 1;
    }
    return optno;
}


static int
find_short(Clp_Parser *clp, const char *text)
     /* If short_name corresponds to one of clp's options, returns it. */
{
    Clp_Internal *cli = clp->internal;
    const Clp_Option *opt = cli->opt;
    const Clp_InternOption *iopt = cli->iopt;
    int i, c;
    if (clp->internal->utf8)
        c = decode_utf8(text, 0);
    else
        c = (unsigned char) *text;

    for (i = 0; i < cli->nopt; i++)
        if (iopt[i].ishort && opt[i].short_name == c
            && (!clp->negated || iopt[i].ineg)) {
            clp->negated = clp->negated || !iopt[i].ipos;
            return i;
        }

    return -1;
}


/** @param clp the parser
 * @return option ID of next option
 *
 * Parse the next argument from the argument list, store information about
 * that argument in the fields of @a clp, and return the option's ID.
 *
 * If an argument was successfully parsed, that option's ID is returned.
 * Other possible return values are:
 *
 * <dl>
 * <dt>Clp_Done</dt>
 * <dd>There are no more arguments.</dd>
 * <dt>Clp_NotOption</dt>
 * <dd>The next argument was not an option.  The argument's text is @a
 * clp->vstr (and @a clp->val.s).</dd>
 * <dt>Clp_BadOption</dt>
 * <dd>The next argument was a bad option: either an option that wasn't
 * understood, or an option lacking a required value, or an option whose value
 * couldn't be parsed.  The option has been skipped.</dd>
 * <dt>Clp_Error</dt>
 * <dd>There was an internal error.  This should never occur unless a user
 * messes with, for example, a Clp_Option array.</dd>
 * </dl>
 *
 * The fields of @a clp are set as follows.
 *
 * <dl>
 * <dt><tt>negated</tt></dt>
 * <dd>1 if the option was negated, 0 if it wasn't.</dd>
 * <dt><tt>have_val</tt></dt>
 * <dd>1 if the option had a value, 0 if it didn't.  Note that negated options
 * are not allowed to have values.</dd>
 * <dt><tt>vstr</tt></dt>
 * <dd>The value string, if any.  NULL if there was no value.</dd>
 * <dt><tt>val</tt></dt>
 * <dd>An option's value type will parse the value string into this
 * union.</dd>
 * </dl>
 *
 * The parsed argument is shifted off the argument list, so that sequential
 * calls to Clp_Next() step through the argument list.
 */
int
Clp_Next(Clp_Parser *clp)
{
    Clp_Internal *cli = clp->internal;
    int optno;
    const Clp_Option *opt;
    Clp_ParserState clpsave;
    int vtpos, complain;

    /* Set up clp */
    cli->current_option = -1;
    cli->ambiguous = 0;

    /* Get the next argument or option */
    if (!next_argument(clp, cli->option_processing ? 0 : 2)) {
        clp->val.s = clp->vstr;
        optno = clp->have_val ? Clp_NotOption : Clp_Done;
        clp->option = &clp_option_sentinel[-optno];
        return optno;
    }

    clp->negated = cli->whole_negated;
    if (cli->is_short)
        optno = find_short(clp, cli->xtext);
    else
        optno = find_long(clp, cli->xtext);

    /* If there's ambiguity between long & short options, and we couldn't
       find a long option, look for a short option */
    if (optno < 0 && cli->could_be_short) {
        switch_to_short_argument(clp);
        optno = find_short(clp, cli->xtext);
    }

    /* If we didn't find an option... */
    if (optno < 0 || (clp->negated && !cli->iopt[optno].ineg)) {
        /* 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->iopt, cli->option_chars,
                            "option %<%s%s%> is ambiguous",
                            cli->option_chars, cli->xtext);
        else if (cli->is_short && !cli->could_be_short)
            Clp_OptionError(clp, "unrecognized option %<%s%C%>",
                            cli->option_chars, cli->xtext);
        else
            Clp_OptionError(clp, "unrecognized option %<%s%s%>",
                            cli->option_chars, cli->xtext);

        clp->option = &clp_option_sentinel[-Clp_BadOption];
        return Clp_BadOption;
    }

    /* Set the current option */
    cli->current_option = optno;
    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
        || (!cli->iopt[optno].imandatory && !cli->iopt[optno].ioptional)) {
        if (clp->have_val) {
            Clp_OptionError(clp, "%<%O%> can%,t take an argument");
            clp->option = &clp_option_sentinel[-Clp_BadOption];
            return Clp_BadOption;
        } else {
            clp->option = &cli->opt[optno];
            return cli->opt[optno].option_id;
        }
    }

    /* Get an argument if we need one, or if it's optional */
    /* Sanity-check the argument type. */
    opt = &cli->opt[optno];
    if (opt->val_type <= 0) {
        clp->option = &clp_option_sentinel[-Clp_Error];
        return Clp_Error;
    }
    vtpos = val_type_binsearch(cli, opt->val_type);
    if (vtpos == cli->nvaltype
        || cli->valtype[vtpos].val_type != opt->val_type) {
        clp->option = &clp_option_sentinel[-Clp_Error];
        return Clp_Error;
    }

    /* complain == 1 only if the argument was explicitly given,
       or it is mandatory. */
    complain = (clp->have_val != 0) || cli->iopt[optno].imandatory;
    Clp_SaveParser(clp, &clpsave);

    if (cli->iopt[optno].imandatory && !clp->have_val) {
        /* 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 = (cli->valtype[vtpos].flags & Clp_DisallowOptions) != 0;
        next_argument(clp, disallow ? 1 : 2);
        if (!clp->have_val) {
            int got_option = cli->xtext != 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");
            clp->option = &clp_option_sentinel[-Clp_BadOption];
            return Clp_BadOption;
        }

    } else if (cli->is_short && !clp->have_val
               && cli->xtext[clp_utf8_charlen(cli, cli->xtext)])
        /* The -[option]argument case:
           Assume that the rest of the current string is the argument. */
        next_argument(clp, 1);

    /* Parse the argument */
    clp->option = opt;
    if (clp->have_val) {
        Clp_ValType *atr = &cli->valtype[vtpos];
        if (atr->func(clp, clp->vstr, complain, atr->user_data) <= 0) {
            /* parser failed */
            clp->have_val = 0;
            if (complain) {
                clp->option = &clp_option_sentinel[-Clp_BadOption];
                return Clp_BadOption;
            } else {
                Clp_RestoreParser(clp, &clpsave);
                clp->option = opt;
            }
        }
    }

    return opt->option_id;
}


/** @param clp the parser
 * @param allow_options whether options will be allowed
 *
 * Remove and return the next argument from @a clp's argument array.  If there
 * are no arguments left, or if the next argument is an option and @a
 * allow_options != 0, then returns null.
 */
const char *
Clp_Shift(Clp_Parser *clp, int allow_options)
     /* 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_options ? 2 : 1);
    if (!clp->have_val)
        Clp_RestoreParser(clp, &clpsave);
    return clp->vstr;
}


/*******
 * Clp_OptionError
 **/

typedef struct Clp_BuildString {
    char* data;
    char* pos;
    char* end_data;
    char buf[256];
} Clp_BuildString;

static void build_string_program_prefix(Clp_BuildString* bs,
                                        const Clp_Parser* clp);

static void build_string_init(Clp_BuildString* bs, Clp_Parser* clp) {
    bs->data = bs->pos = bs->buf;
    bs->end_data = &bs->buf[sizeof(bs->buf)];
    if (clp)
        build_string_program_prefix(bs, clp);
}

static void build_string_cleanup(Clp_BuildString* bs) {
    if (bs->data != bs->buf)
        free(bs->data);
}

static int build_string_grow(Clp_BuildString* bs, size_t want) {
    size_t ipos = bs->pos - bs->data, ncap;
    if (!bs->pos)
        return 0;
    for (ncap = (bs->end_data - bs->data) << 1; ncap < want; ncap *= 2)
        /* nada */;
    if (bs->data == bs->buf) {
        if ((bs->data = (char*) malloc(ncap)))
            memcpy(bs->data, bs->buf, bs->pos - bs->buf);
    } else
        bs->data = (char*) realloc(bs->data, ncap);
    if (!bs->data) {
        bs->pos = bs->end_data = bs->data;
        return 0;
    } else {
        bs->pos = bs->data + ipos;
        bs->end_data = bs->data + ncap;
        return 1;
    }
}

#define ENSURE_BUILD_STRING(bs, space)                                  \
    ((((bs)->end_data - (bs)->pos) >= (space))                          \
     || build_string_grow((bs), (bs)->pos - (bs)->data + (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 void
build_string_program_prefix(Clp_BuildString* bs, const Clp_Parser* clp)
{
    const Clp_Internal* cli = clp->internal;
    if (cli->program_name && cli->program_name[0]) {
        append_build_string(bs, cli->program_name, -1);
        append_build_string(bs, ": ", 2);
    }
}


static void
Clp_vbsprintf(Clp_Parser *clp, Clp_BuildString *bs,
              const char *fmt, va_list val)
{
    Clp_Internal *cli = clp->internal;
    const char *percent;
    int c;

    for (percent = strchr(fmt, '%'); percent; percent = strchr(fmt, '%')) {
        append_build_string(bs, fmt, percent - fmt);
        switch (*++percent) {

          case 's': {
              const char *s = va_arg(val, const char *);
              append_build_string(bs, s ? s : "(null)", -1);
              break;
          }

          case 'C': {
              const char *s = va_arg(val, const char *);
              if (cli->utf8)
                  c = decode_utf8(s, 0);
              else
                  c = (unsigned char) *s;
              goto char_c;
          }

          case 'c':
            c = va_arg(val, int);
            goto char_c;

          char_c:
            if (ENSURE_BUILD_STRING(bs, 5)) {
                if (c >= 32 && c <= 126)
                    *bs->pos++ = c;
                else if (c < 32) {
                    *bs->pos++ = '^';
                    *bs->pos++ = c + 64;
                } else if (cli->utf8 && c >= 127 && c < 0x110000) {
                    bs->pos = encode_utf8(bs->pos, 4, c);
                } else if (c >= 127 && c <= 255) {
                    snprintf(bs->pos, 5, "\\%03o", c & 0xFF);
                    bs->pos += 4;
                } else {
                    *bs->pos++ = '\\';
                    *bs->pos++ = '?';
                }
            }
            break;

          case 'd': {
              int d = va_arg(val, int);
              if (ENSURE_BUILD_STRING(bs, 32)) {
                  bs->pos += snprintf(bs->pos, 32, "%d", d);
              }
              break;
          }

        case 'O':
        case 'V': {
            int optno = cli->current_option;
            const Clp_Option *opt = &cli->opt[optno];
            if (optno < 0)
                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, 5)) {
                    if (cli->utf8)
                        bs->pos = encode_utf8(bs->pos, 5, opt->short_name);
                    else
                        *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 + cli->iopt[optno].ilongoff, -1);
            } else {
                append_build_string(bs, cli->option_chars, -1);
                append_build_string(bs, opt->long_name + cli->iopt[optno].ilongoff, -1);
            }
            if (optno >= 0 && clp->have_val && *percent == 'V') {
                if (cli->current_short && !cli->iopt[optno].ioptional)
                    append_build_string(bs, " ", 1);
                else if (!cli->current_short)
                    append_build_string(bs, "=", 1);
                append_build_string(bs, clp->vstr, -1);
            }
            break;
        }

          case '%':
            if (ENSURE_BUILD_STRING(bs, 1))
                *bs->pos++ = '%';
            break;

          case '<':
            append_build_string(bs, (cli->utf8 ? "\342\200\230" : "'"), -1);
            break;

          case ',':
          case '>':
            append_build_string(bs, (cli->utf8 ? "\342\200\231" : "'"), -1);
            break;

        case 0:
            append_build_string(bs, "%", 1);
            goto done;

          default:
            if (ENSURE_BUILD_STRING(bs, 2)) {
                *bs->pos++ = '%';
                *bs->pos++ = *percent;
            }
            break;

        }
        fmt = ++percent;
    }

 done:
    append_build_string(bs, fmt, -1);
}

static const char* build_string_text(Clp_BuildString* bs, int report_oom) {
    if (bs->pos) {
        *bs->pos = 0;
        return bs->data;
    } else if (report_oom)
        return "out of memory\n";
    else
        return NULL;
}

static void
do_error(Clp_Parser *clp, Clp_BuildString *bs)
{
    const char *text = build_string_text(bs, 1);
    if (clp->internal->error_handler != 0)
        (*clp->internal->error_handler)(clp, text);
    else
        fputs(text, stderr);
}

/** @param clp the parser
 * @param format error format
 *
 * Format an error message from @a format and any additional arguments in
 * the ellipsis. The resulting error string is then printed to standard
 * error (or passed to the error handler specified by Clp_SetErrorHandler).
 * Returns the number of characters printed.
 *
 * The following format characters are accepted:
 *
 * <dl>
 * <dt><tt>%</tt><tt>c</tt></dt>
 * <dd>A character (type <tt>int</tt>).  Control characters are printed in
 * caret notation.  If the parser is in UTF-8 mode, the character is formatted
 * in UTF-8.  Otherwise, special characters are printed with backslashes and
 * octal notation.</dd>
 * <dt><tt>%</tt><tt>s</tt></dt>
 * <dd>A string (type <tt>const char *</tt>).</dd>
 * <dt><tt>%</tt><tt>C</tt></dt>
 * <dd>The argument is a string (type <tt>const char *</tt>).  The first
 * character in this string is printed.  If the parser is in UTF-8 mode, this
 * may involve multiple bytes.</dd>
 * <dt><tt>%</tt><tt>d</tt></dt>
 * <dd>An integer (type <tt>int</tt>).  Printed in decimal.</dd>
 * <dt><tt>%</tt><tt>O</tt></dt>
 * <dd>The current option.  No values are read from the argument list; the
 * current option is defined in the Clp_Parser object itself.</dd>
 * <dt><tt>%</tt><tt>V</tt></dt>
 * <dd>Like <tt>%</tt><tt>O</tt>, but also includes the current value,
 * if any.</dd>
 * <dt><tt>%%</tt></dt>
 * <dd>Prints a percent character.</dd>
 * <dt><tt>%</tt><tt>&lt;</tt></dt>
 * <dd>Prints an open quote string.  In UTF-8 mode, prints a left single
 * quote.  Otherwise prints a single quote.</dd>
 * <dt><tt>%</tt><tt>&gt;</tt></dt>
 * <dd>Prints a closing quote string.  In UTF-8 mode, prints a right single
 * quote.  Otherwise prints a single quote.</dd>
 * <dt><tt>%</tt><tt>,</tt></dt>
 * <dd>Prints an apostrophe.  In UTF-8 mode, prints a right single quote.
 * Otherwise prints a single quote.</dd>
 * </dl>
 *
 * Note that no flag characters, precision, or field width characters are
 * currently supported.
 *
 * @sa Clp_SetErrorHandler
 */
int
Clp_OptionError(Clp_Parser *clp, const char *format, ...)
{
    Clp_BuildString bs;
    va_list val;
    va_start(val, format);
    build_string_init(&bs, clp);
    Clp_vbsprintf(clp, &bs, format, val);
    append_build_string(&bs, "\n", 1);
    va_end(val);
    do_error(clp, &bs);
    build_string_cleanup(&bs);
    return bs.pos - bs.data;
}

/** @param clp the parser
 * @param f output file
 * @param format error format
 *
 * Format an error message using @a format and additional arguments in the
 * ellipsis, according to the Clp_OptionError formatting conventions. The
 * resulting message is written to @a f.
 *
 * @sa Clp_OptionError */
int
Clp_fprintf(Clp_Parser* clp, FILE* f, const char* format, ...)
{
    Clp_BuildString bs;
    va_list val;
    va_start(val, format);
    build_string_init(&bs, NULL);
    Clp_vbsprintf(clp, &bs, format, val);
    va_end(val);
    if (bs.pos != bs.data)
        fwrite(bs.data, 1, bs.pos - bs.data, f);
    build_string_cleanup(&bs);
    return bs.pos - bs.data;
}

/** @param clp the parser
 * @param f output file
 * @param format error format
 * @param val arguments
 *
 * Format an error message using @a format and @a val, according to the
 * Clp_OptionError formatting conventions. The resulting message is written
 * to @a f.
 *
 * @sa Clp_OptionError */
int
Clp_vfprintf(Clp_Parser* clp, FILE* f, const char* format, va_list val)
{
    Clp_BuildString bs;
    build_string_init(&bs, NULL);
    Clp_vbsprintf(clp, &bs, format, val);
    if (bs.pos != bs.data)
        fwrite(bs.data, 1, bs.pos - bs.data, f);
    build_string_cleanup(&bs);
    return bs.pos - bs.data;
}

/** @param clp the parser
 * @param str output string
 * @param size size of output string
 * @param format error format
 *
 * Format an error message from @a format and any additional arguments in
 * the ellipsis, according to the Clp_OptionError formatting conventions.
 * The resulting string is written to @a str. At most @a size characters are
 * written to @a str, including a terminating null byte. The return value is
 * the number of characters that would have been written (excluding the
 * terminating null byte) if @a size were large enough to contain the entire
 * string.
 *
 * @sa Clp_OptionError */
int
Clp_vsnprintf(Clp_Parser* clp, char* str, size_t size,
              const char* format, va_list val)
{
    Clp_BuildString bs;
    build_string_init(&bs, NULL);
    Clp_vbsprintf(clp, &bs, format, val);
    if ((size_t) (bs.pos - bs.data) < size) {
        memcpy(str, bs.data, bs.pos - bs.data);
        str[bs.pos - bs.data] = 0;
    } else {
        memcpy(str, bs.data, size - 1);
        str[size - 1] = 0;
    }
    build_string_cleanup(&bs);
    return bs.pos - bs.data;
}

static int
ambiguity_error(Clp_Parser *clp, int ambiguous, int *ambiguous_values,
                const Clp_Option *opt, const Clp_InternOption *iopt,
                const char *prefix, const char *fmt, ...)
{
    Clp_Internal *cli = clp->internal;
    Clp_BuildString bs;
    int i;
    va_list val;

    va_start(val, fmt);
    build_string_init(&bs, clp);
    Clp_vbsprintf(clp, &bs, fmt, val);
    append_build_string(&bs, "\n", 1);

    build_string_program_prefix(&bs, clp);
    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);
        if (!iopt[value].unquoted)
            append_build_string(&bs, (cli->utf8 ? "\342\200\230" : "'"), -1);
        append_build_string(&bs, prefix, -1);
        append_build_string(&bs, no_dash, -1);
        append_build_string(&bs, opt[value].long_name + iopt[value].ilongoff, -1);
        if (!iopt[value].unquoted)
            append_build_string(&bs, (cli->utf8 ? "\342\200\231" : "'"), -1);
    }

    if (ambiguous > MAX_AMBIGUOUS_VALUES)
        append_build_string(&bs, ", and others", -1);
    append_build_string(&bs, ".)\n", -1);
    va_end(val);

    do_error(clp, &bs);
    build_string_cleanup(&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;
}

/** @param clp the parser
 * @param buf output buffer
 * @param len length of output buffer
 * @return number of characters written to the buffer, not including the terminating NUL
 *
 * A string that looks like the last option parsed by @a clp is extracted into
 * @a buf.  The correct option characters are put into the string first,
 * followed by the option text.  The output buffer is null-terminated unless
 * @a len == 0.
 *
 * @sa Clp_CurOptionName
 */
int
Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int len)
{
    Clp_Internal *cli = clp->internal;
    int optno = cli->current_option;
    int pos = 0;
    if (optno < 0)
        pos += copy_string(buf, len, pos, "(no current option!)");
    else if (cli->current_short) {
        pos += copy_string(buf, len, pos, cli->option_chars);
        if (cli->utf8)
            pos = (encode_utf8(buf + pos, len - pos - 1, cli->opt[optno].short_name) - buf);
        else if (pos < len - 1)
            buf[pos++] = cli->opt[optno].short_name;
    } else if (cli->negated_by_no) {
        pos += copy_string(buf, len, pos, cli->option_chars);
        pos += copy_string(buf, len, pos, "no-");
        pos += copy_string(buf, len, pos, cli->opt[optno].long_name + cli->iopt[optno].ilongoff);
    } else {
        pos += copy_string(buf, len, pos, cli->option_chars);
        pos += copy_string(buf, len, pos, cli->opt[optno].long_name + cli->iopt[optno].ilongoff);
    }
    if (pos < len)
        buf[pos] = 0;
    return pos;
}

/** @param clp the parser
 * @return string describing the current option
 *
 * This function acts like Clp_CurOptionNameBuf(), but returns a pointer into
 * a static buffer that will be rewritten on the next call to
 * Clp_CurOptionName().
 *
 * @note This function is not thread safe.
 *
 * @sa Clp_CurOptionName
 */
const char *
Clp_CurOptionName(Clp_Parser *clp)
{
    static char buf[256];
    Clp_CurOptionNameBuf(clp, buf, 256);
    return buf;
}

int
Clp_IsLong(Clp_Parser *clp, const char *long_name)
{
    Clp_Internal *cli = clp->internal;
    int optno = cli->current_option;
    return optno >= 0 && strcmp(cli->opt[optno].long_name, long_name) == 0;
}

int
Clp_IsShort(Clp_Parser *clp, int short_name)
{
    Clp_Internal *cli = clp->internal;
    int optno = cli->current_option;
    return optno >= 0 && cli->opt[optno].short_name == short_name;
}

#ifdef __cplusplus
}
#endif
