1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2 * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
3 * 2007 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
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.
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.
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/>.
31 /*********************************
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)
53 * } internal; DO NOT CHANGE
58 * const char *long_opt;
62 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
65 * This is my replacement for getopt(). See the example for a typical usage.
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
89 * Returns the args.r_opt or 0 if ready
90 * r_opt may be -2/-7 to indicate an unknown option/command.
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.
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 },
111 * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
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 --
125 * log_fatal( "Too many args");
129 typedef struct alias_def_s *ALIAS_DEF;
132 char *name; /* malloced buffer with name, \0, value */
133 const char *value; /* ptr into name */
136 static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
137 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
138 static void show_version(void);
141 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
143 if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
144 arg->internal.idx = 0;
145 arg->internal.last = NULL;
146 arg->internal.inarg = 0;
147 arg->internal.stopped = 0;
148 arg->internal.aliases = NULL;
149 arg->internal.cur_alias = NULL;
151 arg->flags |= 1<<15; /* mark initialized */
153 log_bug("Invalid argument for ArgParse\n");
157 if( arg->err ) { /* last option was erroneous */
160 if( arg->r_opt == -6 )
161 log_error("%s:%u: argument not expected\n", filename, *lineno );
162 else if( arg->r_opt == -5 )
163 log_error("%s:%u: read error\n", filename, *lineno );
164 else if( arg->r_opt == -4 )
165 log_error("%s:%u: keyword too long\n", filename, *lineno );
166 else if( arg->r_opt == -3 )
167 log_error("%s:%u: missing argument\n", filename, *lineno );
168 else if( arg->r_opt == -7 )
169 log_error("%s:%u: invalid command\n", filename, *lineno );
170 else if( arg->r_opt == -10 )
171 log_error("%s:%u: invalid alias definition\n",filename,*lineno);
173 log_error("%s:%u: invalid option\n", filename, *lineno );
176 if( arg->r_opt == -3 )
177 log_error("Missing argument for option \"%.50s\"\n",
178 arg->internal.last? arg->internal.last:"[??]" );
179 else if( arg->r_opt == -6 )
180 log_error("Option \"%.50s\" does not expect an argument\n",
181 arg->internal.last? arg->internal.last:"[??]" );
182 else if( arg->r_opt == -7 )
183 log_error("Invalid command \"%.50s\"\n",
184 arg->internal.last? arg->internal.last:"[??]" );
185 else if( arg->r_opt == -8 )
186 log_error("Option \"%.50s\" is ambiguous\n",
187 arg->internal.last? arg->internal.last:"[??]" );
188 else if( arg->r_opt == -9 )
189 log_error("Command \"%.50s\" is ambiguous\n",
190 arg->internal.last? arg->internal.last:"[??]" );
192 log_error("Invalid option \"%.50s\"\n",
193 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 = xmalloc( 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 = xstrdup(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 = xrealloc(buffer, buflen);
404 else if( i < DIM(keyword)-1 )
407 buflen = DIM(keyword)+50;
408 buffer = xmalloc(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 /* Licence string. */
857 if( (s=strusage (10)) )
859 /* copying conditions */
860 if( (s=strusage(15)) )
863 if( (s=strusage(18)) )
865 /* additional program info */
866 for(i=30; i < 40; i++ )
867 if( (s=strusage(i)) )
868 fputs( (const byte*)s, stdout);
877 fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
881 else if( level == 1 ) {
882 fputs(strusage(40),stderr);
885 else if( level == 2 ) {
892 * 0: Copyright String auf stderr ausgeben
893 * 1: Kurzusage auf stderr ausgeben und beenden
894 * 2: Langusage auf stdout ausgeben und beenden
895 * 10: Return license info string
896 * 11: name of program
897 * 12: optional name of package which includes this program.
899 * 14: copyright string
900 * 15: Short copying conditions (with LFs)
901 * 16: Long copying conditions (with LFs)
902 * 17: Optional printable OS name
903 * 18: Optional thanks list (with LFs)
904 * 19: Bug report info
905 *20..29: Additional lib version strings.
906 *30..39: Additional program info (with LFs)
907 * 40: short usage note (with LF)
908 * 41: long usage note (with LF)
911 default_strusage( int level )
913 const char *p = NULL;
915 case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
916 "<http://gnu.org/licenses/gpl.html>");
918 case 11: p = "foo"; break;
919 case 13: p = "0.0"; break;
920 case 14: p = "Copyright (C) 2007 Free Software Foundation, Inc."; break;
922 "This is free software: you are free to change and redistribute it.\n"
923 "There is NO WARRANTY, to the extent permitted by law.\n";
926 "This is free software; you can redistribute it and/or modify\n"
927 "it under the terms of the GNU General Public License as published by\n"
928 "the Free Software Foundation; either version 3 of the License, or\n"
929 "(at your option) any later version.\n\n"
930 "It is distributed in the hope that it will be useful,\n"
931 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
932 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
933 "GNU General Public License for more details.\n\n"
934 "You should have received a copy of the GNU General Public License\n"
935 "along with this software. If not, see <http://www.gnu.org/licenses/>.\n";
937 case 40: /* short and long usage */
938 case 41: p = ""; break;
958 main(int argc, char **argv)
960 ARGPARSE_OPTS opts[] = {
961 { 'v', "verbose", 0 , "Laut sein"},
962 { 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
963 { 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
964 { 'o', "output", 2 },
965 { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
966 { 'm', "my-option", 1|8 },
967 { 500, "a-long-option", 0 },
969 ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
972 while( ArgParse( &pargs, opts) ) {
973 switch( pargs.r_opt ) {
974 case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
975 case 'v': opt.verbose++; break;
976 case 'e': opt.echo++; break;
977 case 'd': opt.debug++; break;
978 case 'o': opt.outfile = pargs.r.ret_str; break;
979 case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
980 case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
981 case 500: opt.a_long_one++; break;
982 default : pargs.err = 1; break; /* force warning output */
985 for(i=0; i < argc; i++ )
986 printf("%3d -> (%s)\n", i, argv[i] );
989 printf(" verbose=%d\n", opt.verbose );
991 printf(" debug=%d\n", opt.debug );
993 printf(" outfile=`%s'\n", opt.outfile );
995 printf(" crffile=`%s'\n", opt.crf );
997 printf(" myopt=%d\n", opt.myopt );
999 printf(" a-long-one=%d\n", opt.a_long_one );
1001 printf(" echo=%d\n", opt.echo );
1006 /**** bottom of file ****/