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