Obsolete option --no-sig-create-check.
[gnupg.git] / util / argparse.c
1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2  * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007,
3  *               2008, 2010, 2012 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <string.h>
26
27 #include "util.h"
28 #include "i18n.h"
29
30
31 /*********************************
32  * @Summary arg_parse
33  *  #include <wk/lib.h>
34  *
35  *  typedef struct {
36  *      char *argc;               pointer to argc (value subject to change)
37  *      char ***argv;             pointer to argv (value subject to change)
38  *      unsigned flags;           Global flags (DO NOT CHANGE)
39  *      int err;                  print error about last option
40  *                                1 = warning, 2 = abort
41  *      int r_opt;                return option
42  *      int r_type;               type of return value (0 = no argument found)
43  *      union {
44  *          int   ret_int;
45  *          long  ret_long
46  *          ulong ret_ulong;
47  *          char *ret_str;
48  *      } r;                      Return values
49  *      struct {
50  *          int idx;
51  *          const char *last;
52  *          void *aliases;
53  *      } internal;               DO NOT CHANGE
54  *  } ARGPARSE_ARGS;
55  *
56  *  typedef struct {
57  *      int         short_opt;
58  *      const char *long_opt;
59  *      unsigned flags;
60  *  } ARGPARSE_OPTS;
61  *
62  *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
63  *
64  * @Description
65  *  This is my replacement for getopt(). See the example for a typical usage.
66  *  Global flags are:
67  *     Bit 0 : Do not remove options form argv
68  *     Bit 1 : Do not stop at last option but return other args
69  *             with r_opt set to -1.
70  *     Bit 2 : Assume options and real args are mixed.
71  *     Bit 3 : Do not use -- to stop option processing.
72  *     Bit 4 : Do not skip the first arg.
73  *     Bit 5 : allow usage of long option with only one dash
74  *     Bit 6 : ignore --version and --help
75  *     all other bits must be set to zero, this value is modified by the
76  *     function, so assume this is write only.
77  *  Local flags (for each option):
78  *     Bit 2-0 : 0 = does not take an argument
79  *               1 = takes int argument
80  *               2 = takes string argument
81  *               3 = takes long argument
82  *               4 = takes ulong argument
83  *     Bit 3 : argument is optional (r_type will the be set to 0)
84  *     Bit 4 : allow 0x etc. prefixed values.
85  *     Bit 7 : this is a command and not an option
86  *  You stop the option processing by setting opts to NULL, the function will
87  *  then return 0.
88  * @Return Value
89  *   Returns the args.r_opt or 0 if ready
90  *   r_opt may be -2/-7 to indicate an unknown option/command.
91  * @See Also
92  *   ArgExpand
93  * @Notes
94  *  You do not need to process the options 'h', '--help' or '--version'
95  *  because this function includes standard help processing; but if you
96  *  specify '-h', '--help' or '--version' you have to do it yourself.
97  *  The option '--' stops argument processing; if bit 1 is set the function
98  *  continues to return normal arguments.
99  *  To process float args or unsigned args you must use a string args and do
100  *  the conversion yourself.
101  * @Example
102  *
103  *     ARGPARSE_OPTS opts[] = {
104  *     { 'v', "verbose",   0 },
105  *     { 'd', "debug",     0 },
106  *     { 'o', "output",    2 },
107  *     { 'c', "cross-ref", 2|8 },
108  *     { 'm', "my-option", 1|8 },
109  *     { 500, "have-no-short-option-for-this-long-option", 0 },
110  *     {0} };
111  *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
112  *
113  *     while( ArgParse( &pargs, &opts) ) {
114  *         switch( pargs.r_opt ) {
115  *           case 'v': opt.verbose++; break;
116  *           case 'd': opt.debug++; break;
117  *           case 'o': opt.outfile = pargs.r.ret_str; break;
118  *           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
119  *           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
120  *           case 500: opt.a_long_one++;  break
121  *           default : pargs.err = 1; break; -- force warning output --
122  *         }
123  *     }
124  *     if( argc > 1 )
125  *         log_fatal( "Too many args");
126  *
127  */
128
129 typedef struct alias_def_s *ALIAS_DEF;
130 struct alias_def_s {
131     ALIAS_DEF next;
132     char *name;   /* malloced buffer with name, \0, value */
133     const char *value; /* ptr into name */
134 };
135
136 /* Object to store the names for the --ignore-invalid-option option.
137    This is a simple linked list.  */
138 typedef struct iio_item_def_s *IIO_ITEM_DEF;
139 struct iio_item_def_s
140 {
141   IIO_ITEM_DEF next;
142   char name[1];      /* String with the long option name.  */
143 };
144
145 static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
146 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
147 static void show_version(void);
148
149 static void
150 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
151 {
152     if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
153         arg->internal.idx = 0;
154         arg->internal.last = NULL;
155         arg->internal.inarg = 0;
156         arg->internal.stopped = 0;
157         arg->internal.aliases = NULL;
158         arg->internal.cur_alias = NULL;
159         arg->internal.iio_list = NULL;
160         arg->err = 0;
161         arg->flags |= 1<<15; /* mark initialized */
162         if( *arg->argc < 0 )
163             log_bug("Invalid argument for ArgParse\n");
164     }
165
166
167     if( arg->err ) { /* last option was erroneous */
168
169         if( filename ) {
170             if( arg->r_opt == -6 )
171               log_error("%s:%u: argument not expected\n", filename, *lineno );
172             else if( arg->r_opt == -5 )
173               log_error("%s:%u: read error\n", filename, *lineno );
174             else if( arg->r_opt == -4 )
175               log_error("%s:%u: keyword too long\n", filename, *lineno );
176             else if( arg->r_opt == -3 )
177               log_error("%s:%u: missing argument\n", filename, *lineno );
178             else if( arg->r_opt == -7 )
179               log_error("%s:%u: invalid command\n", filename, *lineno );
180             else if( arg->r_opt == -10 )
181               log_error("%s:%u: invalid alias definition\n",filename,*lineno);
182             else
183               log_error("%s:%u: invalid option\n", filename, *lineno );
184         }
185         else {
186             if( arg->r_opt == -3 )
187               log_error("Missing argument for option \"%.50s\"\n",
188                         arg->internal.last? arg->internal.last:"[??]" );
189             else if( arg->r_opt == -6 )
190               log_error("Option \"%.50s\" does not expect an argument\n",
191                         arg->internal.last? arg->internal.last:"[??]" );
192             else if( arg->r_opt == -7 )
193               log_error("Invalid command \"%.50s\"\n",
194                         arg->internal.last? arg->internal.last:"[??]" );
195             else if( arg->r_opt == -8 )
196               log_error("Option \"%.50s\" is ambiguous\n",
197                         arg->internal.last? arg->internal.last:"[??]" );
198             else if( arg->r_opt == -9 )
199               log_error("Command \"%.50s\" is ambiguous\n",
200                         arg->internal.last? arg->internal.last:"[??]" );
201             else
202               log_error("Invalid option \"%.50s\"\n",
203                         arg->internal.last? arg->internal.last:"[??]" );
204         }
205         if( arg->err != 1 || arg->r_opt == -5 )
206             exit(2);
207         arg->err = 0;
208     }
209
210     /* clearout the return value union */
211     arg->r.ret_str = NULL;
212     arg->r.ret_long= 0;
213 }
214
215
216 static void
217 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
218 {
219     /* TODO: replace this dummy function with a rea one
220      * and fix the probelms IRIX has with (ALIAS_DEV)arg..
221      * used as lvalue
222      */
223 #if 0
224     ALIAS_DEF a = xmalloc( sizeof *a );
225     a->name = name;
226     a->value = value;
227     a->next = (ALIAS_DEF)arg->internal.aliases;
228     (ALIAS_DEF)arg->internal.aliases = a;
229 #endif
230 }
231
232
233 /* Return true if KEYWORD is in the ignore-invalid-option list.  */
234 static int
235 ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
236 {
237   IIO_ITEM_DEF item = arg->internal.iio_list;
238
239   for (; item; item = item->next)
240     if (!strcmp (item->name, keyword))
241       return 1;
242   return 0;
243 }
244
245
246 /* Add the keywords up to the next LF to the list of to be ignored
247    options.  After returning FP will either be at EOF or the next
248    character read wll be the first of a new line.  */
249 static void
250 ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
251 {
252   IIO_ITEM_DEF item;
253   int c;
254   char name[100];
255   int namelen = 0;
256   int ready = 0;
257   enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
258
259   while (!ready)
260     {
261       c = getc (fp);
262       if (c == '\n')
263         ready = 1;
264       else if (c == EOF)
265         {
266           c = '\n';
267           ready = 1;
268         }
269     again:
270       switch (state)
271         {
272         case skipWS:
273           if (!isascii (c) || !isspace(c))
274             {
275               namelen = 0;
276               state = collectNAME;
277               goto again;
278             }
279           break;
280
281         case collectNAME:
282           if (isspace (c))
283             {
284               state = addNAME;
285               goto again;
286             }
287           else if (namelen < DIM(name)-1)
288             name[namelen++] = c;
289           else /* Too long.  */
290             state = skipNAME;
291           break;
292
293         case skipNAME:
294           if (isspace (c))
295             {
296               state = skipWS;
297               goto again;
298             }
299           break;
300
301         case addNAME:
302           name[namelen] = 0;
303           if (!ignore_invalid_option_p (arg, name))
304             {
305               item = xmalloc (sizeof *item + namelen);
306               strcpy (item->name, name);
307               item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
308               arg->internal.iio_list = item;
309             }
310           state = skipWS;
311           goto again;
312         }
313     }
314 }
315
316
317 /* Clear the entire ignore-invalid-option list.  */
318 static void
319 ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
320 {
321   IIO_ITEM_DEF item, tmpitem;
322
323   for (item = arg->internal.iio_list; item; item = tmpitem)
324     {
325       tmpitem = item->next;
326       xfree (item);
327     }
328   arg->internal.iio_list = NULL;
329 }
330
331 /****************
332  * Get options from a file.
333  * Lines starting with '#' are comment lines.
334  * Syntax is simply a keyword and the argument.
335  * Valid keywords are all keywords from the long_opt list without
336  * the leading dashes. The special keywords "help", "warranty" and "version"
337  * are not valid here.
338  * The special keyword "alias" may be used to store alias definitions,
339  * which are later expanded like long options.
340  * Caller must free returned strings.
341  * If called with FP set to NULL command line args are parse instead.
342  *
343  * Q: Should we allow the syntax
344  *     keyword = value
345  *    and accept for boolean options a value of 1/0, yes/no or true/false?
346  * Note: Abbreviation of options is here not allowed.
347  */
348 int
349 optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
350                ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
351 {
352     int state, i, c;
353     int idx=0;
354     char keyword[100];
355     char *buffer = NULL;
356     size_t buflen = 0;
357     int inverse=0;
358     int in_alias=0;
359
360     if( !fp ) /* same as arg_parse() in this case */
361         return arg_parse( arg, opts );
362
363     initialize( arg, filename, lineno );
364
365     /* find the next keyword */
366     state = i = 0;
367     for(;;) {
368         c=getc(fp);
369         if( c == '\n' || c== EOF ) {
370             if( c != EOF )
371                 ++*lineno;
372             if( state == -1 )
373                 break;
374             else if( state == 2 ) {
375                 keyword[i] = 0;
376                 for(i=0; opts[i].short_opt; i++ )
377                     if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
378                         break;
379                 idx = i;
380                 arg->r_opt = opts[idx].short_opt;
381                 if( inverse ) /* this does not have an effect, hmmm */
382                     arg->r_opt = -arg->r_opt;
383                 if( !opts[idx].short_opt ) { /* unknown command/option */
384                     if (!strcmp (keyword, "ignore-invalid-option")) {
385                         /* No argument - ignore this meta option.  */
386                         state = i = 0;
387                         continue;
388                     }
389                     else if (ignore_invalid_option_p (arg, keyword)) {
390                         /* This invalid option is in the iio list.  */
391                         state = i = 0;
392                         continue;
393                     }
394                     arg->r_opt = (opts[idx].flags & 256)? -7:-2;
395                 }
396                 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
397                     arg->r_type = 0;           /* okay */
398                 else if( (opts[idx].flags & 8) )  /* argument is optional */
399                     arg->r_type = 0;           /* okay */
400                 else                           /* required argument */
401                     arg->r_opt = -3;           /* error */
402
403                 break;
404             }
405             else if( state == 3 ) {            /* no argument found */
406                 if( in_alias )
407                     arg->r_opt = -3;           /* error */
408                 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
409                     arg->r_type = 0;           /* okay */
410                 else if( (opts[idx].flags & 8) )  /* no optional argument */
411                     arg->r_type = 0;           /* okay */
412                 else                           /* no required argument */
413                     arg->r_opt = -3;           /* error */
414                 break;
415             }
416             else if( state == 4 ) {     /* have an argument */
417                 if( in_alias ) {
418                     if( !buffer )
419                         arg->r_opt = -6;
420                     else {
421                         char *p;
422
423                         buffer[i] = 0;
424                         p = strpbrk( buffer, " \t" );
425                         if( p ) {
426                             *p++ = 0;
427                             trim_spaces( p );
428                         }
429                         if( !p || !*p ) {
430                             xfree( buffer );
431                             arg->r_opt = -10;
432                         }
433                         else {
434                             store_alias( arg, buffer, p );
435                         }
436                     }
437                 }
438                 else if( !(opts[idx].flags & 7) )  /* does not take an arg */
439                     arg->r_opt = -6;        /* error */
440                 else {
441                     char *p;
442                     if( !buffer ) {
443                         keyword[i] = 0;
444                         buffer = xstrdup(keyword);
445                     }
446                     else
447                         buffer[i] = 0;
448
449                     trim_spaces( buffer );
450                     p = buffer;
451                     /* remove quotes if they totally enclose the
452                        string, and do not occur within the string */
453                     if( *p == '"' && p[strlen(p)-1]=='"') {
454                         char *p2=p;
455
456                         while(*(++p2))
457                           if(*p2=='"')
458                             break;
459
460                         if(*p2=='"' && *(p2+1)=='\0') {
461                           p[strlen(p)-1] = 0;
462                           p++;
463                         }
464                     }
465                     if( !set_opt_arg(arg, opts[idx].flags, p) )
466                         xfree(buffer);
467                 }
468                 break;
469             }
470             else if( c == EOF ) {
471                 ignore_invalid_option_clear (arg);
472                 if( ferror(fp) )
473                     arg->r_opt = -5;   /* read error */
474                 else
475                     arg->r_opt = 0;    /* eof */
476                 break;
477             }
478             state = 0;
479             i = 0;
480         }
481         else if( state == -1 )
482             ; /* skip */
483         else if( !state && isspace(c) )
484             ; /* skip leading white space */
485         else if( !state && c == '#' )
486             state = 1;  /* start of a comment */
487         else if( state == 1 )
488             ; /* skip comments */
489         else if( state == 2 && isspace(c) ) {
490             keyword[i] = 0;
491             for(i=0; opts[i].short_opt; i++ )
492                 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
493                     break;
494             idx = i;
495             arg->r_opt = opts[idx].short_opt;
496             if( !opts[idx].short_opt ) {
497                 if( !strcmp( keyword, "alias" ) ) {
498                     in_alias = 1;
499                     state = 3;
500                 }
501                 else if (!strcmp (keyword, "ignore-invalid-option")) {
502                     ignore_invalid_option_add (arg, fp);
503                     state = i = 0;
504                     ++*lineno;
505                 }
506                 else if (ignore_invalid_option_p (arg, keyword))
507                     state = 1; /* Process like a comment.  */
508                 else {
509                     arg->r_opt = (opts[idx].flags & 256)? -7:-2;
510                     state = -1;        /* skip rest of line and leave */
511                 }
512             }
513             else
514                 state = 3;
515         }
516         else if( state == 3 ) { /* skip leading spaces of the argument */
517             if( !isspace(c) ) {
518                 i = 0;
519                 keyword[i++] = c;
520                 state = 4;
521             }
522         }
523         else if( state == 4 ) { /* collect the argument */
524             if( buffer ) {
525                 if( i < buflen-1 )
526                     buffer[i++] = c;
527                 else {
528                     buflen += 50;
529                     buffer = xrealloc(buffer, buflen);
530                     buffer[i++] = c;
531                 }
532             }
533             else if( i < DIM(keyword)-1 )
534                 keyword[i++] = c;
535             else {
536                 buflen = DIM(keyword)+50;
537                 buffer = xmalloc(buflen);
538                 memcpy(buffer, keyword, i);
539                 buffer[i++] = c;
540             }
541         }
542         else if( i >= DIM(keyword)-1 ) {
543             arg->r_opt = -4;   /* keyword to long */
544             state = -1;        /* skip rest of line and leave */
545         }
546         else {
547             keyword[i++] = c;
548             state = 2;
549         }
550     }
551
552     return arg->r_opt;
553 }
554
555
556
557 static int
558 find_long_option( ARGPARSE_ARGS *arg,
559                   ARGPARSE_OPTS *opts, const char *keyword )
560 {
561     int i;
562     size_t n;
563
564     /* Would be better if we can do a binary search, but it is not
565        possible to reorder our option table because we would mess
566        up our help strings - What we can do is: Build a nice option
567        lookup table wehn this function is first invoked */
568     if( !*keyword )
569         return -1;
570     for(i=0; opts[i].short_opt; i++ )
571         if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
572             return i;
573 #if 0
574     {
575         ALIAS_DEF a;
576         /* see whether it is an alias */
577         for( a = args->internal.aliases; a; a = a->next ) {
578             if( !strcmp( a->name, keyword) ) {
579                 /* todo: must parse the alias here */
580                 args->internal.cur_alias = a;
581                 return -3; /* alias available */
582             }
583         }
584     }
585 #endif
586     /* not found, see whether it is an abbreviation */
587     /* aliases may not be abbreviated */
588     n = strlen( keyword );
589     for(i=0; opts[i].short_opt; i++ ) {
590         if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
591             int j;
592             for(j=i+1; opts[j].short_opt; j++ ) {
593                 if( opts[j].long_opt
594                     && !strncmp( opts[j].long_opt, keyword, n ) )
595                     return -2;  /* abbreviation is ambiguous */
596             }
597             return i;
598         }
599     }
600     return -1;
601 }
602
603 int
604 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
605 {
606     int idx;
607     int argc;
608     char **argv;
609     char *s, *s2;
610     int i;
611
612     initialize( arg, NULL, NULL );
613     argc = *arg->argc;
614     argv = *arg->argv;
615     idx = arg->internal.idx;
616
617     if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
618         argc--; argv++; idx++;
619     }
620
621   next_one:
622     if( !argc ) { /* no more args */
623         arg->r_opt = 0;
624         goto leave; /* ready */
625     }
626
627     s = *argv;
628     arg->internal.last = s;
629
630     if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
631         arg->r_opt = -1;  /* not an option but a argument */
632         arg->r_type = 2;
633         arg->r.ret_str = s;
634         argc--; argv++; idx++; /* set to next one */
635     }
636     else if( arg->internal.stopped ) { /* ready */
637         arg->r_opt = 0;
638         goto leave;
639     }
640     else if( *s == '-' && s[1] == '-' ) { /* long option */
641         char *argpos;
642
643         arg->internal.inarg = 0;
644         if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
645             arg->internal.stopped = 1;
646             argc--; argv++; idx++;
647             goto next_one;
648         }
649
650         argpos = strchr( s+2, '=' );
651         if( argpos )
652             *argpos = 0;
653         i = find_long_option( arg, opts, s+2 );
654         if( argpos )
655             *argpos = '=';
656
657         if( i < 0 && !strcmp( "help", s+2) ) {
658             if( !(arg->flags & (1<<6)) ) {
659                 show_help(opts, arg->flags);
660             }
661         }
662         else if( i < 0 && !strcmp( "version", s+2) ) {
663             if( !(arg->flags & (1<<6)) ) {
664                 show_version();
665                 exit(0);
666             }
667         }
668         else if( i < 0 && !strcmp( "warranty", s+2) ) {
669             puts( strusage(16) );
670             exit(0);
671         }
672         else if( i < 0 && !strcmp( "dump-options", s+2) ) {
673             for(i=0; opts[i].short_opt; i++ ) {
674                 if( opts[i].long_opt )
675                     printf( "--%s\n", opts[i].long_opt );
676             }
677             fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
678             exit(0);
679         }
680
681         if( i == -2 ) /* ambiguous option */
682             arg->r_opt = -8;
683         else if( i == -1 ) {
684             arg->r_opt = -2;
685             arg->r.ret_str = s+2;
686         }
687         else
688             arg->r_opt = opts[i].short_opt;
689         if( i < 0 )
690             ;
691         else if( (opts[i].flags & 7) ) {
692             if( argpos ) {
693                 s2 = argpos+1;
694                 if( !*s2 )
695                     s2 = NULL;
696             }
697             else
698                 s2 = argv[1];
699             if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
700                 arg->r_type = 0;               /* because it is optional */
701             }
702             else if( !s2 ) {
703                 arg->r_opt = -3; /* missing argument */
704             }
705             else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
706                 /* the argument is optional and the next seems to be
707                  * an option. We do not check this possible option
708                  * but assume no argument */
709                 arg->r_type = 0;
710             }
711             else {
712                 set_opt_arg(arg, opts[i].flags, s2);
713                 if( !argpos ) {
714                     argc--; argv++; idx++; /* skip one */
715                 }
716             }
717         }
718         else { /* does not take an argument */
719             if( argpos )
720                 arg->r_type = -6; /* argument not expected */
721             else
722                 arg->r_type = 0;
723         }
724         argc--; argv++; idx++; /* set to next one */
725     }
726     else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
727         int dash_kludge = 0;
728         i = 0;
729         if( !arg->internal.inarg ) {
730             arg->internal.inarg++;
731             if( arg->flags & (1<<5) ) {
732                 for(i=0; opts[i].short_opt; i++ )
733                     if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
734                         dash_kludge=1;
735                         break;
736                     }
737             }
738         }
739         s += arg->internal.inarg;
740
741         if( !dash_kludge ) {
742             for(i=0; opts[i].short_opt; i++ )
743                 if( opts[i].short_opt == *s )
744                     break;
745         }
746
747         if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) {
748             if( !(arg->flags & (1<<6)) ) {
749                 show_help(opts, arg->flags);
750             }
751         }
752
753         arg->r_opt = opts[i].short_opt;
754         if( !opts[i].short_opt ) {
755             arg->r_opt = (opts[i].flags & 256)? -7:-2;
756             arg->internal.inarg++; /* point to the next arg */
757             arg->r.ret_str = s;
758         }
759         else if( (opts[i].flags & 7) ) {
760             if( s[1] && !dash_kludge ) {
761                 s2 = s+1;
762                 set_opt_arg(arg, opts[i].flags, s2);
763             }
764             else {
765                 s2 = argv[1];
766                 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
767                     arg->r_type = 0;               /* because it is optional */
768                 }
769                 else if( !s2 ) {
770                     arg->r_opt = -3; /* missing argument */
771                 }
772                 else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
773                     /* the argument is optional and the next seems to be
774                      * an option. We do not check this possible option
775                      * but assume no argument */
776                     arg->r_type = 0;
777                 }
778                 else {
779                     set_opt_arg(arg, opts[i].flags, s2);
780                     argc--; argv++; idx++; /* skip one */
781                 }
782             }
783             s = "x"; /* so that !s[1] yields false */
784         }
785         else { /* does not take an argument */
786             arg->r_type = 0;
787             arg->internal.inarg++; /* point to the next arg */
788         }
789         if( !s[1] || dash_kludge ) { /* no more concatenated short options */
790             arg->internal.inarg = 0;
791             argc--; argv++; idx++;
792         }
793     }
794     else if( arg->flags & (1<<2) ) {
795         arg->r_opt = -1;  /* not an option but a argument */
796         arg->r_type = 2;
797         arg->r.ret_str = s;
798         argc--; argv++; idx++; /* set to next one */
799     }
800     else {
801         arg->internal.stopped = 1; /* stop option processing */
802         goto next_one;
803     }
804
805   leave:
806     *arg->argc = argc;
807     *arg->argv = argv;
808     arg->internal.idx = idx;
809     return arg->r_opt;
810 }
811
812
813
814 static int
815 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
816 {
817     int base = (flags & 16)? 0 : 10;
818
819     switch( arg->r_type = (flags & 7) ) {
820       case 1: /* takes int argument */
821         arg->r.ret_int = (int)strtol(s,NULL,base);
822         return 0;
823       case 3: /* takes long argument   */
824         arg->r.ret_long= strtol(s,NULL,base);
825         return 0;
826       case 4: /* takes ulong argument  */
827         arg->r.ret_ulong= strtoul(s,NULL,base);
828         return 0;
829       case 2: /* takes string argument */
830       default:
831         arg->r.ret_str = s;
832         return 1;
833     }
834 }
835
836
837 static size_t
838 long_opt_strlen( ARGPARSE_OPTS *o )
839 {
840     size_t n = strlen(o->long_opt);
841
842     if( o->description && *o->description == '|' ) {
843         const char *s;
844
845         s=o->description+1;
846         if( *s != '=' )
847             n++;
848         for(; *s && *s != '|'; s++ )
849             n++;
850     }
851     return n;
852 }
853
854 /****************
855  * Print formatted help. The description string has some special
856  * meanings:
857  *  - A description string which is "@" suppresses help output for
858  *    this option
859  *  - a description,ine which starts with a '@' and is followed by
860  *    any other characters is printed as is; this may be used for examples
861  *    ans such.
862  *  - A description which starts with a '|' outputs the string between this
863  *    bar and the next one as arguments of the long option.
864  */
865 static void
866 show_help( ARGPARSE_OPTS *opts, unsigned flags )
867 {
868     const char *s;
869
870     show_version();
871     putchar('\n');
872     s = strusage(41);
873     puts(s);
874     if( opts[0].description ) { /* auto format the option description */
875         int i,j, indent;
876         /* get max. length of long options */
877         for(i=indent=0; opts[i].short_opt; i++ ) {
878             if( opts[i].long_opt )
879                 if( !opts[i].description || *opts[i].description != '@' )
880                     if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
881                          indent = j;
882         }
883         /* example: " -v, --verbose   Viele Sachen ausgeben" */
884         indent += 10;
885         if( *opts[0].description != '@' )
886             puts("Options:");
887         for(i=0; opts[i].short_opt; i++ ) {
888             s = _( opts[i].description );
889             if( s && *s== '@' && !s[1] ) /* hide this line */
890                 continue;
891             if( s && *s == '@' ) { /* unindented comment only line */
892                 for(s++; *s; s++ ) {
893                     if( *s == '\n' ) {
894                         if( s[1] )
895                             putchar('\n');
896                     }
897                     else
898                         putchar(*s);
899                 }
900                 putchar('\n');
901                 continue;
902             }
903
904             j = 3;
905             if( opts[i].short_opt < 256 ) {
906                 printf(" -%c", opts[i].short_opt );
907                 if( !opts[i].long_opt ) {
908                     if(s && *s == '|' ) {
909                         putchar(' '); j++;
910                         for(s++ ; *s && *s != '|'; s++, j++ )
911                             putchar(*s);
912                         if( *s )
913                             s++;
914                     }
915                 }
916             }
917             else
918                 fputs("   ", stdout);
919             if( opts[i].long_opt ) {
920                 j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
921                                        opts[i].long_opt );
922                 if(s && *s == '|' ) {
923                     if( *++s != '=' ) {
924                         putchar(' ');
925                         j++;
926                     }
927                     for( ; *s && *s != '|'; s++, j++ )
928                         putchar(*s);
929                     if( *s )
930                         s++;
931                 }
932                 fputs("   ", stdout);
933                 j += 3;
934             }
935             for(;j < indent; j++ )
936                 putchar(' ');
937             if( s ) {
938                 if( *s && j > indent ) {
939                     putchar('\n');
940                     for(j=0;j < indent; j++ )
941                         putchar(' ');
942                 }
943                 for(; *s; s++ ) {
944                     if( *s == '\n' ) {
945                         if( s[1] ) {
946                             putchar('\n');
947                             for(j=0;j < indent; j++ )
948                                 putchar(' ');
949                         }
950                     }
951                     else
952                         putchar(*s);
953                 }
954             }
955             putchar('\n');
956         }
957         if( flags & 32 )
958             puts("\n(A single dash may be used instead of the double ones)");
959     }
960     if( (s=strusage(19)) ) {  /* bug reports to ... */
961         putchar('\n');
962         fputs(s, stdout);
963     }
964     fflush(stdout);
965     exit(0);
966 }
967
968 static void
969 show_version()
970 {
971     const char *s;
972     int i;
973     /* version line */
974     fputs(strusage(11), stdout);
975     if( (s=strusage(12)) )
976         printf(" (%s)", s );
977     printf(" %s\n", strusage(13) );
978     /* additional version lines */
979     for(i=20; i < 30; i++ )
980         if( (s=strusage(i)) )
981             printf("%s\n", s );
982     /* copyright string */
983     if( (s=strusage(14)) )
984         printf("%s\n", s );
985     /* Licence string.  */
986     if( (s=strusage (10)) )
987         printf("%s\n", s );
988     /* copying conditions */
989     if( (s=strusage(15)) )
990         fputs(s, stdout);
991     /* thanks */
992     if( (s=strusage(18)) )
993         fputs(s, stdout);
994     /* additional program info */
995     for(i=30; i < 40; i++ )
996         if( (s=strusage(i)) )
997             fputs( (const byte*)s, stdout);
998     fflush(stdout);
999 }
1000
1001
1002 void
1003 usage( int level )
1004 {
1005     if( !level ) {
1006         fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
1007                                                      strusage(14) );
1008         fflush(stderr);
1009     }
1010     else if( level == 1 ) {
1011         fputs(strusage(40),stderr);
1012         exit(2);
1013     }
1014     else if( level == 2 ) {
1015         puts(strusage(41));
1016         exit(0);
1017     }
1018 }
1019
1020 /* Level
1021  *     0: Copyright String auf stderr ausgeben
1022  *     1: Kurzusage auf stderr ausgeben und beenden
1023  *     2: Langusage auf stdout ausgeben und beenden
1024  *    10: Return license info string
1025  *    11: name of program
1026  *    12: optional name of package which includes this program.
1027  *    13: version  string
1028  *    14: copyright string
1029  *    15: Short copying conditions (with LFs)
1030  *    16: Long copying conditions (with LFs)
1031  *    17: Optional printable OS name
1032  *    18: Optional thanks list   (with LFs)
1033  *    19: Bug report info
1034  *20..29: Additional lib version strings.
1035  *30..39: Additional program info (with LFs)
1036  *    40: short usage note (with LF)
1037  *    41: long usage note (with LF)
1038  */
1039 const char *
1040 default_strusage( int level )
1041 {
1042     const char *p = NULL;
1043     switch( level ) {
1044       case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
1045                     "<http://gnu.org/licenses/gpl.html>");
1046         break;
1047       case 11: p = "foo"; break;
1048       case 13: p = "0.0"; break;
1049       case 14: p = "Copyright (C) 2015 Free Software Foundation, Inc."; break;
1050       case 15: p =
1051 "This is free software: you are free to change and redistribute it.\n"
1052 "There is NO WARRANTY, to the extent permitted by law.\n";
1053         break;
1054       case 16:  p =
1055 "This is free software; you can redistribute it and/or modify\n"
1056 "it under the terms of the GNU General Public License as published by\n"
1057 "the Free Software Foundation; either version 3 of the License, or\n"
1058 "(at your option) any later version.\n\n"
1059 "It is distributed in the hope that it will be useful,\n"
1060 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1061 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1062 "GNU General Public License for more details.\n\n"
1063 "You should have received a copy of the GNU General Public License\n"
1064 "along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
1065         break;
1066       case 40: /* short and long usage */
1067       case 41: p = ""; break;
1068     }
1069
1070     return p;
1071 }
1072
1073
1074
1075 #ifdef TEST
1076 static struct {
1077     int verbose;
1078     int debug;
1079     char *outfile;
1080     char *crf;
1081     int myopt;
1082     int echo;
1083     int a_long_one;
1084 }opt;
1085
1086 int
1087 main(int argc, char **argv)
1088 {
1089     ARGPARSE_OPTS opts[] = {
1090     { 'v', "verbose",   0 , "Laut sein"},
1091     { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
1092     { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
1093     { 'o', "output",    2   },
1094     { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
1095     { 'm', "my-option", 1|8 },
1096     { 500, "a-long-option", 0 },
1097     {0} };
1098     ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
1099     int i;
1100
1101     while( ArgParse( &pargs, opts) ) {
1102         switch( pargs.r_opt ) {
1103           case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
1104           case 'v': opt.verbose++; break;
1105           case 'e': opt.echo++; break;
1106           case 'd': opt.debug++; break;
1107           case 'o': opt.outfile = pargs.r.ret_str; break;
1108           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1109           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1110           case 500: opt.a_long_one++;  break;
1111           default : pargs.err = 1; break; /* force warning output */
1112         }
1113     }
1114     for(i=0; i < argc; i++ )
1115         printf("%3d -> (%s)\n", i, argv[i] );
1116     puts("Options:");
1117     if( opt.verbose )
1118         printf("  verbose=%d\n", opt.verbose );
1119     if( opt.debug )
1120         printf("  debug=%d\n", opt.debug );
1121     if( opt.outfile )
1122         printf("  outfile=`%s'\n", opt.outfile );
1123     if( opt.crf )
1124         printf("  crffile=`%s'\n", opt.crf );
1125     if( opt.myopt )
1126         printf("  myopt=%d\n", opt.myopt );
1127     if( opt.a_long_one )
1128         printf("  a-long-one=%d\n", opt.a_long_one );
1129     if( opt.echo       )
1130         printf("  echo=%d\n", opt.echo );
1131     return 0;
1132 }
1133 #endif
1134
1135 /**** bottom of file ****/