How with some assembly support
[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     s = strusage(10);
353     fputs( s, stdout );
354     if( *s && s[strlen(s)-1] != '\n' )
355         putchar( '\n' );
356     s = strusage(12);
357     if( *s == '\n' )
358         s++;
359     puts(s);
360     if( opts[0].description ) { /* auto format the option description */
361         int i,j, indent;
362         /* get max. length of long options */
363         for(i=indent=0; opts[i].short_opt; i++ ) {
364             if( opts[i].long_opt )
365                 if( (j=strlen(opts[i].long_opt)) > indent && j < 35 )
366                     indent = j;
367         }
368         /* example: " -v, --verbose   Viele Sachen ausgeben" */
369         indent += 10;
370         puts("Options:");
371         for(i=0; opts[i].short_opt; i++ ) {
372             s = opts[i].description;
373             if( s && *s== '\r' ) /* hide this line */
374                 continue;
375             if( opts[i].short_opt < 256 )
376                 printf(" -%c", opts[i].short_opt );
377             else
378                 fputs("   ", stdout);
379             j = 3;
380             if( opts[i].long_opt )
381                 j += printf("%c --%s   ", opts[i].short_opt < 256?',':' ',
382                                           opts[i].long_opt );
383             for(;j < indent; j++ )
384                 putchar(' ');
385             if( s ) {
386                 for(; *s; s++ ) {
387                     if( *s == '\n' ) {
388                         if( s[1] ) {
389                             putchar('\n');
390                             for(j=0;j < indent; j++ )
391                                 putchar(' ');
392                         }
393                     }
394                     else
395                         putchar(*s);
396                 }
397             }
398             putchar('\n');
399         }
400         if( flags & 32 )
401             puts("\n(A single dash may be used instead of the double ones)");
402     }
403     if( *(s=strusage(26)) ) {  /* bug reports to ... */
404         putchar('\n');
405         fputs(s, stdout);
406     }
407     if( *(s=strusage(30)) ) {  /* special notes */
408         putchar('\n');
409         fputs(s, stdout);
410     }
411     fflush(stdout);
412     exit(0);
413 }
414
415 static void
416 show_version()
417 {
418     const char *s;
419     printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) );
420     if( (s = strusage(24)) && *s ) {
421       #ifdef DEBUG
422         printf(", %s, dbg)\n", s);
423       #else
424         printf(", %s)\n", s);
425       #endif
426     }
427     else {
428       #ifdef DEBUG
429         printf(", dbg)\n");
430       #else
431         printf(")\n");
432       #endif
433     }
434     fflush(stdout);
435     exit(0);
436 }
437
438
439
440 void
441 usage( int level )
442 {
443     static int sentinel=0;
444
445     if( sentinel )
446         return;
447
448     sentinel++;
449     if( !level ) {
450         fputs( strusage(level), stderr ); putc( '\n', stderr );
451         fputs( strusage(31), stderr);
452       #if DEBUG
453         fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) );
454       #else
455         fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) );
456       #endif
457         fflush(stderr);
458     }
459     else if( level == 1 ) {
460         fputs(strusage(level),stderr);putc('\n',stderr);
461         exit(2);}
462     else if( level == 2 ) {
463         puts(strusage(level)); exit(0);}
464     sentinel--;
465 }
466
467
468 const char *
469 default_strusage( int level )
470 {
471     const char *p;
472     switch( level ) {
473       case  0:  p = strusage(10); break;
474       case  1:  p = strusage(11); break;
475       case  2:  p = strusage(12); break;
476       case 10:  p = "WkLib"
477                   #if DOS386 && __WATCOMC__
478                     " (DOS4G)"
479                   #elif DOS386
480                     " (DOSX)"
481                   #elif DOS16RM
482                     " (DOS16RM)"
483                   #elif M_I86VM
484                     " (VCM)"
485                   #elif UNIX || POSIX
486                     " (Posix)"
487                   #elif OS2
488                     " (OS/2)"
489                   #elif WINNT && __CYGWIN32__
490                     " (CygWin)"
491                   #elif WINNT
492                     " (WinNT)"
493                   #elif NETWARE
494                     " (Netware)"
495                   #elif VMS
496                     " (VMS)"
497                   #endif
498                     "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break;
499       case 11:  p = "usage: ?"; break;
500       case 16:
501       case 15:  p = "[Untitled]"; break;
502       case 23:  p = "[unknown]"; break;
503       case 24:  p = ""; break;
504       case 26:  p = ""; break;
505       case 12:  p =
506    "This is free software; you can redistribute it and/or modify\n"
507    "it under the terms of the GNU General Public License as published by\n"
508    "the Free Software Foundation; either version 2 of the License, or\n"
509    "(at your option) any later version.\n\n"
510    "It is distributed in the hope that it will be useful,\n"
511    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
512    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
513    "GNU General Public License for more details.\n\n"
514    "You should have received a copy of the GNU General Public License\n"
515    "along with this program; if not, write to the Free Software\n"
516    "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,"
517                                                                   " USA.\n" ;
518         break;
519       case 22:
520           #if MSDOS
521             #if USE_EMS
522               p = "MSDOS+EMS";
523             #else
524               p = "MSDOS";
525             #endif
526           #elif OS2
527             p = "OS/2";
528           #elif WINNT && __CYGWIN32__
529             p = "CygWin";
530           #elif WINNT
531             p = "WinNT";
532           #elif DOS386
533             p = "DOS386";
534           #elif EMX
535             p = "EMX";
536           #elif DOS16RM
537             p = "DOS16RM";
538           #elif NETWARE
539             p = "Netware";
540           #elif __linux__
541             p = "Linux";
542           #elif UNIX || M_UNIX || M_XENIX
543             p = "UNIX";
544           #elif VMS
545             p = "VMS";
546           #else
547             p = "UnknownOS";
548           #endif
549             break;
550       case 30: p = ""; break;
551       case 31: p =
552     "This program comes with ABSOLUTELY NO WARRANTY.\n"
553     "This is free software, and you are welcome to redistribute it\n"
554     "under certain conditions. See the file COPYING for details.\n";
555             break;
556       case 32: p = "["
557           #if MSDOS
558               "MSDOS Version"
559           #elif DOS386 && __ZTC__
560             "32-Bit MSDOS Version (Zortech's DOSX)"
561           #elif DOS386
562             "32-Bit MSDOS Version"
563           #elif OS20 && EMX
564             "OS/2 2.x EMX Version"
565           #elif OS20
566             "OS/2 2.x Version"
567           #elif OS2
568             "OS/2 1.x Version"
569           #elif WINNT && __CYGWIN32__
570             "Cygnus WinAPI Version"
571           #elif WINNT
572             "Windoze NT Version"
573           #elif EMX
574             "EMX Version"
575           #elif NETWARE
576             "NLM Version"
577           #elif DOS16RM
578             "DOS16RM Version"
579           #elif __linux__
580             "Linux Version"
581           #elif VMS
582             "OpenVMS Version"
583           #elif POSIX
584             "POSIX Version"
585           #elif M_UNIX || M_XENIX
586             "*IX Version"
587           #endif
588             "]";
589             break;
590       case 33: p =
591           #ifdef MULTI_THREADED
592             "mt"
593           #else
594             ""
595           #endif
596             ; break;
597       case 42:
598       case 43:
599       case 44:
600       case 45: p = ""; break;
601       default: p = "?";
602     }
603
604     return p;
605 }
606
607
608
609 #ifdef TEST
610 static struct {
611     int verbose;
612     int debug;
613     char *outfile;
614     char *crf;
615     int myopt;
616     int echo;
617     int a_long_one;
618 }opt;
619
620 int
621 main(int argc, char **argv)
622 {
623     ARGPARSE_OPTS opts[] = {
624     { 'v', "verbose",   0 , "Laut sein"},
625     { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
626     { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
627     { 'o', "output",    2   },
628     { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
629     { 'm', "my-option", 1|8 },
630     { 500, "a-long-option", 0 },
631     {0} };
632     ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
633     int i;
634
635     while( ArgParse( &pargs, opts) ) {
636         switch( pargs.r_opt ) {
637           case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
638           case 'v': opt.verbose++; break;
639           case 'e': opt.echo++; break;
640           case 'd': opt.debug++; break;
641           case 'o': opt.outfile = pargs.r.ret_str; break;
642           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
643           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
644           case 500: opt.a_long_one++;  break;
645           default : pargs.err = 1; break; /* force warning output */
646         }
647     }
648     for(i=0; i < argc; i++ )
649         printf("%3d -> (%s)\n", i, argv[i] );
650     puts("Options:");
651     if( opt.verbose )
652         printf("  verbose=%d\n", opt.verbose );
653     if( opt.debug )
654         printf("  debug=%d\n", opt.debug );
655     if( opt.outfile )
656         printf("  outfile='%s'\n", opt.outfile );
657     if( opt.crf )
658         printf("  crffile='%s'\n", opt.crf );
659     if( opt.myopt )
660         printf("  myopt=%d\n", opt.myopt );
661     if( opt.a_long_one )
662         printf("  a-long-one=%d\n", opt.a_long_one );
663     if( opt.echo       )
664         printf("  echo=%d\n", opt.echo );
665     return 0;
666 }
667 #endif
668
669 /**** bottom of file ****/