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