common: Replace all macros in strusage() returned strings.
[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       writestrings (0, "\n", NULL);
1226       writestrings (0, s, NULL);
1227     }
1228   flushstrings (0);
1229   exit(0);
1230 }
1231
1232 static void
1233 show_version ()
1234 {
1235   const char *s;
1236   int i;
1237
1238   /* Version line.  */
1239   writestrings (0, strusage (11), NULL);
1240   if ((s=strusage (12)))
1241     writestrings (0, " (", s, ")", NULL);
1242   writestrings (0, " ", strusage (13), "\n", NULL);
1243   /* Additional version lines. */
1244   for (i=20; i < 30; i++)
1245     if ((s=strusage (i)))
1246       writestrings (0, s, "\n", NULL);
1247   /* Copyright string.  */
1248   if ((s=strusage (14)))
1249     writestrings (0, s, "\n", NULL);
1250   /* Licence string.  */
1251   if( (s=strusage (10)) )
1252     writestrings (0, s, "\n", NULL);
1253   /* Copying conditions. */
1254   if ( (s=strusage(15)) )
1255     writestrings (0, s, NULL);
1256   /* Thanks. */
1257   if ((s=strusage(18)))
1258     writestrings (0, s, NULL);
1259   /* Additional program info. */
1260   for (i=30; i < 40; i++ )
1261     if ( (s=strusage (i)) )
1262       writestrings (0, s, NULL);
1263   flushstrings (0);
1264 }
1265
1266
1267 void
1268 usage (int level)
1269 {
1270   const char *p;
1271
1272   if (!level)
1273     {
1274       writestrings (1, strusage(11), " ", strusage(13), "; ",
1275                     strusage (14), "\n", NULL);
1276       flushstrings (1);
1277     }
1278   else if (level == 1)
1279     {
1280       p = strusage (40);
1281       writestrings (1, p, NULL);
1282       if (*p && p[strlen(p)] != '\n')
1283         writestrings (1, "\n", NULL);
1284       exit (2);
1285     }
1286   else if (level == 2)
1287     {
1288       writestrings (0, strusage(41), "\n", NULL);
1289       exit (0);
1290     }
1291 }
1292
1293 /* Level
1294  *     0: Print copyright string to stderr
1295  *     1: Print a short usage hint to stderr and terminate
1296  *     2: Print a long usage hint to stdout and terminate
1297  *    10: Return license info string
1298  *    11: Return the name of the program
1299  *    12: Return optional name of package which includes this program.
1300  *    13: version  string
1301  *    14: copyright string
1302  *    15: Short copying conditions (with LFs)
1303  *    16: Long copying conditions (with LFs)
1304  *    17: Optional printable OS name
1305  *    18: Optional thanks list (with LFs)
1306  *    19: Bug report info
1307  *20..29: Additional lib version strings.
1308  *30..39: Additional program info (with LFs)
1309  *    40: short usage note (with LF)
1310  *    41: long usage note (with LF)
1311  */
1312 const char *
1313 strusage( int level )
1314 {
1315   const char *p = strusage_handler? strusage_handler(level) : NULL;
1316
1317   if ( p )
1318     return map_static_macro_string (p);
1319
1320   switch ( level )
1321     {
1322     case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
1323                   "<http://gnu.org/licenses/gpl.html>");
1324       break;
1325     case 11: p = "foo"; break;
1326     case 13: p = "0.0"; break;
1327     case 14: p = "Copyright (C) 2012 Free Software Foundation, Inc."; break;
1328     case 15: p =
1329 "This is free software: you are free to change and redistribute it.\n"
1330 "There is NO WARRANTY, to the extent permitted by law.\n";
1331       break;
1332     case 16: p =
1333 "This is free software; you can redistribute it and/or modify\n"
1334 "it under the terms of the GNU General Public License as published by\n"
1335 "the Free Software Foundation; either version 3 of the License, or\n"
1336 "(at your option) any later version.\n\n"
1337 "It is distributed in the hope that it will be useful,\n"
1338 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1339 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1340 "GNU General Public License for more details.\n\n"
1341 "You should have received a copy of the GNU General Public License\n"
1342 "along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
1343       break;
1344     case 40: /* short and long usage */
1345     case 41: p = ""; break;
1346     }
1347
1348   return p;
1349 }
1350
1351
1352 /* Set the usage handler.  This function is basically a constructor.  */
1353 void
1354 set_strusage ( const char *(*f)( int ) )
1355 {
1356   strusage_handler = f;
1357 }
1358
1359
1360 #ifdef TEST
1361 static struct {
1362     int verbose;
1363     int debug;
1364     char *outfile;
1365     char *crf;
1366     int myopt;
1367     int echo;
1368     int a_long_one;
1369 }opt;
1370
1371 int
1372 main(int argc, char **argv)
1373 {
1374   ARGPARSE_OPTS opts[] = {
1375     ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1376     ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
1377                                   "was wir ein gegeben haben")),
1378     ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1379     ARGPARSE_s_s('o', "output", 0 ),
1380     ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1381     /* Note that on a non-utf8 terminal the ß might garble the output. */
1382     ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1383     ARGPARSE_o_i('m', "my-option", 0),
1384     ARGPARSE_s_n(500, "a-long-option", 0 ),
1385     ARGPARSE_end
1386   };
1387   ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
1388     int i;
1389
1390     while( arg_parse ( &pargs, opts) ) {
1391         switch( pargs.r_opt ) {
1392           case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
1393           case 'v': opt.verbose++; break;
1394           case 'e': opt.echo++; break;
1395           case 'd': opt.debug++; break;
1396           case 'o': opt.outfile = pargs.r.ret_str; break;
1397           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1398           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1399           case 500: opt.a_long_one++;  break;
1400           default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1401         }
1402     }
1403     for(i=0; i < argc; i++ )
1404         printf("%3d -> (%s)\n", i, argv[i] );
1405     puts("Options:");
1406     if( opt.verbose )
1407         printf("  verbose=%d\n", opt.verbose );
1408     if( opt.debug )
1409         printf("  debug=%d\n", opt.debug );
1410     if( opt.outfile )
1411         printf("  outfile='%s'\n", opt.outfile );
1412     if( opt.crf )
1413         printf("  crffile='%s'\n", opt.crf );
1414     if( opt.myopt )
1415         printf("  myopt=%d\n", opt.myopt );
1416     if( opt.a_long_one )
1417         printf("  a-long-one=%d\n", opt.a_long_one );
1418     if( opt.echo       )
1419         printf("  echo=%d\n", opt.echo );
1420     return 0;
1421 }
1422 #endif
1423
1424 /**** bottom of file ****/