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