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

#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>
#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 !HAVE_STRTOUL && !defined(HAVE_CONFIG_H)
# define HAVE_STRTOUL 1
#endif
#if !HAVE_UINTPTR_T && !HAVE_INTTYPES_H && 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 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 = 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].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;
    Clp_StringList *clsl = (Clp_StringList *)malloc(sizeof(Clp_StringList));
    Clp_InternOption *iopt = (Clp_InternOption *)malloc(sizeof(Clp_InternOption) * nitems);
    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;

    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++) {
	iopt[i].ilong = iopt[i].ipos = 1;
	iopt[i].ishort = iopt[i].ineg = iopt[i].ilongoff = iopt[i].iprefmatch = 0;
    }
    calculate_lmm(clp, items, iopt, 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 ? 1 : 0);
    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, 4)) {
		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) {
		    sprintf(bs->pos, "\\%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)) {
		  sprintf(bs->pos, "%d", d);
		  bs->pos = strchr(bs->pos, 0);
	      }
	      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 '`':		/* backwards compatibility */
	  case '<':
	    append_build_string(bs, (cli->utf8 ? "\342\200\230" : "'"), -1);
	    break;

	  case '\'':		/* backwards compatibility */
	  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);
	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);
	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
