1
/* [argparse.c wk 17.06.97] Argument Parser for option handling
2
* Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* GnuPG is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
* Note: This is an independent version of the one in WkLib
34
/*********************************
39
* char *argc; pointer to argc (value subject to change)
40
* char ***argv; pointer to argv (value subject to change)
41
* unsigned flags; Global flags (DO NOT CHANGE)
42
* int err; print error about last option
43
* 1 = warning, 2 = abort
44
* int r_opt; return option
45
* int r_type; type of return value (0 = no argument found)
56
* } internal; DO NOT CHANGE
61
* const char *long_opt;
65
* int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
68
* This is my replacement for getopt(). See the example for a typical usage.
70
* Bit 0 : Do not remove options form argv
71
* Bit 1 : Do not stop at last option but return other args
72
* with r_opt set to -1.
73
* Bit 2 : Assume options and real args are mixed.
74
* Bit 3 : Do not use -- to stop option processing.
75
* Bit 4 : Do not skip the first arg.
76
* Bit 5 : allow usage of long option with only one dash
77
* Bit 6 : ignore --version and --help
78
* all other bits must be set to zero, this value is modified by the
79
* function, so assume this is write only.
80
* Local flags (for each option):
81
* Bit 2-0 : 0 = does not take an argument
82
* 1 = takes int argument
83
* 2 = takes string argument
84
* 3 = takes long argument
85
* 4 = takes ulong argument
86
* Bit 3 : argument is optional (r_type will the be set to 0)
87
* Bit 4 : allow 0x etc. prefixed values.
88
* Bit 7 : this is a command and not an option
89
* You stop the option processing by setting opts to NULL, the function will
92
* Returns the args.r_opt or 0 if ready
93
* r_opt may be -2/-7 to indicate an unknown option/command.
97
* You do not need to process the options 'h', '--help' or '--version'
98
* because this function includes standard help processing; but if you
99
* specify '-h', '--help' or '--version' you have to do it yourself.
100
* The option '--' stops argument processing; if bit 1 is set the function
101
* continues to return normal arguments.
102
* To process float args or unsigned args you must use a string args and do
103
* the conversion yourself.
106
* ARGPARSE_OPTS opts[] = {
107
* { 'v', "verbose", 0 },
108
* { 'd', "debug", 0 },
109
* { 'o', "output", 2 },
110
* { 'c', "cross-ref", 2|8 },
111
* { 'm', "my-option", 1|8 },
112
* { 500, "have-no-short-option-for-this-long-option", 0 },
114
* ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
116
* while( ArgParse( &pargs, &opts) ) {
117
* switch( pargs.r_opt ) {
118
* case 'v': opt.verbose++; break;
119
* case 'd': opt.debug++; break;
120
* case 'o': opt.outfile = pargs.r.ret_str; break;
121
* case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
122
* case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
123
* case 500: opt.a_long_one++; break
124
* default : pargs.err = 1; break; -- force warning output --
128
* log_fatal( "Too many args");
132
typedef struct alias_def_s *ALIAS_DEF;
135
char *name; /* malloced buffer with name, \0, value */
136
const char *value; /* ptr into name */
139
static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
140
static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
141
static void show_version(void);
144
initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
146
if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
147
arg->internal.idx = 0;
148
arg->internal.last = NULL;
149
arg->internal.inarg = 0;
150
arg->internal.stopped = 0;
151
arg->internal.aliases = NULL;
152
arg->internal.cur_alias = NULL;
154
arg->flags |= 1<<15; /* mark initialized */
156
log_bug("Invalid argument for ArgParse\n");
160
if( arg->err ) { /* last option was erroneous */
164
if( arg->r_opt == -6 )
165
s = "%s:%u: argument not expected\n";
166
else if( arg->r_opt == -5 )
167
s = "%s:%u: read error\n";
168
else if( arg->r_opt == -4 )
169
s = "%s:%u: keyword too long\n";
170
else if( arg->r_opt == -3 )
171
s = "%s:%u: missing argument\n";
172
else if( arg->r_opt == -7 )
173
s = "%s:%u: invalid command\n";
174
else if( arg->r_opt == -10 )
175
s = "%s:%u: invalid alias definition\n";
177
s = "%s:%u: invalid option\n";
178
log_error(s, filename, *lineno );
181
if( arg->r_opt == -3 )
182
s = "Missing argument for option \"%.50s\"\n";
183
else if( arg->r_opt == -6 )
184
s = "Option \"%.50s\" does not expect an argument\n";
185
else if( arg->r_opt == -7 )
186
s = "Invalid command \"%.50s\"\n";
187
else if( arg->r_opt == -8 )
188
s = "Option \"%.50s\" is ambiguous\n";
189
else if( arg->r_opt == -9 )
190
s = "Command \"%.50s\" is ambiguous\n";
192
s = "Invalid option \"%.50s\"\n";
193
log_error(s, arg->internal.last? arg->internal.last:"[??]" );
195
if( arg->err != 1 || arg->r_opt == -5 )
200
/* clearout the return value union */
201
arg->r.ret_str = NULL;
207
store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
209
/* TODO: replace this dummy function with a rea one
210
* and fix the probelms IRIX has with (ALIAS_DEV)arg..
214
ALIAS_DEF a = m_alloc( sizeof *a );
217
a->next = (ALIAS_DEF)arg->internal.aliases;
218
(ALIAS_DEF)arg->internal.aliases = a;
223
* Get options from a file.
224
* Lines starting with '#' are comment lines.
225
* Syntax is simply a keyword and the argument.
226
* Valid keywords are all keywords from the long_opt list without
227
* the leading dashes. The special keywords "help", "warranty" and "version"
228
* are not valid here.
229
* The special keyword "alias" may be used to store alias definitions,
230
* which are later expanded like long options.
231
* Caller must free returned strings.
232
* If called with FP set to NULL command line args are parse instead.
234
* Q: Should we allow the syntax
236
* and accept for boolean options a value of 1/0, yes/no or true/false?
237
* Note: Abbreviation of options is here not allowed.
240
optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
241
ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
251
if( !fp ) /* same as arg_parse() in this case */
252
return arg_parse( arg, opts );
254
initialize( arg, filename, lineno );
256
/* find the next keyword */
260
if( c == '\n' || c== EOF ) {
265
else if( state == 2 ) {
267
for(i=0; opts[i].short_opt; i++ )
268
if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
271
arg->r_opt = opts[idx].short_opt;
272
if( inverse ) /* this does not have an effect, hmmm */
273
arg->r_opt = -arg->r_opt;
274
if( !opts[idx].short_opt ) /* unknown command/option */
275
arg->r_opt = (opts[idx].flags & 256)? -7:-2;
276
else if( !(opts[idx].flags & 7) ) /* does not take an arg */
277
arg->r_type = 0; /* okay */
278
else if( (opts[idx].flags & 8) ) /* argument is optional */
279
arg->r_type = 0; /* okay */
280
else /* required argument */
281
arg->r_opt = -3; /* error */
284
else if( state == 3 ) { /* no argument found */
286
arg->r_opt = -3; /* error */
287
else if( !(opts[idx].flags & 7) ) /* does not take an arg */
288
arg->r_type = 0; /* okay */
289
else if( (opts[idx].flags & 8) ) /* no optional argument */
290
arg->r_type = 0; /* okay */
291
else /* no required argument */
292
arg->r_opt = -3; /* error */
295
else if( state == 4 ) { /* have an argument */
303
p = strpbrk( buffer, " \t" );
313
store_alias( arg, buffer, p );
317
else if( !(opts[idx].flags & 7) ) /* does not take an arg */
318
arg->r_opt = -6; /* error */
323
buffer = m_strdup(keyword);
328
trim_spaces( buffer );
330
/* remove quotes if they totally enclose the
331
string, and do not occur within the string */
332
if( *p == '"' && p[strlen(p)-1]=='"') {
339
if(*p2=='"' && *(p2+1)=='\0') {
344
if( !set_opt_arg(arg, opts[idx].flags, p) )
349
else if( c == EOF ) {
351
arg->r_opt = -5; /* read error */
353
arg->r_opt = 0; /* eof */
359
else if( state == -1 )
361
else if( !state && isspace(c) )
362
; /* skip leading white space */
363
else if( !state && c == '#' )
364
state = 1; /* start of a comment */
365
else if( state == 1 )
366
; /* skip comments */
367
else if( state == 2 && isspace(c) ) {
369
for(i=0; opts[i].short_opt; i++ )
370
if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
373
arg->r_opt = opts[idx].short_opt;
374
if( !opts[idx].short_opt ) {
375
if( !strcmp( keyword, "alias" ) ) {
380
arg->r_opt = (opts[idx].flags & 256)? -7:-2;
381
state = -1; /* skip rest of line and leave */
387
else if( state == 3 ) { /* skip leading spaces of the argument */
394
else if( state == 4 ) { /* collect the argument */
400
buffer = m_realloc(buffer, buflen);
404
else if( i < DIM(keyword)-1 )
407
buflen = DIM(keyword)+50;
408
buffer = m_alloc(buflen);
409
memcpy(buffer, keyword, i);
413
else if( i >= DIM(keyword)-1 ) {
414
arg->r_opt = -4; /* keyword to long */
415
state = -1; /* skip rest of line and leave */
429
find_long_option( ARGPARSE_ARGS *arg,
430
ARGPARSE_OPTS *opts, const char *keyword )
435
/* Would be better if we can do a binary search, but it is not
436
possible to reorder our option table because we would mess
437
up our help strings - What we can do is: Build a nice option
438
lookup table wehn this function is first invoked */
441
for(i=0; opts[i].short_opt; i++ )
442
if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
447
/* see whether it is an alias */
448
for( a = args->internal.aliases; a; a = a->next ) {
449
if( !strcmp( a->name, keyword) ) {
450
/* todo: must parse the alias here */
451
args->internal.cur_alias = a;
452
return -3; /* alias available */
457
/* not found, see whether it is an abbreviation */
458
/* aliases may not be abbreviated */
459
n = strlen( keyword );
460
for(i=0; opts[i].short_opt; i++ ) {
461
if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
463
for(j=i+1; opts[j].short_opt; j++ ) {
465
&& !strncmp( opts[j].long_opt, keyword, n ) )
466
return -2; /* abbreviation is ambiguous */
475
arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
483
initialize( arg, NULL, NULL );
486
idx = arg->internal.idx;
488
if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
489
argc--; argv++; idx++;
493
if( !argc ) { /* no more args */
495
goto leave; /* ready */
499
arg->internal.last = s;
501
if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
502
arg->r_opt = -1; /* not an option but a argument */
505
argc--; argv++; idx++; /* set to next one */
507
else if( arg->internal.stopped ) { /* ready */
511
else if( *s == '-' && s[1] == '-' ) { /* long option */
514
arg->internal.inarg = 0;
515
if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
516
arg->internal.stopped = 1;
517
argc--; argv++; idx++;
521
argpos = strchr( s+2, '=' );
524
i = find_long_option( arg, opts, s+2 );
528
if( i < 0 && !strcmp( "help", s+2) ) {
529
if( !(arg->flags & (1<<6)) ) {
530
show_help(opts, arg->flags);
533
else if( i < 0 && !strcmp( "version", s+2) ) {
534
if( !(arg->flags & (1<<6)) ) {
539
else if( i < 0 && !strcmp( "warranty", s+2) ) {
540
puts( strusage(16) );
543
else if( i < 0 && !strcmp( "dump-options", s+2) ) {
544
for(i=0; opts[i].short_opt; i++ ) {
545
if( opts[i].long_opt )
546
printf( "--%s\n", opts[i].long_opt );
548
fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
552
if( i == -2 ) /* ambiguous option */
556
arg->r.ret_str = s+2;
559
arg->r_opt = opts[i].short_opt;
562
else if( (opts[i].flags & 7) ) {
570
if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
571
arg->r_type = 0; /* because it is optional */
574
arg->r_opt = -3; /* missing argument */
576
else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
577
/* the argument is optional and the next seems to be
578
* an option. We do not check this possible option
579
* but assume no argument */
583
set_opt_arg(arg, opts[i].flags, s2);
585
argc--; argv++; idx++; /* skip one */
589
else { /* does not take an argument */
591
arg->r_type = -6; /* argument not expected */
595
argc--; argv++; idx++; /* set to next one */
597
else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
600
if( !arg->internal.inarg ) {
601
arg->internal.inarg++;
602
if( arg->flags & (1<<5) ) {
603
for(i=0; opts[i].short_opt; i++ )
604
if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
610
s += arg->internal.inarg;
613
for(i=0; opts[i].short_opt; i++ )
614
if( opts[i].short_opt == *s )
618
if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) {
619
if( !(arg->flags & (1<<6)) ) {
620
show_help(opts, arg->flags);
624
arg->r_opt = opts[i].short_opt;
625
if( !opts[i].short_opt ) {
626
arg->r_opt = (opts[i].flags & 256)? -7:-2;
627
arg->internal.inarg++; /* point to the next arg */
630
else if( (opts[i].flags & 7) ) {
631
if( s[1] && !dash_kludge ) {
633
set_opt_arg(arg, opts[i].flags, s2);
637
if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
638
arg->r_type = 0; /* because it is optional */
641
arg->r_opt = -3; /* missing argument */
643
else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
644
/* the argument is optional and the next seems to be
645
* an option. We do not check this possible option
646
* but assume no argument */
650
set_opt_arg(arg, opts[i].flags, s2);
651
argc--; argv++; idx++; /* skip one */
654
s = "x"; /* so that !s[1] yields false */
656
else { /* does not take an argument */
658
arg->internal.inarg++; /* point to the next arg */
660
if( !s[1] || dash_kludge ) { /* no more concatenated short options */
661
arg->internal.inarg = 0;
662
argc--; argv++; idx++;
665
else if( arg->flags & (1<<2) ) {
666
arg->r_opt = -1; /* not an option but a argument */
669
argc--; argv++; idx++; /* set to next one */
672
arg->internal.stopped = 1; /* stop option processing */
679
arg->internal.idx = idx;
686
set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
688
int base = (flags & 16)? 0 : 10;
690
switch( arg->r_type = (flags & 7) ) {
691
case 1: /* takes int argument */
692
arg->r.ret_int = (int)strtol(s,NULL,base);
694
case 3: /* takes long argument */
695
arg->r.ret_long= strtol(s,NULL,base);
697
case 4: /* takes ulong argument */
698
arg->r.ret_ulong= strtoul(s,NULL,base);
700
case 2: /* takes string argument */
709
long_opt_strlen( ARGPARSE_OPTS *o )
711
size_t n = strlen(o->long_opt);
713
if( o->description && *o->description == '|' ) {
719
for(; *s && *s != '|'; s++ )
726
* Print formatted help. The description string has some special
728
* - A description string which is "@" suppresses help output for
730
* - a description,ine which starts with a '@' and is followed by
731
* any other characters is printed as is; this may be used for examples
733
* - A description which starts with a '|' outputs the string between this
734
* bar and the next one as arguments of the long option.
737
show_help( ARGPARSE_OPTS *opts, unsigned flags )
745
if( opts[0].description ) { /* auto format the option description */
747
/* get max. length of long options */
748
for(i=indent=0; opts[i].short_opt; i++ ) {
749
if( opts[i].long_opt )
750
if( !opts[i].description || *opts[i].description != '@' )
751
if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
754
/* example: " -v, --verbose Viele Sachen ausgeben" */
756
if( *opts[0].description != '@' )
758
for(i=0; opts[i].short_opt; i++ ) {
759
s = _( opts[i].description );
760
if( s && *s== '@' && !s[1] ) /* hide this line */
762
if( s && *s == '@' ) { /* unindented comment only line */
776
if( opts[i].short_opt < 256 ) {
777
printf(" -%c", opts[i].short_opt );
778
if( !opts[i].long_opt ) {
779
if(s && *s == '|' ) {
781
for(s++ ; *s && *s != '|'; s++, j++ )
790
if( opts[i].long_opt ) {
791
j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
793
if(s && *s == '|' ) {
798
for( ; *s && *s != '|'; s++, j++ )
806
for(;j < indent; j++ )
809
if( *s && j > indent ) {
811
for(j=0;j < indent; j++ )
818
for(j=0;j < indent; j++ )
829
puts("\n(A single dash may be used instead of the double ones)");
831
if( (s=strusage(19)) ) { /* bug reports to ... */
845
fputs(strusage(11), stdout);
846
if( (s=strusage(12)) )
848
printf(" %s\n", strusage(13) );
849
/* additional version lines */
850
for(i=20; i < 30; i++ )
851
if( (s=strusage(i)) )
853
/* copyright string */
854
if( (s=strusage(14)) )
856
/* copying conditions */
857
if( (s=strusage(15)) )
860
if( (s=strusage(18)) )
862
/* additional program info */
863
for(i=30; i < 40; i++ )
864
if( (s=strusage(i)) )
865
fputs( (const byte*)s, stdout);
874
fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
878
else if( level == 1 ) {
879
fputs(strusage(40),stderr);
882
else if( level == 2 ) {
889
* 0: Copyright String auf stderr ausgeben
890
* 1: Kurzusage auf stderr ausgeben und beenden
891
* 2: Langusage auf stdout ausgeben und beenden
892
* 11: name of program
893
* 12: optional name of package which includes this program.
895
* 14: copyright string
896
* 15: Short copying conditions (with LFs)
897
* 16: Long copying conditions (with LFs)
898
* 17: Optional printable OS name
899
* 18: Optional thanks list (with LFs)
900
* 19: Bug report info
901
*20..29: Additional lib version strings.
902
*30..39: Additional program info (with LFs)
903
* 40: short usage note (with LF)
904
* 41: long usage note (with LF)
907
default_strusage( int level )
909
const char *p = NULL;
911
case 11: p = "foo"; break;
912
case 13: p = "0.0"; break;
913
case 14: p = "Copyright (C) 2002 Free Software Foundation, Inc."; break;
915
"This program comes with ABSOLUTELY NO WARRANTY.\n"
916
"This is free software, and you are welcome to redistribute it\n"
917
"under certain conditions. See the file COPYING for details.\n"; break;
919
"This is free software; you can redistribute it and/or modify\n"
920
"it under the terms of the GNU General Public License as published by\n"
921
"the Free Software Foundation; either version 2 of the License, or\n"
922
"(at your option) any later version.\n\n"
923
"It is distributed in the hope that it will be useful,\n"
924
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
925
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
926
"GNU General Public License for more details.\n\n"
927
"You should have received a copy of the GNU General Public License\n"
928
"along with this program; if not, write to the Free Software\n"
929
"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
931
case 40: /* short and long usage */
932
case 41: p = ""; break;
952
main(int argc, char **argv)
954
ARGPARSE_OPTS opts[] = {
955
{ 'v', "verbose", 0 , "Laut sein"},
956
{ 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
957
{ 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
958
{ 'o', "output", 2 },
959
{ 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
960
{ 'm', "my-option", 1|8 },
961
{ 500, "a-long-option", 0 },
963
ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
966
while( ArgParse( &pargs, opts) ) {
967
switch( pargs.r_opt ) {
968
case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
969
case 'v': opt.verbose++; break;
970
case 'e': opt.echo++; break;
971
case 'd': opt.debug++; break;
972
case 'o': opt.outfile = pargs.r.ret_str; break;
973
case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
974
case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
975
case 500: opt.a_long_one++; break;
976
default : pargs.err = 1; break; /* force warning output */
979
for(i=0; i < argc; i++ )
980
printf("%3d -> (%s)\n", i, argv[i] );
983
printf(" verbose=%d\n", opt.verbose );
985
printf(" debug=%d\n", opt.debug );
987
printf(" outfile=`%s'\n", opt.outfile );
989
printf(" crffile=`%s'\n", opt.crf );
991
printf(" myopt=%d\n", opt.myopt );
993
printf(" a-long-one=%d\n", opt.a_long_one );
995
printf(" echo=%d\n", opt.echo );
1000
/**** bottom of file ****/