initially checkin
[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 #ifdef DOCUMENTATION
31 @Summary arg_parse
32  #include <wk/lib.h>
33
34  typedef struct {
35      char *argc;            /* pointer to argc (value subject to change) */
36      char ***argv;          /* pointer to argv (value subject to change) */
37      unsigned flags;        /* Global flags (DO NOT CHANGE) */
38      int err;               /* print error about last option */
39                             /* 1 = warning, 2 = abort */
40      int r_opt;             /* return option */
41      int r_type;            /* type of return value (0 = no argument found)*/
42      union {
43          int   ret_int;
44          long  ret_long
45          ulong ret_ulong;
46          char *ret_str;
47      } r;                   /* Return values */
48      struct {
49          int index;
50          const char *last;
51      } internal;            /* DO NOT CHANGE */
52  } ARGPARSE_ARGS;
53
54  typedef struct {
55      int         short_opt;
56      const char *long_opt;
57      unsigned flags;
58  } ARGPARSE_OPTS;
59
60  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
61
62 @Description
63  This is my replacement for getopt(). See the example for a typical usage.
64  Global flags are:
65     Bit 0 : Do not remove options form argv
66     Bit 1 : Do not stop at last option but return other args
67             with r_opt set to -1.
68     Bit 2 : Assume options and real args are mixed.
69     Bit 3 : Do not use -- to stop option processing.
70     Bit 4 : Do not skip the first arg.
71     Bit 5 : allow usage of long option with only one dash
72     all other bits must be set to zero, this value is modified by the function
73     so assume this is write only.
74  Local flags (for each option):
75     Bit 2-0 : 0 = does not take an argument
76               1 = takes int argument
77               2 = takes string argument
78               3 = takes long argument
79               4 = takes ulong argument
80     Bit 3 : argument is optional (r_type will the be set to 0)
81     Bit 4 : allow 0x etc. prefixed values.
82  If can stop the option processing by setting opts to NULL, the function will
83  then return 0.
84 @Return Value
85   Returns the args.r_opt or 0 if ready
86   r_opt may be -2 to indicate an unknown option.
87 @See Also
88   ArgExpand
89 @Notes
90  You do not need to process the options 'h', '--help' or '--version'
91  because this function includes standard help processing; but if you
92  specify '-h', '--help' or '--version' you have to do it yourself.
93  The option '--' stops argument processing; if bit 1 is set the function
94  continues to return normal arguments.
95  To process float args or unsigned args you must use a string args and do
96  the conversion yourself.
97 @Example
98
99     ARGPARSE_OPTS opts[] = {
100     { 'v', "verbose",   0 },
101     { 'd', "debug",     0 },
102     { 'o', "output",    2 },
103     { 'c', "cross-ref", 2|8 },
104     { 'm', "my-option", 1|8 },
105     { 500, "have-no-short-option-for-this-long-option", 0 },
106     {0} };
107     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
108
109     while( ArgParse( &pargs, &opts) ) {
110         switch( pargs.r_opt ) {
111           case 'v': opt.verbose++; break;
112           case 'd': opt.debug++; break;
113           case 'o': opt.outfile = pargs.r.ret_str; break;
114           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
115           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
116           case 500: opt.a_long_one++;  break
117           default : pargs.err = 1; break; /* force warning output */
118         }
119     }
120     if( argc > 1 )
121         log_fatal( "Too many args");
122
123 #endif /*DOCUMENTATION*/
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     fflush(stdout);
398     exit(0);
399 }
400
401 static void
402 show_version()
403 {
404     const char *s;
405     printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) );
406     if( (s = strusage(24)) && *s ) {
407       #ifdef DEBUG
408         printf(", %s, dbg)\n", s);
409       #else
410         printf(", %s)\n", s);
411       #endif
412     }
413     else {
414       #ifdef DEBUG
415         printf(", dbg)\n");
416       #else
417         printf(")\n");
418       #endif
419     }
420     fflush(stdout);
421     exit(0);
422 }
423
424
425
426 void
427 usage( int level )
428 {
429     static int sentinel=0;
430
431     if( sentinel )
432         return;
433
434     sentinel++;
435     if( !level ) {
436         fputs( strusage(level), stderr ); putc( '\n', stderr );
437         fputs( strusage(31), stderr);
438       #if DEBUG
439         fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) );
440       #else
441         fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) );
442       #endif
443         fflush(stderr);
444     }
445     else if( level == 1 ) {
446         fputs(strusage(level),stderr);putc('\n',stderr);
447         exit(2);}
448     else if( level == 2 ) {
449         puts(strusage(level)); exit(0);}
450     sentinel--;
451 }
452
453
454 const char *
455 default_strusage( int level )
456 {
457     const char *p;
458     switch( level ) {
459       case  0:  p = strusage(10); break;
460       case  1:  p = strusage(11); break;
461       case  2:  p = strusage(12); break;
462       case 10:  p = "WkLib"
463                   #if DOS386 && __WATCOMC__
464                     " (DOS4G)"
465                   #elif DOS386
466                     " (DOSX)"
467                   #elif DOS16RM
468                     " (DOS16RM)"
469                   #elif M_I86VM
470                     " (VCM)"
471                   #elif UNIX || POSIX
472                     " (Posix)"
473                   #elif OS2
474                     " (OS/2)"
475                   #elif WINNT && __CYGWIN32__
476                     " (CygWin)"
477                   #elif WINNT
478                     " (WinNT)"
479                   #elif NETWARE
480                     " (Netware)"
481                   #elif VMS
482                     " (VMS)"
483                   #endif
484                     "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break;
485       case 11:  p = "usage: ?"; break;
486       case 16:
487       case 15:  p = "[Untitled]"; break;
488       case 23:  p = "[unknown]"; break;
489       case 24:  p = ""; break;
490       case 12:  p =
491    "This is free software; you can redistribute it and/or modify\n"
492    "it under the terms of the GNU General Public License as published by\n"
493    "the Free Software Foundation; either version 2 of the License, or\n"
494    "(at your option) any later version.\n\n"
495    "WkLib is distributed in the hope that it will be useful,\n"
496    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
497    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
498    "GNU General Public License for more details.\n\n"
499    "You should have received a copy of the GNU General Public License\n"
500    "along with this program; if not, write to the Free Software\n"
501    "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,"
502                                                                   " USA.\n" ;
503         break;
504       case 22:
505           #if MSDOS
506             #if USE_EMS
507               p = "MSDOS+EMS";
508             #else
509               p = "MSDOS";
510             #endif
511           #elif OS2
512             p = "OS/2";
513           #elif WINNT && __CYGWIN32__
514             p = "CygWin";
515           #elif WINNT
516             p = "WinNT";
517           #elif DOS386
518             p = "DOS386";
519           #elif EMX
520             p = "EMX";
521           #elif DOS16RM
522             p = "DOS16RM";
523           #elif NETWARE
524             p = "Netware";
525           #elif __linux__
526             p = "Linux";
527           #elif UNIX || M_UNIX || M_XENIX
528             p = "UNIX";
529           #elif VMS
530             p = "VMS";
531           #else
532             p = "UnknownOS";
533           #endif
534             break;
535       case 31: p =
536     "This program comes with ABSOLUTELY NO WARRANTY.\n"
537     "This is free software, and you are welcome to redistribute it\n"
538     "under certain conditions. See the file COPYING for details.\n";
539             break;
540       case 32: p = "["
541           #if MSDOS
542               "MSDOS Version"
543           #elif DOS386 && __ZTC__
544             "32-Bit MSDOS Version (Zortech's DOSX)"
545           #elif DOS386
546             "32-Bit MSDOS Version"
547           #elif OS20 && EMX
548             "OS/2 2.x EMX Version"
549           #elif OS20
550             "OS/2 2.x Version"
551           #elif OS2
552             "OS/2 1.x Version"
553           #elif WINNT && __CYGWIN32__
554             "Cygnus WinAPI Version"
555           #elif WINNT
556             "Windoze NT Version"
557           #elif EMX
558             "EMX Version"
559           #elif NETWARE
560             "NLM Version"
561           #elif DOS16RM
562             "DOS16RM Version"
563           #elif __linux__
564             "Linux Version"
565           #elif VMS
566             "OpenVMS Version"
567           #elif POSIX
568             "POSIX Version"
569           #elif M_UNIX || M_XENIX
570             "*IX Version"
571           #endif
572             "]";
573             break;
574       case 33: p =
575           #ifdef MULTI_THREADED
576             "mt"
577           #else
578             ""
579           #endif
580             ; break;
581       case 42:
582       case 43:
583       case 44:
584       case 45: p = ""; break;
585       default: p = "?";
586     }
587
588     return p;
589 }
590
591
592
593 #ifdef TEST
594 static struct {
595     int verbose;
596     int debug;
597     char *outfile;
598     char *crf;
599     int myopt;
600     int echo;
601     int a_long_one;
602 }opt;
603
604 int
605 main(int argc, char **argv)
606 {
607     ARGPARSE_OPTS opts[] = {
608     { 'v', "verbose",   0 , "Laut sein"},
609     { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
610     { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
611     { 'o', "output",    2   },
612     { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
613     { 'm', "my-option", 1|8 },
614     { 500, "a-long-option", 0 },
615     {0} };
616     ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
617     int i;
618
619     while( ArgParse( &pargs, opts) ) {
620         switch( pargs.r_opt ) {
621           case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
622           case 'v': opt.verbose++; break;
623           case 'e': opt.echo++; break;
624           case 'd': opt.debug++; break;
625           case 'o': opt.outfile = pargs.r.ret_str; break;
626           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
627           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
628           case 500: opt.a_long_one++;  break;
629           default : pargs.err = 1; break; /* force warning output */
630         }
631     }
632     for(i=0; i < argc; i++ )
633         printf("%3d -> (%s)\n", i, argv[i] );
634     puts("Options:");
635     if( opt.verbose )
636         printf("  verbose=%d\n", opt.verbose );
637     if( opt.debug )
638         printf("  debug=%d\n", opt.debug );
639     if( opt.outfile )
640         printf("  outfile='%s'\n", opt.outfile );
641     if( opt.crf )
642         printf("  crffile='%s'\n", opt.crf );
643     if( opt.myopt )
644         printf("  myopt=%d\n", opt.myopt );
645     if( opt.a_long_one )
646         printf("  a-long-one=%d\n", opt.a_long_one );
647     if( opt.echo       )
648         printf("  echo=%d\n", opt.echo );
649     return 0;
650 }
651 #endif
652
653 /**** bottom of file ****/