Imported Upstream version 2.2.15
[platform/upstream/gpg2.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 and/or modify this
8  * part of GnuPG 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 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) 2019 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 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   /* Fill in missing standard options: help, version, warranty and
922    * dump-options.  */
923   ARGPARSE_OPTS help_opt
924     = ARGPARSE_s_n (ARGPARSE_SHORTOPT_HELP, "help", "@");
925   ARGPARSE_OPTS version_opt
926     = ARGPARSE_s_n (ARGPARSE_SHORTOPT_VERSION, "version", "@");
927   ARGPARSE_OPTS warranty_opt
928     = ARGPARSE_s_n (ARGPARSE_SHORTOPT_WARRANTY, "warranty", "@");
929   ARGPARSE_OPTS dump_options_opt
930     = ARGPARSE_s_n(ARGPARSE_SHORTOPT_DUMP_OPTIONS, "dump-options", "@");
931   int seen_help = 0;
932   int seen_version = 0;
933   int seen_warranty = 0;
934   int seen_dump_options = 0;
935
936   i = 0;
937   while (opts[i].short_opt)
938     {
939       if (opts[i].long_opt)
940         {
941           if (!strcmp(opts[i].long_opt, help_opt.long_opt))
942             seen_help = 1;
943           else if (!strcmp(opts[i].long_opt, version_opt.long_opt))
944             seen_version = 1;
945           else if (!strcmp(opts[i].long_opt, warranty_opt.long_opt))
946             seen_warranty = 1;
947           else if (!strcmp(opts[i].long_opt, dump_options_opt.long_opt))
948             seen_dump_options = 1;
949         }
950       i++;
951     }
952   if (! seen_help)
953     opts[i++] = help_opt;
954   if (! seen_version)
955     opts[i++] = version_opt;
956   if (! seen_warranty)
957     opts[i++] = warranty_opt;
958   if (! seen_dump_options)
959     opts[i++] = dump_options_opt;
960
961   initialize( arg, NULL, NULL );
962   argc = *arg->argc;
963   argv = *arg->argv;
964   idx = arg->internal.idx;
965
966   if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
967     {
968       /* Skip the first argument.  */
969       argc--; argv++; idx++;
970     }
971
972  next_one:
973   if (!argc)
974     {
975       /* No more args.  */
976       arg->r_opt = 0;
977       goto leave; /* Ready. */
978     }
979
980   s = *argv;
981   arg->internal.last = s;
982
983   if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
984     {
985       arg->r_opt = ARGPARSE_IS_ARG;  /* Not an option but an argument.  */
986       arg->r_type = 2;
987       arg->r.ret_str = s;
988       argc--; argv++; idx++; /* set to next one */
989     }
990   else if( arg->internal.stopped )
991     {
992       arg->r_opt = 0;
993       goto leave; /* Ready.  */
994     }
995   else if ( *s == '-' && s[1] == '-' )
996     {
997       /* Long option.  */
998       char *argpos;
999
1000       arg->internal.inarg = 0;
1001       if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
1002         {
1003           /* Stop option processing.  */
1004           arg->internal.stopped = 1;
1005           arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
1006           argc--; argv++; idx++;
1007           goto next_one;
1008         }
1009
1010       argpos = strchr( s+2, '=' );
1011       if ( argpos )
1012         *argpos = 0;
1013       i = find_long_option ( arg, opts, s+2 );
1014       if ( argpos )
1015         *argpos = '=';
1016
1017       if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_HELP)
1018         show_help (opts, arg->flags);
1019       else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_VERSION)
1020         {
1021           if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
1022             {
1023               show_version ();
1024               exit(0);
1025             }
1026         }
1027       else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_WARRANTY)
1028         {
1029           writestrings (0, strusage (16), "\n", NULL);
1030           exit (0);
1031         }
1032       else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTIONS)
1033         {
1034           for (i=0; opts[i].short_opt; i++ )
1035             {
1036               if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
1037                 writestrings (0, "--", opts[i].long_opt, "\n", NULL);
1038             }
1039           exit (0);
1040         }
1041
1042       if ( i == -2 )
1043         arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
1044       else if ( i == -1 )
1045         {
1046           arg->r_opt = ARGPARSE_INVALID_OPTION;
1047           arg->r.ret_str = s+2;
1048         }
1049       else
1050         arg->r_opt = opts[i].short_opt;
1051       if ( i < 0 )
1052         ;
1053       else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1054         {
1055           if ( argpos )
1056             {
1057               s2 = argpos+1;
1058               if ( !*s2 )
1059                 s2 = NULL;
1060             }
1061           else
1062             s2 = argv[1];
1063           if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1064             {
1065               arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional.  */
1066             }
1067           else if ( !s2 )
1068             {
1069               arg->r_opt = ARGPARSE_MISSING_ARG;
1070             }
1071           else if ( !argpos && *s2 == '-'
1072                     && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1073             {
1074               /* The argument is optional and the next seems to be an
1075                  option.  We do not check this possible option but
1076                  assume no argument */
1077               arg->r_type = ARGPARSE_TYPE_NONE;
1078             }
1079           else
1080             {
1081               set_opt_arg (arg, opts[i].flags, s2);
1082               if ( !argpos )
1083                 {
1084                   argc--; argv++; idx++; /* Skip one.  */
1085                 }
1086             }
1087         }
1088       else
1089         {
1090           /* Does not take an argument. */
1091           if ( argpos )
1092             arg->r_type = ARGPARSE_UNEXPECTED_ARG;
1093           else
1094             arg->r_type = 0;
1095         }
1096       argc--; argv++; idx++; /* Set to next one.  */
1097     }
1098     else if ( (*s == '-' && s[1]) || arg->internal.inarg )
1099       {
1100         /* Short option.  */
1101         int dash_kludge = 0;
1102
1103         i = 0;
1104         if ( !arg->internal.inarg )
1105           {
1106             arg->internal.inarg++;
1107             if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
1108               {
1109                 for (i=0; opts[i].short_opt; i++ )
1110                   if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
1111                     {
1112                       dash_kludge = 1;
1113                       break;
1114                     }
1115               }
1116           }
1117         s += arg->internal.inarg;
1118
1119         if (!dash_kludge )
1120           {
1121             for (i=0; opts[i].short_opt; i++ )
1122               if ( opts[i].short_opt == *s )
1123                 break;
1124           }
1125
1126         if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
1127           show_help (opts, arg->flags);
1128
1129         arg->r_opt = opts[i].short_opt;
1130         if (!opts[i].short_opt )
1131           {
1132             arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
1133               ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
1134             arg->internal.inarg++; /* Point to the next arg.  */
1135             arg->r.ret_str = s;
1136           }
1137         else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1138           {
1139             if ( s[1] && !dash_kludge )
1140               {
1141                 s2 = s+1;
1142                 set_opt_arg (arg, opts[i].flags, s2);
1143               }
1144             else
1145               {
1146                 s2 = argv[1];
1147                 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1148                   {
1149                     arg->r_type = ARGPARSE_TYPE_NONE;
1150                   }
1151                 else if ( !s2 )
1152                   {
1153                     arg->r_opt = ARGPARSE_MISSING_ARG;
1154                   }
1155                 else if ( *s2 == '-' && s2[1]
1156                           && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1157                   {
1158                     /* The argument is optional and the next seems to
1159                        be an option.  We do not check this possible
1160                        option but assume no argument.  */
1161                     arg->r_type = ARGPARSE_TYPE_NONE;
1162                   }
1163                 else
1164                   {
1165                     set_opt_arg (arg, opts[i].flags, s2);
1166                     argc--; argv++; idx++; /* Skip one.  */
1167                   }
1168               }
1169             s = "x"; /* This is so that !s[1] yields false.  */
1170           }
1171         else
1172           {
1173             /* Does not take an argument.  */
1174             arg->r_type = ARGPARSE_TYPE_NONE;
1175             arg->internal.inarg++; /* Point to the next arg.  */
1176           }
1177         if ( !s[1] || dash_kludge )
1178           {
1179             /* No more concatenated short options.  */
1180             arg->internal.inarg = 0;
1181             argc--; argv++; idx++;
1182           }
1183       }
1184   else if ( arg->flags & ARGPARSE_FLAG_MIXED )
1185     {
1186       arg->r_opt = ARGPARSE_IS_ARG;
1187       arg->r_type = 2;
1188       arg->r.ret_str = s;
1189       argc--; argv++; idx++; /* Set to next one.  */
1190     }
1191   else
1192     {
1193       arg->internal.stopped = 1; /* Stop option processing.  */
1194       goto next_one;
1195     }
1196
1197  leave:
1198   *arg->argc = argc;
1199   *arg->argv = argv;
1200   arg->internal.idx = idx;
1201   return arg->r_opt;
1202 }
1203
1204
1205 /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
1206    type argument.  */
1207 static int
1208 set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
1209 {
1210   int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
1211   long l;
1212
1213   switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
1214     {
1215     case ARGPARSE_TYPE_LONG:
1216     case ARGPARSE_TYPE_INT:
1217       errno = 0;
1218       l = strtol (s, NULL, base);
1219       if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
1220         {
1221           arg->r_opt = ARGPARSE_INVALID_ARG;
1222           return -1;
1223         }
1224       if (arg->r_type == ARGPARSE_TYPE_LONG)
1225         arg->r.ret_long = l;
1226       else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
1227         {
1228           arg->r_opt = ARGPARSE_INVALID_ARG;
1229           return -1;
1230         }
1231       else
1232         arg->r.ret_int = (int)l;
1233       return 0;
1234
1235     case ARGPARSE_TYPE_ULONG:
1236       while (isascii (*s) && isspace(*s))
1237         s++;
1238       if (*s == '-')
1239         {
1240           arg->r.ret_ulong = 0;
1241           arg->r_opt = ARGPARSE_INVALID_ARG;
1242           return -1;
1243         }
1244       errno = 0;
1245       arg->r.ret_ulong = strtoul (s, NULL, base);
1246       if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
1247         {
1248           arg->r_opt = ARGPARSE_INVALID_ARG;
1249           return -1;
1250         }
1251       return 0;
1252
1253     case ARGPARSE_TYPE_STRING:
1254     default:
1255       arg->r.ret_str = s;
1256       return 1;
1257     }
1258 }
1259
1260
1261 static size_t
1262 long_opt_strlen( ARGPARSE_OPTS *o )
1263 {
1264   size_t n = strlen (o->long_opt);
1265
1266   if ( o->description && *o->description == '|' )
1267     {
1268       const char *s;
1269       int is_utf8 = is_native_utf8 ();
1270
1271       s=o->description+1;
1272       if ( *s != '=' )
1273         n++;
1274       /* For a (mostly) correct length calculation we exclude
1275          continuation bytes (10xxxxxx) if we are on a native utf8
1276          terminal. */
1277       for (; *s && *s != '|'; s++ )
1278         if ( is_utf8 && (*s&0xc0) != 0x80 )
1279           n++;
1280     }
1281   return n;
1282 }
1283
1284
1285 /****************
1286  * Print formatted help. The description string has some special
1287  * meanings:
1288  *  - A description string which is "@" suppresses help output for
1289  *    this option
1290  *  - a description,ine which starts with a '@' and is followed by
1291  *    any other characters is printed as is; this may be used for examples
1292  *    ans such.
1293  *  - A description which starts with a '|' outputs the string between this
1294  *    bar and the next one as arguments of the long option.
1295  */
1296 static void
1297 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
1298 {
1299   const char *s;
1300   char tmp[2];
1301
1302   show_version ();
1303   writestrings (0, "\n", NULL);
1304   s = strusage (42);
1305   if (s && *s == '1')
1306     {
1307       s = strusage (40);
1308       writestrings (1, s, NULL);
1309       if (*s && s[strlen(s)] != '\n')
1310         writestrings (1, "\n", NULL);
1311     }
1312   s = strusage(41);
1313   writestrings (0, s, "\n", NULL);
1314   if ( opts[0].description )
1315     {
1316       /* Auto format the option description.  */
1317       int i,j, indent;
1318
1319       /* Get max. length of long options.  */
1320       for (i=indent=0; opts[i].short_opt; i++ )
1321         {
1322           if ( opts[i].long_opt )
1323             if ( !opts[i].description || *opts[i].description != '@' )
1324               if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
1325                 indent = j;
1326         }
1327
1328       /* Example: " -v, --verbose   Viele Sachen ausgeben" */
1329       indent += 10;
1330       if ( *opts[0].description != '@' )
1331         writestrings (0, "Options:", "\n", NULL);
1332       for (i=0; opts[i].short_opt; i++ )
1333         {
1334           s = map_static_macro_string (_( opts[i].description ));
1335           if ( s && *s== '@' && !s[1] ) /* Hide this line.  */
1336             continue;
1337           if ( s && *s == '@' )  /* Unindented comment only line.  */
1338             {
1339               for (s++; *s; s++ )
1340                 {
1341                   if ( *s == '\n' )
1342                     {
1343                       if( s[1] )
1344                         writestrings (0, "\n", NULL);
1345                     }
1346                   else
1347                     {
1348                       tmp[0] = *s;
1349                       tmp[1] = 0;
1350                       writestrings (0, tmp, NULL);
1351                     }
1352                 }
1353               writestrings (0, "\n", NULL);
1354               continue;
1355             }
1356
1357           j = 3;
1358           if ( opts[i].short_opt < 256 )
1359             {
1360               tmp[0] = opts[i].short_opt;
1361               tmp[1] = 0;
1362               writestrings (0, " -", tmp, NULL );
1363               if ( !opts[i].long_opt )
1364                 {
1365                   if (s && *s == '|' )
1366                     {
1367                       writestrings (0, " ", NULL); j++;
1368                       for (s++ ; *s && *s != '|'; s++, j++ )
1369                         {
1370                           tmp[0] = *s;
1371                           tmp[1] = 0;
1372                           writestrings (0, tmp, NULL);
1373                         }
1374                       if ( *s )
1375                         s++;
1376                     }
1377                 }
1378             }
1379           else
1380             writestrings (0, "   ", NULL);
1381           if ( opts[i].long_opt )
1382             {
1383               tmp[0] = opts[i].short_opt < 256?',':' ';
1384               tmp[1] = 0;
1385               j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
1386               if (s && *s == '|' )
1387                 {
1388                   if ( *++s != '=' )
1389                     {
1390                       writestrings (0, " ", NULL);
1391                       j++;
1392                     }
1393                   for ( ; *s && *s != '|'; s++, j++ )
1394                     {
1395                       tmp[0] = *s;
1396                       tmp[1] = 0;
1397                       writestrings (0, tmp, NULL);
1398                     }
1399                   if ( *s )
1400                     s++;
1401                 }
1402               writestrings (0, "   ", NULL);
1403               j += 3;
1404             }
1405           for (;j < indent; j++ )
1406             writestrings (0, " ", NULL);
1407           if ( s )
1408             {
1409               if ( *s && j > indent )
1410                 {
1411                   writestrings (0, "\n", NULL);
1412                   for (j=0;j < indent; j++ )
1413                     writestrings (0, " ", NULL);
1414                 }
1415               for (; *s; s++ )
1416                 {
1417                   if ( *s == '\n' )
1418                     {
1419                       if ( s[1] )
1420                         {
1421                           writestrings (0, "\n", NULL);
1422                           for (j=0; j < indent; j++ )
1423                             writestrings (0, " ", NULL);
1424                         }
1425                     }
1426                   else
1427                     {
1428                       tmp[0] = *s;
1429                       tmp[1] = 0;
1430                       writestrings (0, tmp, NULL);
1431                     }
1432                 }
1433             }
1434           writestrings (0, "\n", NULL);
1435         }
1436         if ( (flags & ARGPARSE_FLAG_ONEDASH) )
1437           writestrings (0, "\n(A single dash may be used "
1438                         "instead of the double ones)\n", NULL);
1439     }
1440   if ( (s=strusage(19)) )
1441     {
1442       writestrings (0, "\n", NULL);
1443       writestrings (0, s, NULL);
1444     }
1445   flushstrings (0);
1446   exit(0);
1447 }
1448
1449 static void
1450 show_version ()
1451 {
1452   const char *s;
1453   int i;
1454
1455   /* Version line.  */
1456   writestrings (0, strusage (11), NULL);
1457   if ((s=strusage (12)))
1458     writestrings (0, " (", s, ")", NULL);
1459   writestrings (0, " ", strusage (13), "\n", NULL);
1460   /* Additional version lines. */
1461   for (i=20; i < 30; i++)
1462     if ((s=strusage (i)))
1463       writestrings (0, s, "\n", NULL);
1464   /* Copyright string.  */
1465   if ((s=strusage (14)))
1466     writestrings (0, s, "\n", NULL);
1467   /* Licence string.  */
1468   if( (s=strusage (10)) )
1469     writestrings (0, s, "\n", NULL);
1470   /* Copying conditions. */
1471   if ( (s=strusage(15)) )
1472     writestrings (0, s, NULL);
1473   /* Thanks. */
1474   if ((s=strusage(18)))
1475     writestrings (0, s, NULL);
1476   /* Additional program info. */
1477   for (i=30; i < 40; i++ )
1478     if ( (s=strusage (i)) )
1479       writestrings (0, s, NULL);
1480   flushstrings (0);
1481 }
1482
1483
1484 void
1485 usage (int level)
1486 {
1487   const char *p;
1488
1489   if (!level)
1490     {
1491       writestrings (1, strusage(11), " ", strusage(13), "; ",
1492                     strusage (14), "\n", NULL);
1493       flushstrings (1);
1494     }
1495   else if (level == 1)
1496     {
1497       p = strusage (40);
1498       writestrings (1, p, NULL);
1499       if (*p && p[strlen(p)] != '\n')
1500         writestrings (1, "\n", NULL);
1501       exit (2);
1502     }
1503   else if (level == 2)
1504     {
1505       p = strusage (42);
1506       if (p && *p == '1')
1507         {
1508           p = strusage (40);
1509           writestrings (1, p, NULL);
1510           if (*p && p[strlen(p)] != '\n')
1511             writestrings (1, "\n", NULL);
1512         }
1513       writestrings (0, strusage(41), "\n", NULL);
1514       exit (0);
1515     }
1516 }
1517
1518 /* Level
1519  *     0: Print copyright string to stderr
1520  *     1: Print a short usage hint to stderr and terminate
1521  *     2: Print a long usage hint to stdout and terminate
1522  *    10: Return license info string
1523  *    11: Return the name of the program
1524  *    12: Return optional name of package which includes this program.
1525  *    13: version  string
1526  *    14: copyright string
1527  *    15: Short copying conditions (with LFs)
1528  *    16: Long copying conditions (with LFs)
1529  *    17: Optional printable OS name
1530  *    18: Optional thanks list (with LFs)
1531  *    19: Bug report info
1532  *20..29: Additional lib version strings.
1533  *30..39: Additional program info (with LFs)
1534  *    40: short usage note (with LF)
1535  *    41: long usage note (with LF)
1536  *    42: Flag string:
1537  *          First char is '1':
1538  *             The short usage notes needs to be printed
1539  *             before the long usage note.
1540  */
1541 const char *
1542 strusage( int level )
1543 {
1544   const char *p = strusage_handler? strusage_handler(level) : NULL;
1545
1546   if ( p )
1547     return map_static_macro_string (p);
1548
1549   switch ( level )
1550     {
1551
1552     case 10:
1553 #if ARGPARSE_GPL_VERSION == 3
1554       p = ("License GPLv3+: GNU GPL version 3 or later "
1555            "<https://gnu.org/licenses/gpl.html>");
1556 #else
1557       p = ("License GPLv2+: GNU GPL version 2 or later "
1558            "<https://gnu.org/licenses/>");
1559 #endif
1560       break;
1561     case 11: p = "foo"; break;
1562     case 13: p = "0.0"; break;
1563     case 14: p = ARGPARSE_CRIGHT_STR; break;
1564     case 15: p =
1565 "This is free software: you are free to change and redistribute it.\n"
1566 "There is NO WARRANTY, to the extent permitted by law.\n";
1567       break;
1568     case 16: p =
1569 "This is free software; you can redistribute it and/or modify\n"
1570 "it under the terms of the GNU General Public License as published by\n"
1571 "the Free Software Foundation; either version "
1572 ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
1573 " of the License, or\n"
1574 "(at your option) any later version.\n\n"
1575 "It is distributed in the hope that it will be useful,\n"
1576 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1577 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1578 "GNU General Public License for more details.\n\n"
1579 "You should have received a copy of the GNU General Public License\n"
1580 "along with this software.  If not, see <https://gnu.org/licenses/>.\n";
1581       break;
1582     case 40: /* short and long usage */
1583     case 41: p = ""; break;
1584     }
1585
1586   return p;
1587 }
1588
1589
1590 /* Set the usage handler.  This function is basically a constructor.  */
1591 void
1592 set_strusage ( const char *(*f)( int ) )
1593 {
1594   strusage_handler = f;
1595 }
1596
1597
1598 #ifdef TEST
1599 static struct {
1600     int verbose;
1601     int debug;
1602     char *outfile;
1603     char *crf;
1604     int myopt;
1605     int echo;
1606     int a_long_one;
1607 } opt;
1608
1609 int
1610 main(int argc, char **argv)
1611 {
1612   ARGPARSE_OPTS opts[] = {
1613     ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1614     ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
1615                                   "was wir eingegeben haben")),
1616     ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1617     ARGPARSE_s_s('o', "output", 0 ),
1618     ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1619     /* Note that on a non-utf8 terminal the ß might garble the output. */
1620     ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1621     ARGPARSE_o_i('m', "my-option", 0),
1622     ARGPARSE_s_n(500, "a-long-option", 0 ),
1623     ARGPARSE_end()
1624   };
1625   ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
1626                                          | ARGPARSE_FLAG_MIXED
1627                                          | ARGPARSE_FLAG_ONEDASH) };
1628   int i;
1629
1630   while (arg_parse  (&pargs, opts))
1631     {
1632       switch (pargs.r_opt)
1633         {
1634         case ARGPARSE_IS_ARG :
1635           printf ("arg='%s'\n", pargs.r.ret_str);
1636           break;
1637         case 'v': opt.verbose++; break;
1638         case 'e': opt.echo++; break;
1639         case 'd': opt.debug++; break;
1640         case 'o': opt.outfile = pargs.r.ret_str; break;
1641         case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1642         case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1643         case 500: opt.a_long_one++;  break;
1644         default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1645         }
1646     }
1647   for (i=0; i < argc; i++ )
1648     printf ("%3d -> (%s)\n", i, argv[i] );
1649   puts ("Options:");
1650   if (opt.verbose)
1651     printf ("  verbose=%d\n", opt.verbose );
1652   if (opt.debug)
1653     printf ("  debug=%d\n", opt.debug );
1654   if (opt.outfile)
1655     printf ("  outfile='%s'\n", opt.outfile );
1656   if (opt.crf)
1657     printf ("  crffile='%s'\n", opt.crf );
1658   if (opt.myopt)
1659     printf ("  myopt=%d\n", opt.myopt );
1660   if (opt.a_long_one)
1661     printf ("  a-long-one=%d\n", opt.a_long_one );
1662   if (opt.echo)
1663     printf ("  echo=%d\n", opt.echo );
1664
1665   return 0;
1666 }
1667 #endif /*TEST*/
1668
1669 /**** bottom of file ****/