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