pinentry-dmenu

a pinentry program based on dmenu
git clone anongit@rnpnr.xyz:pinentry-dmenu.git
Log | Files | Refs | Feed | README | LICENSE

argparse.c (45832B)


      1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
      2  * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
      3  * Copyright (C) 1997-2001, 2006-2008, 2013-2015 Werner Koch
      4  *
      5  * This file is part of JNLIB, which is a subsystem of GnuPG.
      6  *
      7  * JNLIB is free software; you can redistribute it and/or modify it
      8  * under the terms of either
      9  *
     10  *   - the GNU Lesser General Public License as published by the Free
     11  *     Software Foundation; either version 3 of the License, or (at
     12  *     your option) any later version.
     13  *
     14  * or
     15  *
     16  *   - the GNU General Public License as published by the Free
     17  *     Software Foundation; either version 2 of the License, or (at
     18  *     your option) any later version.
     19  *
     20  * or both in parallel, as here.
     21  *
     22  * JNLIB is distributed in the hope that it will be useful, but
     23  * WITHOUT ANY WARRANTY; without even the implied warranty of
     24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     25  * General Public License for more details.
     26  *
     27  * You should have received a copies of the GNU General Public License
     28  * and the GNU Lesser General Public License along with this program;
     29  * if not, see <http://www.gnu.org/licenses/>.
     30  */
     31 
     32 /* This file may be used as part of GnuPG or standalone.  A GnuPG
     33    build is detected by the presence of the macro GNUPG_MAJOR_VERSION.
     34    Some feature are only availalbe in the GnuPG build mode.
     35  */
     36 
     37 #ifdef HAVE_CONFIG_H
     38 #include <config.h>
     39 #endif
     40 
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <ctype.h>
     44 #include <string.h>
     45 #include <stdarg.h>
     46 #include <limits.h>
     47 #include <errno.h>
     48 
     49 #ifdef GNUPG_MAJOR_VERSION
     50 # include "libjnlib-config.h"
     51 # include "mischelp.h"
     52 # include "stringhelp.h"
     53 # include "logging.h"
     54 # ifdef JNLIB_NEED_UTF8CONV
     55 #  include "utf8conv.h"
     56 # endif
     57 #endif /*GNUPG_MAJOR_VERSION*/
     58 
     59 #include "argparse.h"
     60 
     61 /* GnuPG uses GPLv3+ but a standalone version of this defaults to
     62    GPLv2+ because that is the license of this file.  Change this if
     63    you include it in a program which uses GPLv3.  If you don't want to
     64    set a a copyright string for your usage() you may also hardcode it
     65    here.  */
     66 #ifndef GNUPG_MAJOR_VERSION
     67 
     68 # define ARGPARSE_GPL_VERSION      2
     69 # define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME"
     70 
     71 #else /* Used by GnuPG  */
     72 
     73 # define ARGPARSE_GPL_VERSION      3
     74 # define ARGPARSE_CRIGHT_STR "Copyright (C) 2015 Free Software Foundation, Inc."
     75 
     76 #endif /*GNUPG_MAJOR_VERSION*/
     77 
     78 /* Replacements for standalone builds.  */
     79 #ifndef GNUPG_MAJOR_VERSION
     80 # ifndef _
     81 #  define _(a)  (a)
     82 # endif
     83 # ifndef DIM
     84 #  define DIM(v)           (sizeof(v)/sizeof((v)[0]))
     85 # endif
     86 # define jnlib_malloc(a)    malloc ((a))
     87 # define jnlib_realloc(a,b) realloc ((a), (b))
     88 # define jnlib_strdup(a)    strdup ((a))
     89 # define jnlib_free(a)      free ((a))
     90 # define jnlib_log_error    my_log_error
     91 # define jnlib_log_bug	    my_log_bug
     92 # define trim_spaces(a)     my_trim_spaces ((a))
     93 # define map_static_macro_string(a)  (a)
     94 #endif /*!GNUPG_MAJOR_VERSION*/
     95 
     96 
     97 #define ARGPARSE_STR(v) #v
     98 #define ARGPARSE_STR2(v) ARGPARSE_STR(v)
     99 
    100 
    101 /* Replacements for standalone builds.  */
    102 #ifndef GNUPG_MAJOR_VERSION
    103 static void
    104 my_log_error (const char *fmt, ...)
    105 {
    106   va_list arg_ptr ;
    107 
    108   va_start (arg_ptr, fmt);
    109   fprintf (stderr, "%s: ", strusage (11));
    110   vfprintf (stderr, fmt, arg_ptr);
    111   va_end (arg_ptr);
    112 }
    113 
    114 static void
    115 my_log_bug (const char *fmt, ...)
    116 {
    117   va_list arg_ptr ;
    118 
    119   va_start (arg_ptr, fmt);
    120   fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
    121   vfprintf (stderr, fmt, arg_ptr);
    122   va_end (arg_ptr);
    123   abort ();
    124 }
    125 
    126 static char *
    127 my_trim_spaces (char *str)
    128 {
    129   char *string, *p, *mark;
    130 
    131   string = str;
    132   /* Find first non space character. */
    133   for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
    134     ;
    135   /* Move characters. */
    136   for ((mark = NULL); (*string = *p); string++, p++)
    137     if (isspace (*(unsigned char*)p))
    138       {
    139         if (!mark)
    140           mark = string;
    141       }
    142     else
    143       mark = NULL;
    144   if (mark)
    145     *mark = '\0' ;  /* Remove trailing spaces. */
    146 
    147   return str ;
    148 }
    149 
    150 #endif /*!GNUPG_MAJOR_VERSION*/
    151 
    152 
    153 
    154 /*********************************
    155  * @Summary arg_parse
    156  *  #include "argparse.h"
    157  *
    158  *  typedef struct {
    159  *	char *argc;		  pointer to argc (value subject to change)
    160  *	char ***argv;		  pointer to argv (value subject to change)
    161  *	unsigned flags; 	  Global flags (DO NOT CHANGE)
    162  *	int err;		  print error about last option
    163  *				  1 = warning, 2 = abort
    164  *	int r_opt;		  return option
    165  *	int r_type;		  type of return value (0 = no argument found)
    166  *	union {
    167  *	    int   ret_int;
    168  *	    long  ret_long
    169  *	    ulong ret_ulong;
    170  *	    char *ret_str;
    171  *	} r;			  Return values
    172  *	struct {
    173  *	    int idx;
    174  *	    const char *last;
    175  *	    void *aliases;
    176  *	} internal;		  DO NOT CHANGE
    177  *  } ARGPARSE_ARGS;
    178  *
    179  *  typedef struct {
    180  *	int	    short_opt;
    181  *	const char *long_opt;
    182  *	unsigned flags;
    183  *  } ARGPARSE_OPTS;
    184  *
    185  *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
    186  *
    187  * @Description
    188  *  This is my replacement for getopt(). See the example for a typical usage.
    189  *  Global flags are:
    190  *     Bit 0 : Do not remove options form argv
    191  *     Bit 1 : Do not stop at last option but return other args
    192  *	       with r_opt set to -1.
    193  *     Bit 2 : Assume options and real args are mixed.
    194  *     Bit 3 : Do not use -- to stop option processing.
    195  *     Bit 4 : Do not skip the first arg.
    196  *     Bit 5 : allow usage of long option with only one dash
    197  *     Bit 6 : ignore --version
    198  *     all other bits must be set to zero, this value is modified by the
    199  *     function, so assume this is write only.
    200  *  Local flags (for each option):
    201  *     Bit 2-0 : 0 = does not take an argument
    202  *		 1 = takes int argument
    203  *		 2 = takes string argument
    204  *		 3 = takes long argument
    205  *		 4 = takes ulong argument
    206  *     Bit 3 : argument is optional (r_type will the be set to 0)
    207  *     Bit 4 : allow 0x etc. prefixed values.
    208  *     Bit 6 : Ignore this option
    209  *     Bit 7 : This is a command and not an option
    210  *  You stop the option processing by setting opts to NULL, the function will
    211  *  then return 0.
    212  * @Return Value
    213  *   Returns the args.r_opt or 0 if ready
    214  *   r_opt may be -2/-7 to indicate an unknown option/command.
    215  * @See Also
    216  *   ArgExpand
    217  * @Notes
    218  *  You do not need to process the options 'h', '--help' or '--version'
    219  *  because this function includes standard help processing; but if you
    220  *  specify '-h', '--help' or '--version' you have to do it yourself.
    221  *  The option '--' stops argument processing; if bit 1 is set the function
    222  *  continues to return normal arguments.
    223  *  To process float args or unsigned args you must use a string args and do
    224  *  the conversion yourself.
    225  * @Example
    226  *
    227  *     ARGPARSE_OPTS opts[] = {
    228  *     { 'v', "verbose",   0 },
    229  *     { 'd', "debug",     0 },
    230  *     { 'o', "output",    2 },
    231  *     { 'c', "cross-ref", 2|8 },
    232  *     { 'm', "my-option", 1|8 },
    233  *     { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
    234  *     { 500, "have-no-short-option-for-this-long-option", 0 },
    235  *     {0} };
    236  *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
    237  *
    238  *     while( ArgParse( &pargs, &opts) ) {
    239  *	   switch( pargs.r_opt ) {
    240  *	     case 'v': opt.verbose++; break;
    241  *	     case 'd': opt.debug++; break;
    242  *	     case 'o': opt.outfile = pargs.r.ret_str; break;
    243  *	     case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
    244  *	     case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
    245  *	     case 500: opt.a_long_one++;  break
    246  *	     default : pargs.err = 1; break; -- force warning output --
    247  *	   }
    248  *     }
    249  *     if( argc > 1 )
    250  *	   log_fatal( "Too many args");
    251  *
    252  */
    253 
    254 typedef struct alias_def_s *ALIAS_DEF;
    255 struct alias_def_s {
    256     ALIAS_DEF next;
    257     char *name;   /* malloced buffer with name, \0, value */
    258     const char *value; /* ptr into name */
    259 };
    260 
    261 
    262 /* Object to store the names for the --ignore-invalid-option option.
    263    This is a simple linked list.  */
    264 typedef struct iio_item_def_s *IIO_ITEM_DEF;
    265 struct iio_item_def_s
    266 {
    267   IIO_ITEM_DEF next;
    268   char name[1];      /* String with the long option name.  */
    269 };
    270 
    271 static const char *(*strusage_handler)( int ) = NULL;
    272 static int (*custom_outfnc) (int, const char *);
    273 
    274 static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
    275 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
    276 static void show_version(void);
    277 static int writestrings (int is_error, const char *string, ...)
    278 #if __GNUC__ >= 4
    279   __attribute__ ((sentinel(0)))
    280 #endif
    281   ;
    282 
    283 
    284 void
    285 argparse_register_outfnc (int (*fnc)(int, const char *))
    286 {
    287   custom_outfnc = fnc;
    288 }
    289 
    290 
    291 /* Write STRING and all following const char * arguments either to
    292    stdout or, if IS_ERROR is set, to stderr.  The list of strings must
    293    be terminated by a NULL.  */
    294 static int
    295 writestrings (int is_error, const char *string, ...)
    296 {
    297   va_list arg_ptr;
    298   const char *s;
    299   int count = 0;
    300 
    301   if (string)
    302     {
    303       s = string;
    304       va_start (arg_ptr, string);
    305       do
    306         {
    307           if (custom_outfnc)
    308             custom_outfnc (is_error? 2:1, s);
    309           else
    310             fputs (s, is_error? stderr : stdout);
    311           count += strlen (s);
    312         }
    313       while ((s = va_arg (arg_ptr, const char *)));
    314       va_end (arg_ptr);
    315     }
    316   return count;
    317 }
    318 
    319 
    320 static void
    321 flushstrings (int is_error)
    322 {
    323   if (custom_outfnc)
    324     custom_outfnc (is_error? 2:1, NULL);
    325   else
    326     fflush (is_error? stderr : stdout);
    327 }
    328 
    329 
    330 static void
    331 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
    332 {
    333   if( !(arg->flags & (1<<15)) )
    334     {
    335       /* Initialize this instance. */
    336       arg->internal.idx = 0;
    337       arg->internal.last = NULL;
    338       arg->internal.inarg = 0;
    339       arg->internal.stopped = 0;
    340       arg->internal.aliases = NULL;
    341       arg->internal.cur_alias = NULL;
    342       arg->internal.iio_list = NULL;
    343       arg->err = 0;
    344       arg->flags |= 1<<15; /* Mark as initialized.  */
    345       if ( *arg->argc < 0 )
    346         jnlib_log_bug ("invalid argument for arg_parse\n");
    347     }
    348 
    349 
    350   if (arg->err)
    351     {
    352       /* Last option was erroneous.  */
    353       const char *s;
    354 
    355       if (filename)
    356         {
    357           if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
    358             s = _("argument not expected");
    359           else if ( arg->r_opt == ARGPARSE_READ_ERROR )
    360             s = _("read error");
    361           else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
    362             s = _("keyword too long");
    363           else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
    364             s = _("missing argument");
    365           else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
    366             s = _("invalid argument");
    367           else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
    368             s = _("invalid command");
    369           else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
    370             s = _("invalid alias definition");
    371           else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
    372             s = _("out of core");
    373           else
    374             s = _("invalid option");
    375           jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
    376 	}
    377       else
    378         {
    379           s = arg->internal.last? arg->internal.last:"[??]";
    380 
    381           if ( arg->r_opt == ARGPARSE_MISSING_ARG )
    382             jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s);
    383           else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
    384             jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s);
    385           else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
    386             jnlib_log_error (_("option \"%.50s\" does not expect an "
    387                                "argument\n"), s );
    388           else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
    389             jnlib_log_error (_("invalid command \"%.50s\"\n"), s);
    390           else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
    391             jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s);
    392           else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
    393             jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s );
    394           else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
    395             jnlib_log_error ("%s\n", _("out of core\n"));
    396           else
    397             jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
    398 	}
    399       if (arg->err != ARGPARSE_PRINT_WARNING)
    400         exit (2);
    401       arg->err = 0;
    402     }
    403 
    404   /* Zero out the return value union.  */
    405   arg->r.ret_str = NULL;
    406   arg->r.ret_long = 0;
    407 }
    408 
    409 
    410 static void
    411 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
    412 {
    413     /* TODO: replace this dummy function with a rea one
    414      * and fix the probelms IRIX has with (ALIAS_DEV)arg..
    415      * used as lvalue
    416      */
    417   (void)arg;
    418   (void)name;
    419   (void)value;
    420 #if 0
    421     ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
    422     a->name = name;
    423     a->value = value;
    424     a->next = (ALIAS_DEF)arg->internal.aliases;
    425     (ALIAS_DEF)arg->internal.aliases = a;
    426 #endif
    427 }
    428 
    429 
    430 /* Return true if KEYWORD is in the ignore-invalid-option list.  */
    431 static int
    432 ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
    433 {
    434   IIO_ITEM_DEF item = arg->internal.iio_list;
    435 
    436   for (; item; item = item->next)
    437     if (!strcmp (item->name, keyword))
    438       return 1;
    439   return 0;
    440 }
    441 
    442 
    443 /* Add the keywords up to the next LF to the list of to be ignored
    444    options.  After returning FP will either be at EOF or the next
    445    character read wll be the first of a new line.  The function
    446    returns 0 on success or true on malloc failure.  */
    447 static int
    448 ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
    449 {
    450   IIO_ITEM_DEF item;
    451   int c;
    452   char name[100];
    453   int namelen = 0;
    454   int ready = 0;
    455   enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
    456 
    457   while (!ready)
    458     {
    459       c = getc (fp);
    460       if (c == '\n')
    461         ready = 1;
    462       else if (c == EOF)
    463         {
    464           c = '\n';
    465           ready = 1;
    466         }
    467     again:
    468       switch (state)
    469         {
    470         case skipWS:
    471           if (!isascii (c) || !isspace(c))
    472             {
    473               namelen = 0;
    474               state = collectNAME;
    475               goto again;
    476             }
    477           break;
    478 
    479         case collectNAME:
    480           if (isspace (c))
    481             {
    482               state = addNAME;
    483               goto again;
    484             }
    485           else if (namelen < DIM(name)-1)
    486             name[namelen++] = c;
    487           else /* Too long.  */
    488             state = skipNAME;
    489           break;
    490 
    491         case skipNAME:
    492           if (isspace (c))
    493             {
    494               state = skipWS;
    495               goto again;
    496             }
    497           break;
    498 
    499         case addNAME:
    500           name[namelen] = 0;
    501           if (!ignore_invalid_option_p (arg, name))
    502             {
    503               item = jnlib_malloc (sizeof *item + namelen);
    504               if (!item)
    505                 return 1;
    506               strcpy (item->name, name);
    507               item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
    508               arg->internal.iio_list = item;
    509             }
    510           state = skipWS;
    511           goto again;
    512         }
    513     }
    514   return 0;
    515 }
    516 
    517 
    518 /* Clear the entire ignore-invalid-option list.  */
    519 static void
    520 ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
    521 {
    522   IIO_ITEM_DEF item, tmpitem;
    523 
    524   for (item = arg->internal.iio_list; item; item = tmpitem)
    525     {
    526       tmpitem = item->next;
    527       jnlib_free (item);
    528     }
    529   arg->internal.iio_list = NULL;
    530 }
    531 
    532 
    533 
    534 /****************
    535  * Get options from a file.
    536  * Lines starting with '#' are comment lines.
    537  * Syntax is simply a keyword and the argument.
    538  * Valid keywords are all keywords from the long_opt list without
    539  * the leading dashes. The special keywords "help", "warranty" and "version"
    540  * are not valid here.
    541  * The special keyword "alias" may be used to store alias definitions,
    542  * which are later expanded like long options.
    543  * The option
    544  *   ignore-invalid-option OPTIONNAMEs
    545  * is recognized and updates a list of option which should be ignored if they
    546  * are not defined.
    547  * Caller must free returned strings.
    548  * If called with FP set to NULL command line args are parse instead.
    549  *
    550  * Q: Should we allow the syntax
    551  *     keyword = value
    552  *    and accept for boolean options a value of 1/0, yes/no or true/false?
    553  * Note: Abbreviation of options is here not allowed.
    554  */
    555 int
    556 optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
    557 	       ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
    558 {
    559   int state, i, c;
    560   int idx=0;
    561   char keyword[100];
    562   char *buffer = NULL;
    563   size_t buflen = 0;
    564   int in_alias=0;
    565 
    566   if (!fp) /* Divert to to arg_parse() in this case.  */
    567     return arg_parse (arg, opts);
    568 
    569   initialize (arg, filename, lineno);
    570 
    571   /* Find the next keyword.  */
    572   state = i = 0;
    573   for (;;)
    574     {
    575       c = getc (fp);
    576       if (c == '\n' || c== EOF )
    577         {
    578           if ( c != EOF )
    579             ++*lineno;
    580           if (state == -1)
    581             break;
    582           else if (state == 2)
    583             {
    584               keyword[i] = 0;
    585               for (i=0; opts[i].short_opt; i++ )
    586                 {
    587                   if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
    588                     break;
    589                 }
    590               idx = i;
    591               arg->r_opt = opts[idx].short_opt;
    592               if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
    593                 {
    594                   state = i = 0;
    595                   continue;
    596                 }
    597               else if (!opts[idx].short_opt )
    598                 {
    599                   if (!strcmp (keyword, "ignore-invalid-option"))
    600                     {
    601                       /* No argument - ignore this meta option.  */
    602                       state = i = 0;
    603                       continue;
    604                     }
    605                   else if (ignore_invalid_option_p (arg, keyword))
    606                     {
    607                       /* This invalid option is in the iio list.  */
    608                       state = i = 0;
    609                       continue;
    610                     }
    611                   arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
    612                                 ? ARGPARSE_INVALID_COMMAND
    613                                 : ARGPARSE_INVALID_OPTION);
    614                 }
    615               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
    616                 arg->r_type = 0; /* Does not take an arg. */
    617               else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
    618                 arg->r_type = 0; /* Arg is optional.  */
    619               else
    620                 arg->r_opt = ARGPARSE_MISSING_ARG;
    621 
    622               break;
    623 	    }
    624           else if (state == 3)
    625             {
    626               /* No argument found.  */
    627               if (in_alias)
    628                 arg->r_opt = ARGPARSE_MISSING_ARG;
    629               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
    630                 arg->r_type = 0; /* Does not take an arg. */
    631               else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
    632                 arg->r_type = 0; /* No optional argument. */
    633               else
    634                 arg->r_opt = ARGPARSE_MISSING_ARG;
    635 
    636               break;
    637 	    }
    638           else if (state == 4)
    639             {
    640               /* Has an argument. */
    641               if (in_alias)
    642                 {
    643                   if (!buffer)
    644                     arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
    645                   else
    646                     {
    647                       char *p;
    648 
    649                       buffer[i] = 0;
    650                       p = strpbrk (buffer, " \t");
    651                       if (p)
    652                         {
    653                           *p++ = 0;
    654                           trim_spaces (p);
    655 			}
    656                       if (!p || !*p)
    657                         {
    658                           jnlib_free (buffer);
    659                           arg->r_opt = ARGPARSE_INVALID_ALIAS;
    660                         }
    661                       else
    662                         {
    663                           store_alias (arg, buffer, p);
    664                         }
    665 		    }
    666 		}
    667               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
    668                 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
    669               else
    670                 {
    671                   char *p;
    672 
    673                   if (!buffer)
    674                     {
    675                       keyword[i] = 0;
    676                       buffer = jnlib_strdup (keyword);
    677                       if (!buffer)
    678                         arg->r_opt = ARGPARSE_OUT_OF_CORE;
    679 		    }
    680                   else
    681                     buffer[i] = 0;
    682 
    683                   if (buffer)
    684                     {
    685                       trim_spaces (buffer);
    686                       p = buffer;
    687                       if (*p == '"')
    688                         {
    689                           /* Remove quotes. */
    690                           p++;
    691                           if (*p && p[strlen(p)-1] == '\"' )
    692                             p[strlen(p)-1] = 0;
    693                         }
    694                       if (!set_opt_arg (arg, opts[idx].flags, p))
    695                         jnlib_free(buffer);
    696                     }
    697                 }
    698               break;
    699             }
    700           else if (c == EOF)
    701             {
    702               ignore_invalid_option_clear (arg);
    703               if (ferror (fp))
    704                 arg->r_opt = ARGPARSE_READ_ERROR;
    705               else
    706                 arg->r_opt = 0; /* EOF. */
    707               break;
    708             }
    709           state = 0;
    710           i = 0;
    711         }
    712       else if (state == -1)
    713         ; /* Skip. */
    714       else if (state == 0 && isascii (c) && isspace(c))
    715         ; /* Skip leading white space.  */
    716       else if (state == 0 && c == '#' )
    717         state = 1;	/* Start of a comment.  */
    718       else if (state == 1)
    719         ; /* Skip comments. */
    720       else if (state == 2 && isascii (c) && isspace(c))
    721         {
    722           /* Check keyword.  */
    723           keyword[i] = 0;
    724           for (i=0; opts[i].short_opt; i++ )
    725             if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
    726               break;
    727           idx = i;
    728           arg->r_opt = opts[idx].short_opt;
    729           if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
    730             {
    731               state = 1; /* Process like a comment.  */
    732             }
    733           else if (!opts[idx].short_opt)
    734             {
    735               if (!strcmp (keyword, "alias"))
    736                 {
    737                   in_alias = 1;
    738                   state = 3;
    739                 }
    740               else if (!strcmp (keyword, "ignore-invalid-option"))
    741                 {
    742                   if (ignore_invalid_option_add (arg, fp))
    743                     {
    744                       arg->r_opt = ARGPARSE_OUT_OF_CORE;
    745                       break;
    746                     }
    747                   state = i = 0;
    748                   ++*lineno;
    749                 }
    750               else if (ignore_invalid_option_p (arg, keyword))
    751                 state = 1; /* Process like a comment.  */
    752               else
    753                 {
    754                   arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
    755                                 ? ARGPARSE_INVALID_COMMAND
    756                                 : ARGPARSE_INVALID_OPTION);
    757                   state = -1; /* Skip rest of line and leave.  */
    758                 }
    759             }
    760           else
    761             state = 3;
    762         }
    763       else if (state == 3)
    764         {
    765           /* Skip leading spaces of the argument.  */
    766           if (!isascii (c) || !isspace(c))
    767             {
    768               i = 0;
    769               keyword[i++] = c;
    770               state = 4;
    771             }
    772         }
    773       else if (state == 4)
    774         {
    775           /* Collect the argument. */
    776           if (buffer)
    777             {
    778               if (i < buflen-1)
    779                 buffer[i++] = c;
    780               else
    781                 {
    782                   char *tmp;
    783                   size_t tmplen = buflen + 50;
    784 
    785                   tmp = jnlib_realloc (buffer, tmplen);
    786                   if (tmp)
    787                     {
    788                       buflen = tmplen;
    789                       buffer = tmp;
    790                       buffer[i++] = c;
    791                     }
    792                   else
    793                     {
    794                       jnlib_free (buffer);
    795                       arg->r_opt = ARGPARSE_OUT_OF_CORE;
    796                       break;
    797                     }
    798                 }
    799             }
    800           else if (i < DIM(keyword)-1)
    801             keyword[i++] = c;
    802           else
    803             {
    804               size_t tmplen = DIM(keyword) + 50;
    805               buffer = jnlib_malloc (tmplen);
    806               if (buffer)
    807                 {
    808                   buflen = tmplen;
    809                   memcpy(buffer, keyword, i);
    810                   buffer[i++] = c;
    811                 }
    812               else
    813                 {
    814                   arg->r_opt = ARGPARSE_OUT_OF_CORE;
    815                   break;
    816                 }
    817             }
    818         }
    819       else if (i >= DIM(keyword)-1)
    820         {
    821           arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
    822           state = -1; /* Skip rest of line and leave.  */
    823         }
    824       else
    825         {
    826           keyword[i++] = c;
    827           state = 2;
    828         }
    829     }
    830 
    831   return arg->r_opt;
    832 }
    833 
    834 
    835 
    836 static int
    837 find_long_option( ARGPARSE_ARGS *arg,
    838 		  ARGPARSE_OPTS *opts, const char *keyword )
    839 {
    840     int i;
    841     size_t n;
    842 
    843     (void)arg;
    844 
    845     /* Would be better if we can do a binary search, but it is not
    846        possible to reorder our option table because we would mess
    847        up our help strings - What we can do is: Build a nice option
    848        lookup table wehn this function is first invoked */
    849     if( !*keyword )
    850 	return -1;
    851     for(i=0; opts[i].short_opt; i++ )
    852 	if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
    853 	    return i;
    854 #if 0
    855     {
    856 	ALIAS_DEF a;
    857 	/* see whether it is an alias */
    858 	for( a = args->internal.aliases; a; a = a->next ) {
    859 	    if( !strcmp( a->name, keyword) ) {
    860 		/* todo: must parse the alias here */
    861 		args->internal.cur_alias = a;
    862 		return -3; /* alias available */
    863 	    }
    864 	}
    865     }
    866 #endif
    867     /* not found, see whether it is an abbreviation */
    868     /* aliases may not be abbreviated */
    869     n = strlen( keyword );
    870     for(i=0; opts[i].short_opt; i++ ) {
    871 	if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
    872 	    int j;
    873 	    for(j=i+1; opts[j].short_opt; j++ ) {
    874 		if( opts[j].long_opt
    875 		    && !strncmp( opts[j].long_opt, keyword, n ) )
    876 		    return -2;	/* abbreviation is ambiguous */
    877 	    }
    878 	    return i;
    879 	}
    880     }
    881     return -1;  /* Not found.  */
    882 }
    883 
    884 int
    885 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
    886 {
    887   int idx;
    888   int argc;
    889   char **argv;
    890   char *s, *s2;
    891   int i;
    892 
    893   initialize( arg, NULL, NULL );
    894   argc = *arg->argc;
    895   argv = *arg->argv;
    896   idx = arg->internal.idx;
    897 
    898   if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
    899     {
    900       /* Skip the first argument.  */
    901       argc--; argv++; idx++;
    902     }
    903 
    904  next_one:
    905   if (!argc)
    906     {
    907       /* No more args.  */
    908       arg->r_opt = 0;
    909       goto leave; /* Ready. */
    910     }
    911 
    912   s = *argv;
    913   arg->internal.last = s;
    914 
    915   if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
    916     {
    917       arg->r_opt = ARGPARSE_IS_ARG;  /* Not an option but an argument.  */
    918       arg->r_type = 2;
    919       arg->r.ret_str = s;
    920       argc--; argv++; idx++; /* set to next one */
    921     }
    922   else if( arg->internal.stopped )
    923     {
    924       arg->r_opt = 0;
    925       goto leave; /* Ready.  */
    926     }
    927   else if ( *s == '-' && s[1] == '-' )
    928     {
    929       /* Long option.  */
    930       char *argpos;
    931 
    932       arg->internal.inarg = 0;
    933       if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
    934         {
    935           /* Stop option processing.  */
    936           arg->internal.stopped = 1;
    937           arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
    938           argc--; argv++; idx++;
    939           goto next_one;
    940 	}
    941 
    942       argpos = strchr( s+2, '=' );
    943       if ( argpos )
    944         *argpos = 0;
    945       i = find_long_option ( arg, opts, s+2 );
    946       if ( argpos )
    947         *argpos = '=';
    948 
    949       if ( i < 0 && !strcmp ( "help", s+2) )
    950         show_help (opts, arg->flags);
    951       else if ( i < 0 && !strcmp ( "version", s+2) )
    952         {
    953           if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
    954             {
    955               show_version ();
    956               exit(0);
    957             }
    958 	}
    959       else if ( i < 0 && !strcmp( "warranty", s+2))
    960         {
    961           writestrings (0, strusage (16), "\n", NULL);
    962           exit (0);
    963 	}
    964       else if ( i < 0 && !strcmp( "dump-options", s+2) )
    965         {
    966           for (i=0; opts[i].short_opt; i++ )
    967             {
    968               if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
    969                 writestrings (0, "--", opts[i].long_opt, "\n", NULL);
    970 	    }
    971           writestrings (0, "--dump-options\n--help\n--version\n--warranty\n",
    972                         NULL);
    973           exit (0);
    974 	}
    975 
    976       if ( i == -2 )
    977         arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
    978       else if ( i == -1 )
    979         {
    980           arg->r_opt = ARGPARSE_INVALID_OPTION;
    981           arg->r.ret_str = s+2;
    982 	}
    983       else
    984         arg->r_opt = opts[i].short_opt;
    985       if ( i < 0 )
    986         ;
    987       else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
    988         {
    989           if ( argpos )
    990             {
    991               s2 = argpos+1;
    992               if ( !*s2 )
    993                 s2 = NULL;
    994 	    }
    995           else
    996             s2 = argv[1];
    997           if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
    998             {
    999               arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional.  */
   1000 	    }
   1001           else if ( !s2 )
   1002             {
   1003               arg->r_opt = ARGPARSE_MISSING_ARG;
   1004 	    }
   1005           else if ( !argpos && *s2 == '-'
   1006                     && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
   1007             {
   1008               /* The argument is optional and the next seems to be an
   1009                  option.  We do not check this possible option but
   1010                  assume no argument */
   1011               arg->r_type = ARGPARSE_TYPE_NONE;
   1012 	    }
   1013           else
   1014             {
   1015               set_opt_arg (arg, opts[i].flags, s2);
   1016               if ( !argpos )
   1017                 {
   1018                   argc--; argv++; idx++; /* Skip one.  */
   1019 		}
   1020 	    }
   1021 	}
   1022       else
   1023         {
   1024           /* Does not take an argument. */
   1025           if ( argpos )
   1026             arg->r_type = ARGPARSE_UNEXPECTED_ARG;
   1027           else
   1028             arg->r_type = 0;
   1029 	}
   1030       argc--; argv++; idx++; /* Set to next one.  */
   1031     }
   1032     else if ( (*s == '-' && s[1]) || arg->internal.inarg )
   1033       {
   1034         /* Short option.  */
   1035 	int dash_kludge = 0;
   1036 
   1037 	i = 0;
   1038 	if ( !arg->internal.inarg )
   1039           {
   1040 	    arg->internal.inarg++;
   1041 	    if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
   1042               {
   1043                 for (i=0; opts[i].short_opt; i++ )
   1044                   if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
   1045                     {
   1046                       dash_kludge = 1;
   1047                       break;
   1048 		    }
   1049               }
   1050           }
   1051 	s += arg->internal.inarg;
   1052 
   1053 	if (!dash_kludge )
   1054           {
   1055 	    for (i=0; opts[i].short_opt; i++ )
   1056               if ( opts[i].short_opt == *s )
   1057                 break;
   1058           }
   1059 
   1060 	if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
   1061           show_help (opts, arg->flags);
   1062 
   1063 	arg->r_opt = opts[i].short_opt;
   1064 	if (!opts[i].short_opt )
   1065           {
   1066 	    arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
   1067               ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
   1068 	    arg->internal.inarg++; /* Point to the next arg.  */
   1069 	    arg->r.ret_str = s;
   1070           }
   1071 	else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
   1072           {
   1073 	    if ( s[1] && !dash_kludge )
   1074               {
   1075 		s2 = s+1;
   1076 		set_opt_arg (arg, opts[i].flags, s2);
   1077               }
   1078 	    else
   1079               {
   1080 		s2 = argv[1];
   1081 		if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
   1082                   {
   1083 		    arg->r_type = ARGPARSE_TYPE_NONE;
   1084                   }
   1085 		else if ( !s2 )
   1086                   {
   1087 		    arg->r_opt = ARGPARSE_MISSING_ARG;
   1088                   }
   1089 		else if ( *s2 == '-' && s2[1]
   1090                           && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
   1091                   {
   1092 		    /* The argument is optional and the next seems to
   1093 	               be an option.  We do not check this possible
   1094 	               option but assume no argument.  */
   1095 		    arg->r_type = ARGPARSE_TYPE_NONE;
   1096                   }
   1097 		else
   1098                   {
   1099 		    set_opt_arg (arg, opts[i].flags, s2);
   1100 		    argc--; argv++; idx++; /* Skip one.  */
   1101                   }
   1102               }
   1103 	    s = "x"; /* This is so that !s[1] yields false.  */
   1104           }
   1105 	else
   1106           {
   1107             /* Does not take an argument.  */
   1108 	    arg->r_type = ARGPARSE_TYPE_NONE;
   1109 	    arg->internal.inarg++; /* Point to the next arg.  */
   1110           }
   1111 	if ( !s[1] || dash_kludge )
   1112           {
   1113             /* No more concatenated short options.  */
   1114 	    arg->internal.inarg = 0;
   1115 	    argc--; argv++; idx++;
   1116           }
   1117       }
   1118   else if ( arg->flags & ARGPARSE_FLAG_MIXED )
   1119     {
   1120       arg->r_opt = ARGPARSE_IS_ARG;
   1121       arg->r_type = 2;
   1122       arg->r.ret_str = s;
   1123       argc--; argv++; idx++; /* Set to next one.  */
   1124     }
   1125   else
   1126     {
   1127       arg->internal.stopped = 1; /* Stop option processing.  */
   1128       goto next_one;
   1129     }
   1130 
   1131  leave:
   1132   *arg->argc = argc;
   1133   *arg->argv = argv;
   1134   arg->internal.idx = idx;
   1135   return arg->r_opt;
   1136 }
   1137 
   1138 
   1139 /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
   1140    type argument.  */
   1141 static int
   1142 set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
   1143 {
   1144   int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
   1145   long l;
   1146 
   1147   switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
   1148     {
   1149     case ARGPARSE_TYPE_LONG:
   1150     case ARGPARSE_TYPE_INT:
   1151       errno = 0;
   1152       l = strtol (s, NULL, base);
   1153       if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
   1154         {
   1155           arg->r_opt = ARGPARSE_INVALID_ARG;
   1156           return -1;
   1157         }
   1158       if (arg->r_type == ARGPARSE_TYPE_LONG)
   1159         arg->r.ret_long = l;
   1160       else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
   1161         {
   1162           arg->r_opt = ARGPARSE_INVALID_ARG;
   1163           return -1;
   1164         }
   1165       else
   1166         arg->r.ret_int = (int)l;
   1167       return 0;
   1168 
   1169     case ARGPARSE_TYPE_ULONG:
   1170       while (isascii (*s) && isspace(*s))
   1171         s++;
   1172       if (*s == '-')
   1173         {
   1174           arg->r.ret_ulong = 0;
   1175           arg->r_opt = ARGPARSE_INVALID_ARG;
   1176           return -1;
   1177         }
   1178       errno = 0;
   1179       arg->r.ret_ulong = strtoul (s, NULL, base);
   1180       if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
   1181         {
   1182           arg->r_opt = ARGPARSE_INVALID_ARG;
   1183           return -1;
   1184         }
   1185       return 0;
   1186 
   1187     case ARGPARSE_TYPE_STRING:
   1188     default:
   1189       arg->r.ret_str = s;
   1190       return 1;
   1191     }
   1192 }
   1193 
   1194 
   1195 static size_t
   1196 long_opt_strlen( ARGPARSE_OPTS *o )
   1197 {
   1198   size_t n = strlen (o->long_opt);
   1199 
   1200   if ( o->description && *o->description == '|' )
   1201     {
   1202       const char *s;
   1203 #ifdef JNLIB_NEED_UTF8CONV
   1204       int is_utf8 = is_native_utf8 ();
   1205 #endif
   1206 
   1207       s=o->description+1;
   1208       if ( *s != '=' )
   1209         n++;
   1210       /* For a (mostly) correct length calculation we exclude
   1211          continuation bytes (10xxxxxx) if we are on a native utf8
   1212          terminal. */
   1213       for (; *s && *s != '|'; s++ )
   1214 #ifdef JNLIB_NEED_UTF8CONV
   1215         if ( is_utf8 && (*s&0xc0) != 0x80 )
   1216 #endif
   1217           n++;
   1218     }
   1219   return n;
   1220 }
   1221 
   1222 
   1223 /****************
   1224  * Print formatted help. The description string has some special
   1225  * meanings:
   1226  *  - A description string which is "@" suppresses help output for
   1227  *    this option
   1228  *  - a description,ine which starts with a '@' and is followed by
   1229  *    any other characters is printed as is; this may be used for examples
   1230  *    ans such.
   1231  *  - A description which starts with a '|' outputs the string between this
   1232  *    bar and the next one as arguments of the long option.
   1233  */
   1234 static void
   1235 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
   1236 {
   1237   const char *s;
   1238   char tmp[2];
   1239 
   1240   show_version ();
   1241   writestrings (0, "\n", NULL);
   1242   s = strusage (42);
   1243   if (s && *s == '1')
   1244     {
   1245       s = strusage (40);
   1246       writestrings (1, s, NULL);
   1247       if (*s && s[strlen(s)] != '\n')
   1248         writestrings (1, "\n", NULL);
   1249     }
   1250   s = strusage(41);
   1251   writestrings (0, s, "\n", NULL);
   1252   if ( opts[0].description )
   1253     {
   1254       /* Auto format the option description.  */
   1255       int i,j, indent;
   1256 
   1257       /* Get max. length of long options.  */
   1258       for (i=indent=0; opts[i].short_opt; i++ )
   1259         {
   1260           if ( opts[i].long_opt )
   1261             if ( !opts[i].description || *opts[i].description != '@' )
   1262               if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
   1263                 indent = j;
   1264 	}
   1265 
   1266       /* Example: " -v, --verbose   Viele Sachen ausgeben" */
   1267       indent += 10;
   1268       if ( *opts[0].description != '@' )
   1269         writestrings (0, "Options:", "\n", NULL);
   1270       for (i=0; opts[i].short_opt; i++ )
   1271         {
   1272           s = map_static_macro_string (_( opts[i].description ));
   1273           if ( s && *s== '@' && !s[1] ) /* Hide this line.  */
   1274             continue;
   1275           if ( s && *s == '@' )  /* Unindented comment only line.  */
   1276             {
   1277               for (s++; *s; s++ )
   1278                 {
   1279                   if ( *s == '\n' )
   1280                     {
   1281                       if( s[1] )
   1282                         writestrings (0, "\n", NULL);
   1283 		    }
   1284                   else
   1285                     {
   1286                       tmp[0] = *s;
   1287                       tmp[1] = 0;
   1288                       writestrings (0, tmp, NULL);
   1289                     }
   1290                 }
   1291               writestrings (0, "\n", NULL);
   1292               continue;
   1293 	    }
   1294 
   1295           j = 3;
   1296           if ( opts[i].short_opt < 256 )
   1297             {
   1298               tmp[0] = opts[i].short_opt;
   1299               tmp[1] = 0;
   1300               writestrings (0, " -", tmp, NULL );
   1301               if ( !opts[i].long_opt )
   1302                 {
   1303                   if (s && *s == '|' )
   1304                     {
   1305                       writestrings (0, " ", NULL); j++;
   1306                       for (s++ ; *s && *s != '|'; s++, j++ )
   1307                         {
   1308                           tmp[0] = *s;
   1309                           tmp[1] = 0;
   1310                           writestrings (0, tmp, NULL);
   1311                         }
   1312                       if ( *s )
   1313                         s++;
   1314 		    }
   1315 		}
   1316 	    }
   1317           else
   1318             writestrings (0, "   ", NULL);
   1319           if ( opts[i].long_opt )
   1320             {
   1321               tmp[0] = opts[i].short_opt < 256?',':' ';
   1322               tmp[1] = 0;
   1323               j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
   1324               if (s && *s == '|' )
   1325                 {
   1326                   if ( *++s != '=' )
   1327                     {
   1328                       writestrings (0, " ", NULL);
   1329                       j++;
   1330 		    }
   1331                   for ( ; *s && *s != '|'; s++, j++ )
   1332                     {
   1333                       tmp[0] = *s;
   1334                       tmp[1] = 0;
   1335                       writestrings (0, tmp, NULL);
   1336                     }
   1337                   if ( *s )
   1338                     s++;
   1339 		}
   1340               writestrings (0, "   ", NULL);
   1341               j += 3;
   1342 	    }
   1343           for (;j < indent; j++ )
   1344             writestrings (0, " ", NULL);
   1345           if ( s )
   1346             {
   1347               if ( *s && j > indent )
   1348                 {
   1349                   writestrings (0, "\n", NULL);
   1350                   for (j=0;j < indent; j++ )
   1351                     writestrings (0, " ", NULL);
   1352 		}
   1353               for (; *s; s++ )
   1354                 {
   1355                   if ( *s == '\n' )
   1356                     {
   1357                       if ( s[1] )
   1358                         {
   1359                           writestrings (0, "\n", NULL);
   1360                           for (j=0; j < indent; j++ )
   1361                             writestrings (0, " ", NULL);
   1362 			}
   1363 		    }
   1364                   else
   1365                     {
   1366                       tmp[0] = *s;
   1367                       tmp[1] = 0;
   1368                       writestrings (0, tmp, NULL);
   1369                     }
   1370 		}
   1371 	    }
   1372           writestrings (0, "\n", NULL);
   1373 	}
   1374 	if ( (flags & ARGPARSE_FLAG_ONEDASH) )
   1375           writestrings (0, "\n(A single dash may be used "
   1376                         "instead of the double ones)\n", NULL);
   1377     }
   1378   if ( (s=strusage(19)) )
   1379     {
   1380       writestrings (0, "\n", NULL);
   1381       writestrings (0, s, NULL);
   1382     }
   1383   flushstrings (0);
   1384   exit(0);
   1385 }
   1386 
   1387 static void
   1388 show_version ()
   1389 {
   1390   const char *s;
   1391   int i;
   1392 
   1393   /* Version line.  */
   1394   writestrings (0, strusage (11), NULL);
   1395   if ((s=strusage (12)))
   1396     writestrings (0, " (", s, ")", NULL);
   1397   writestrings (0, " ", strusage (13), "\n", NULL);
   1398   /* Additional version lines. */
   1399   for (i=20; i < 30; i++)
   1400     if ((s=strusage (i)))
   1401       writestrings (0, s, "\n", NULL);
   1402   /* Copyright string.  */
   1403   if ((s=strusage (14)))
   1404     writestrings (0, s, "\n", NULL);
   1405   /* Licence string.  */
   1406   if( (s=strusage (10)) )
   1407     writestrings (0, s, "\n", NULL);
   1408   /* Copying conditions. */
   1409   if ( (s=strusage(15)) )
   1410     writestrings (0, s, NULL);
   1411   /* Thanks. */
   1412   if ((s=strusage(18)))
   1413     writestrings (0, s, NULL);
   1414   /* Additional program info. */
   1415   for (i=30; i < 40; i++ )
   1416     if ( (s=strusage (i)) )
   1417       writestrings (0, s, NULL);
   1418   flushstrings (0);
   1419 }
   1420 
   1421 
   1422 void
   1423 usage (int level)
   1424 {
   1425   const char *p;
   1426 
   1427   if (!level)
   1428     {
   1429       writestrings (1, strusage(11), " ", strusage(13), "; ",
   1430                     strusage (14), "\n", NULL);
   1431       flushstrings (1);
   1432     }
   1433   else if (level == 1)
   1434     {
   1435       p = strusage (40);
   1436       writestrings (1, p, NULL);
   1437       if (*p && p[strlen(p)] != '\n')
   1438         writestrings (1, "\n", NULL);
   1439       exit (2);
   1440     }
   1441   else if (level == 2)
   1442     {
   1443       p = strusage (42);
   1444       if (p && *p == '1')
   1445         {
   1446           p = strusage (40);
   1447           writestrings (1, p, NULL);
   1448           if (*p && p[strlen(p)] != '\n')
   1449             writestrings (1, "\n", NULL);
   1450         }
   1451       writestrings (0, strusage(41), "\n", NULL);
   1452       exit (0);
   1453     }
   1454 }
   1455 
   1456 /* Level
   1457  *     0: Print copyright string to stderr
   1458  *     1: Print a short usage hint to stderr and terminate
   1459  *     2: Print a long usage hint to stdout and terminate
   1460  *    10: Return license info string
   1461  *    11: Return the name of the program
   1462  *    12: Return optional name of package which includes this program.
   1463  *    13: version  string
   1464  *    14: copyright string
   1465  *    15: Short copying conditions (with LFs)
   1466  *    16: Long copying conditions (with LFs)
   1467  *    17: Optional printable OS name
   1468  *    18: Optional thanks list (with LFs)
   1469  *    19: Bug report info
   1470  *20..29: Additional lib version strings.
   1471  *30..39: Additional program info (with LFs)
   1472  *    40: short usage note (with LF)
   1473  *    41: long usage note (with LF)
   1474  *    42: Flag string:
   1475  *          First char is '1':
   1476  *             The short usage notes needs to be printed
   1477  *             before the long usage note.
   1478  */
   1479 const char *
   1480 strusage( int level )
   1481 {
   1482   const char *p = strusage_handler? strusage_handler(level) : NULL;
   1483 
   1484   if ( p )
   1485     return map_static_macro_string (p);
   1486 
   1487   switch ( level )
   1488     {
   1489 
   1490     case 10:
   1491 #if ARGPARSE_GPL_VERSION == 3
   1492       p = ("License GPLv3+: GNU GPL version 3 or later "
   1493            "<http://gnu.org/licenses/gpl.html>");
   1494 #else
   1495       p = ("License GPLv2+: GNU GPL version 2 or later "
   1496            "<http://gnu.org/licenses/>");
   1497 #endif
   1498       break;
   1499     case 11: p = "foo"; break;
   1500     case 13: p = "0.0"; break;
   1501     case 14: p = ARGPARSE_CRIGHT_STR; break;
   1502     case 15: p =
   1503 "This is free software: you are free to change and redistribute it.\n"
   1504 "There is NO WARRANTY, to the extent permitted by law.\n";
   1505       break;
   1506     case 16: p =
   1507 "This is free software; you can redistribute it and/or modify\n"
   1508 "it under the terms of the GNU General Public License as published by\n"
   1509 "the Free Software Foundation; either version "
   1510 ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
   1511 " of the License, or\n"
   1512 "(at your option) any later version.\n\n"
   1513 "It is distributed in the hope that it will be useful,\n"
   1514 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
   1515 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
   1516 "GNU General Public License for more details.\n\n"
   1517 "You should have received a copy of the GNU General Public License\n"
   1518 "along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
   1519       break;
   1520     case 40: /* short and long usage */
   1521     case 41: p = ""; break;
   1522     }
   1523 
   1524   return p;
   1525 }
   1526 
   1527 
   1528 /* Set the usage handler.  This function is basically a constructor.  */
   1529 void
   1530 set_strusage ( const char *(*f)( int ) )
   1531 {
   1532   strusage_handler = f;
   1533 }
   1534 
   1535 
   1536 #ifdef TEST
   1537 static struct {
   1538     int verbose;
   1539     int debug;
   1540     char *outfile;
   1541     char *crf;
   1542     int myopt;
   1543     int echo;
   1544     int a_long_one;
   1545 } opt;
   1546 
   1547 int
   1548 main(int argc, char **argv)
   1549 {
   1550   ARGPARSE_OPTS opts[] = {
   1551     ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
   1552     ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
   1553                                   "was wir eingegeben haben")),
   1554     ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
   1555     ARGPARSE_s_s('o', "output", 0 ),
   1556     ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
   1557     /* Note that on a non-utf8 terminal the ß might garble the output. */
   1558     ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
   1559     ARGPARSE_o_i('m', "my-option", 0),
   1560     ARGPARSE_s_n(500, "a-long-option", 0 ),
   1561     ARGPARSE_end()
   1562   };
   1563   ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
   1564                                          | ARGPARSE_FLAG_MIXED
   1565                                          | ARGPARSE_FLAG_ONEDASH) };
   1566   int i;
   1567 
   1568   while (arg_parse  (&pargs, opts))
   1569     {
   1570       switch (pargs.r_opt)
   1571         {
   1572         case ARGPARSE_IS_ARG :
   1573           printf ("arg='%s'\n", pargs.r.ret_str);
   1574           break;
   1575         case 'v': opt.verbose++; break;
   1576         case 'e': opt.echo++; break;
   1577         case 'd': opt.debug++; break;
   1578         case 'o': opt.outfile = pargs.r.ret_str; break;
   1579         case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
   1580         case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
   1581         case 500: opt.a_long_one++;  break;
   1582         default : pargs.err = ARGPARSE_PRINT_WARNING; break;
   1583 	}
   1584     }
   1585   for (i=0; i < argc; i++ )
   1586     printf ("%3d -> (%s)\n", i, argv[i] );
   1587   puts ("Options:");
   1588   if (opt.verbose)
   1589     printf ("  verbose=%d\n", opt.verbose );
   1590   if (opt.debug)
   1591     printf ("  debug=%d\n", opt.debug );
   1592   if (opt.outfile)
   1593     printf ("  outfile='%s'\n", opt.outfile );
   1594   if (opt.crf)
   1595     printf ("  crffile='%s'\n", opt.crf );
   1596   if (opt.myopt)
   1597     printf ("  myopt=%d\n", opt.myopt );
   1598   if (opt.a_long_one)
   1599     printf ("  a-long-one=%d\n", opt.a_long_one );
   1600   if (opt.echo)
   1601     printf ("  echo=%d\n", opt.echo );
   1602 
   1603   return 0;
   1604 }
   1605 #endif /*TEST*/
   1606 
   1607 /**** bottom of file ****/