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