Armor works now
[gnupg.git] / util / argparse.c
1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *  This file is part of WkLib.
4  *
5  *  WkLib is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  WkLib is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  *
19  *
20  * Note: This is an independent version of the one in WkLib
21  */
22
23 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "util.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 index;
51  *          const char *last;
52  *      } internal;               DO NOT CHANGE
53  *  } ARGPARSE_ARGS;
54  *
55  *  typedef struct {
56  *      int         short_opt;
57  *      const char *long_opt;
58  *      unsigned flags;
59  *  } ARGPARSE_OPTS;
60  *
61  *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
62  *
63  * @Description
64  *  This is my replacement for getopt(). See the example for a typical usage.
65  *  Global flags are:
66  *     Bit 0 : Do not remove options form argv
67  *     Bit 1 : Do not stop at last option but return other args
68  *             with r_opt set to -1.
69  *     Bit 2 : Assume options and real args are mixed.
70  *     Bit 3 : Do not use -- to stop option processing.
71  *     Bit 4 : Do not skip the first arg.
72  *     Bit 5 : allow usage of long option with only one dash
73  *     all other bits must be set to zero, this value is modified by the function
74  *     so assume this is write only.
75  *  Local flags (for each option):
76  *     Bit 2-0 : 0 = does not take an argument
77  *               1 = takes int argument
78  *               2 = takes string argument
79  *               3 = takes long argument
80  *               4 = takes ulong argument
81  *     Bit 3 : argument is optional (r_type will the be set to 0)
82  *     Bit 4 : allow 0x etc. prefixed values.
83  *  If can stop the option processing by setting opts to NULL, the function will
84  *  then return 0.
85  * @Return Value
86  *   Returns the args.r_opt or 0 if ready
87  *   r_opt may be -2 to indicate an unknown option.
88  * @See Also
89  *   ArgExpand
90  * @Notes
91  *  You do not need to process the options 'h', '--help' or '--version'
92  *  because this function includes standard help processing; but if you
93  *  specify '-h', '--help' or '--version' you have to do it yourself.
94  *  The option '--' stops argument processing; if bit 1 is set the function
95  *  continues to return normal arguments.
96  *  To process float args or unsigned args you must use a string args and do
97  *  the conversion yourself.
98  * @Example
99  *
100  *     ARGPARSE_OPTS opts[] = {
101  *     { 'v', "verbose",   0 },
102  *     { 'd', "debug",     0 },
103  *     { 'o', "output",    2 },
104  *     { 'c', "cross-ref", 2|8 },
105  *     { 'm', "my-option", 1|8 },
106  *     { 500, "have-no-short-option-for-this-long-option", 0 },
107  *     {0} };
108  *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
109  *
110  *     while( ArgParse( &pargs, &opts) ) {
111  *         switch( pargs.r_opt ) {
112  *           case 'v': opt.verbose++; break;
113  *           case 'd': opt.debug++; break;
114  *           case 'o': opt.outfile = pargs.r.ret_str; break;
115  *           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
116  *           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
117  *           case 500: opt.a_long_one++;  break
118  *           default : pargs.err = 1; break; -- force warning output --
119  *         }
120  *     }
121  *     if( argc > 1 )
122  *         log_fatal( "Too many args");
123  *
124  */
125
126
127 static void set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
128 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
129 static void show_version(void);
130
131
132 int
133 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
134 {
135     int index;
136     int argc;
137     char **argv;
138     char *s, *s2;
139     int i;
140
141     if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
142         arg->internal.index = 0;
143         arg->internal.last = NULL;
144         arg->internal.inarg = 0;
145         arg->internal.stopped= 0;
146         arg->err = 0;
147         arg->flags |= 1<<15; /* mark initialized */
148         if( *arg->argc < 0 )
149             log_bug("Invalid argument for ArgParse\n");
150     }
151     argc = *arg->argc;
152     argv = *arg->argv;
153     index = arg->internal.index;
154
155     if( arg->err ) { /* last option was erroneous */
156         if( arg->r_opt == -3 )
157             s = "Missing argument for option \"%.50s\"\n";
158         else
159             s = "Invalid option \"%.50s\"\n";
160         log_error(s, arg->internal.last? arg->internal.last:"[??]" );
161         if( arg->err != 1 )
162             exit(2);
163         arg->err = 0;
164     }
165
166     if( !index && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
167         argc--; argv++; index++;
168     }
169
170   next_one:
171     if( !argc ) { /* no more args */
172         arg->r_opt = 0;
173         goto leave; /* ready */
174     }
175
176     s = *argv;
177     arg->internal.last = s;
178
179     if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
180         arg->r_opt = -1;  /* not an option but a argument */
181         arg->r_type = 2;
182         arg->r.ret_str = s;
183         argc--; argv++; index++; /* set to next one */
184     }
185     else if( arg->internal.stopped ) { /* ready */
186         arg->r_opt = 0;
187         goto leave;
188     }
189     else if( *s == '-' && s[1] == '-' ) { /* long option */
190         arg->internal.inarg = 0;
191         if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
192             arg->internal.stopped = 1;
193             argc--; argv++; index++;
194             goto next_one;
195         }
196
197         for(i=0; opts[i].short_opt; i++ )
198             if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+2) )
199                 break;
200
201         if( !opts[i].short_opt && !strcmp( "help", s+2) )
202             show_help(opts, arg->flags);
203         else if( !opts[i].short_opt && !strcmp( "version", s+2) )
204             show_version();
205         else if( !opts[i].short_opt && !strcmp( "warranty", s+2) ) {
206             puts( strusage(10) );
207             puts( strusage(31) );
208             exit(0);
209         }
210
211         arg->r_opt = opts[i].short_opt;
212         if( !opts[i].short_opt ) {
213             arg->r_opt = -2; /* unknown option */
214             arg->r.ret_str = s+2;
215         }
216         else if( (opts[i].flags & 7) ) {
217             s2 = argv[1];
218             if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
219                 arg->r_type = 0;               /* because it is optional */
220             }
221             else if( !s2 ) {
222                 arg->r_opt = -3; /* missing argument */
223             }
224             else if( *s2 == '-' && (opts[i].flags & 8) ) {
225                 /* the argument is optional and the next seems to be
226                  * an option. We do not check this possible option
227                  * but assume no argument */
228                 arg->r_type = 0;
229             }
230             else {
231                 set_opt_arg(arg, opts[i].flags, s2);
232                 argc--; argv++; index++; /* skip one */
233             }
234         }
235         else { /* does not take an argument */
236             arg->r_type = 0;
237         }
238         argc--; argv++; index++; /* set to next one */
239     }
240     else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
241         int dash_kludge = 0;
242         i = 0;
243         if( !arg->internal.inarg ) {
244             arg->internal.inarg++;
245             if( arg->flags & (1<<5) ) {
246                 for(i=0; opts[i].short_opt; i++ )
247                     if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
248                         dash_kludge=1;
249                         break;
250                     }
251             }
252         }
253         s += arg->internal.inarg;
254
255         if( !dash_kludge ) {
256             for(i=0; opts[i].short_opt; i++ )
257                 if( opts[i].short_opt == *s )
258                     break;
259         }
260
261         if( !opts[i].short_opt && *s == 'h' )
262             show_help(opts, arg->flags);
263
264         arg->r_opt = opts[i].short_opt;
265         if( !opts[i].short_opt ) {
266             arg->r_opt = -2; /* unknown option */
267             arg->internal.inarg++; /* point to the next arg */
268             arg->r.ret_str = s;
269         }
270         else if( (opts[i].flags & 7) ) {
271             if( s[1] && !dash_kludge ) {
272                 s2 = s+1;
273                 set_opt_arg(arg, opts[i].flags, s2);
274             }
275             else {
276                 s2 = argv[1];
277                 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
278                     arg->r_type = 0;               /* because it is optional */
279                 }
280                 else if( !s2 ) {
281                     arg->r_opt = -3; /* missing argument */
282                 }
283                 else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
284                     /* the argument is optional and the next seems to be
285                      * an option. We do not check this possible option
286                      * but assume no argument */
287                     arg->r_type = 0;
288                 }
289                 else {
290                     set_opt_arg(arg, opts[i].flags, s2);
291                     argc--; argv++; index++; /* skip one */
292                 }
293             }
294             s = "x"; /* so that !s[1] yields false */
295         }
296         else { /* does not take an argument */
297             arg->r_type = 0;
298             arg->internal.inarg++; /* point to the next arg */
299         }
300         if( !s[1] || dash_kludge ) { /* no more concatenated short options */
301             arg->internal.inarg = 0;
302             argc--; argv++; index++;
303         }
304     }
305     else if( arg->flags & (1<<2) ) {
306         arg->r_opt = -1;  /* not an option but a argument */
307         arg->r_type = 2;
308         arg->r.ret_str = s;
309         argc--; argv++; index++; /* set to next one */
310     }
311     else {
312         arg->internal.stopped = 1; /* stop option processing */
313         goto next_one;
314     }
315
316   leave:
317     *arg->argc = argc;
318     *arg->argv = argv;
319     arg->internal.index = index;
320     return arg->r_opt;
321 }
322
323
324
325 static void
326 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
327 {
328     int base = (flags & 16)? 0 : 10;
329
330     switch( arg->r_type = (flags & 7) ) {
331       case 1: /* takes int argument */
332         arg->r.ret_int = (int)strtol(s,NULL,base);
333         break;
334       default:
335       case 2: /* takes string argument */
336         arg->r.ret_str = s;
337         break;
338       case 3: /* takes long argument   */
339         arg->r.ret_long= strtol(s,NULL,base);
340         break;
341       case 4: /* takes ulong argument  */
342         arg->r.ret_ulong= strtoul(s,NULL,base);
343         break;
344     }
345 }
346
347 static void
348 show_help( ARGPARSE_OPTS *opts, unsigned flags )
349 {
350     const char *s;
351
352     puts( strusage(10) );
353     s = strusage(12);
354     if( *s == '\n' )
355         s++;
356     puts(s);
357     if( opts[0].description ) { /* auto format the option description */
358         int i,j, indent;
359         /* get max. length of long options */
360         for(i=indent=0; opts[i].short_opt; i++ ) {
361             if( opts[i].long_opt )
362                 if( (j=strlen(opts[i].long_opt)) > indent && j < 35 )
363                     indent = j;
364         }
365         /* example: " -v, --verbose   Viele Sachen ausgeben" */
366         indent += 10;
367         puts("Options:");
368         for(i=0; opts[i].short_opt; i++ ) {
369             if( opts[i].short_opt < 256 )
370                 printf(" -%c", opts[i].short_opt );
371             else
372                 fputs("   ", stdout);
373             j = 3;
374             if( opts[i].long_opt )
375                 j += printf("%c --%s   ", opts[i].short_opt < 256?',':' ',
376                                           opts[i].long_opt );
377             for(;j < indent; j++ )
378                 putchar(' ');
379             if( (s = opts[i].description) ) {
380                 for(; *s; s++ ) {
381                     if( *s == '\n' ) {
382                         if( s[1] ) {
383                             putchar('\n');
384                             for(j=0;j < indent; j++ )
385                                 putchar(' ');
386                         }
387                     }
388                     else
389                         putchar(*s);
390                 }
391             }
392             putchar('\n');
393         }
394         if( flags & 32 )
395             puts("\n(A single dash may be used instead of the double ones)");
396     }
397     if( *(s=strusage(26)) ) {  /* bug reports to ... */
398         putchar('\n');
399         fputs(s, stdout);
400     }
401     fflush(stdout);
402     exit(0);
403 }
404
405 static void
406 show_version()
407 {
408     const char *s;
409     printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) );
410     if( (s = strusage(24)) && *s ) {
411       #ifdef DEBUG
412         printf(", %s, dbg)\n", s);
413       #else
414         printf(", %s)\n", s);
415       #endif
416     }
417     else {
418       #ifdef DEBUG
419         printf(", dbg)\n");
420       #else
421         printf(")\n");
422       #endif
423     }
424     fflush(stdout);
425     exit(0);
426 }
427
428
429
430 void
431 usage( int level )
432 {
433     static int sentinel=0;
434
435     if( sentinel )
436         return;
437
438     sentinel++;
439     if( !level ) {
440         fputs( strusage(level), stderr ); putc( '\n', stderr );
441         fputs( strusage(31), stderr);
442       #if DEBUG
443         fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) );
444       #else
445         fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) );
446       #endif
447         fflush(stderr);
448     }
449     else if( level == 1 ) {
450         fputs(strusage(level),stderr);putc('\n',stderr);
451         exit(2);}
452     else if( level == 2 ) {
453         puts(strusage(level)); exit(0);}
454     sentinel--;
455 }
456
457
458 const char *
459 default_strusage( int level )
460 {
461     const char *p;
462     switch( level ) {
463       case  0:  p = strusage(10); break;
464       case  1:  p = strusage(11); break;
465       case  2:  p = strusage(12); break;
466       case 10:  p = "WkLib"
467                   #if DOS386 && __WATCOMC__
468                     " (DOS4G)"
469                   #elif DOS386
470                     " (DOSX)"
471                   #elif DOS16RM
472                     " (DOS16RM)"
473                   #elif M_I86VM
474                     " (VCM)"
475                   #elif UNIX || POSIX
476                     " (Posix)"
477                   #elif OS2
478                     " (OS/2)"
479                   #elif WINNT && __CYGWIN32__
480                     " (CygWin)"
481                   #elif WINNT
482                     " (WinNT)"
483                   #elif NETWARE
484                     " (Netware)"
485                   #elif VMS
486                     " (VMS)"
487                   #endif
488                     "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break;
489       case 11:  p = "usage: ?"; break;
490       case 16:
491       case 15:  p = "[Untitled]"; break;
492       case 23:  p = "[unknown]"; break;
493       case 24:  p = ""; break;
494       case 26:  p = ""; break;
495       case 12:  p =
496    "This is free software; you can redistribute it and/or modify\n"
497    "it under the terms of the GNU General Public License as published by\n"
498    "the Free Software Foundation; either version 2 of the License, or\n"
499    "(at your option) any later version.\n\n"
500    "WkLib is distributed in the hope that it will be useful,\n"
501    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
502    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
503    "GNU General Public License for more details.\n\n"
504    "You should have received a copy of the GNU General Public License\n"
505    "along with this program; if not, write to the Free Software\n"
506    "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,"
507                                                                   " USA.\n" ;
508         break;
509       case 22:
510           #if MSDOS
511             #if USE_EMS
512               p = "MSDOS+EMS";
513             #else
514               p = "MSDOS";
515             #endif
516           #elif OS2
517             p = "OS/2";
518           #elif WINNT && __CYGWIN32__
519             p = "CygWin";
520           #elif WINNT
521             p = "WinNT";
522           #elif DOS386
523             p = "DOS386";
524           #elif EMX
525             p = "EMX";
526           #elif DOS16RM
527             p = "DOS16RM";
528           #elif NETWARE
529             p = "Netware";
530           #elif __linux__
531             p = "Linux";
532           #elif UNIX || M_UNIX || M_XENIX
533             p = "UNIX";
534           #elif VMS
535             p = "VMS";
536           #else
537             p = "UnknownOS";
538           #endif
539             break;
540       case 31: p =
541     "This program comes with ABSOLUTELY NO WARRANTY.\n"
542     "This is free software, and you are welcome to redistribute it\n"
543     "under certain conditions. See the file COPYING for details.\n";
544             break;
545       case 32: p = "["
546           #if MSDOS
547               "MSDOS Version"
548           #elif DOS386 && __ZTC__
549             "32-Bit MSDOS Version (Zortech's DOSX)"
550           #elif DOS386
551             "32-Bit MSDOS Version"
552           #elif OS20 && EMX
553             "OS/2 2.x EMX Version"
554           #elif OS20
555             "OS/2 2.x Version"
556           #elif OS2
557             "OS/2 1.x Version"
558           #elif WINNT && __CYGWIN32__
559             "Cygnus WinAPI Version"
560           #elif WINNT
561             "Windoze NT Version"
562           #elif EMX
563             "EMX Version"
564           #elif NETWARE
565             "NLM Version"
566           #elif DOS16RM
567             "DOS16RM Version"
568           #elif __linux__
569             "Linux Version"
570           #elif VMS
571             "OpenVMS Version"
572           #elif POSIX
573             "POSIX Version"
574           #elif M_UNIX || M_XENIX
575             "*IX Version"
576           #endif
577             "]";
578             break;
579       case 33: p =
580           #ifdef MULTI_THREADED
581             "mt"
582           #else
583             ""
584           #endif
585             ; break;
586       case 42:
587       case 43:
588       case 44:
589       case 45: p = ""; break;
590       default: p = "?";
591     }
592
593     return p;
594 }
595
596
597
598 #ifdef TEST
599 static struct {
600     int verbose;
601     int debug;
602     char *outfile;
603     char *crf;
604     int myopt;
605     int echo;
606     int a_long_one;
607 }opt;
608
609 int
610 main(int argc, char **argv)
611 {
612     ARGPARSE_OPTS opts[] = {
613     { 'v', "verbose",   0 , "Laut sein"},
614     { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
615     { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
616     { 'o', "output",    2   },
617     { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
618     { 'm', "my-option", 1|8 },
619     { 500, "a-long-option", 0 },
620     {0} };
621     ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
622     int i;
623
624     while( ArgParse( &pargs, opts) ) {
625         switch( pargs.r_opt ) {
626           case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
627           case 'v': opt.verbose++; break;
628           case 'e': opt.echo++; break;
629           case 'd': opt.debug++; break;
630           case 'o': opt.outfile = pargs.r.ret_str; break;
631           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
632           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
633           case 500: opt.a_long_one++;  break;
634           default : pargs.err = 1; break; /* force warning output */
635         }
636     }
637     for(i=0; i < argc; i++ )
638         printf("%3d -> (%s)\n", i, argv[i] );
639     puts("Options:");
640     if( opt.verbose )
641         printf("  verbose=%d\n", opt.verbose );
642     if( opt.debug )
643         printf("  debug=%d\n", opt.debug );
644     if( opt.outfile )
645         printf("  outfile='%s'\n", opt.outfile );
646     if( opt.crf )
647         printf("  crffile='%s'\n", opt.crf );
648     if( opt.myopt )
649         printf("  myopt=%d\n", opt.myopt );
650     if( opt.a_long_one )
651         printf("  a-long-one=%d\n", opt.a_long_one );
652     if( opt.echo       )
653         printf("  echo=%d\n", opt.echo );
654     return 0;
655 }
656 #endif
657
658 /**** bottom of file ****/