2006-09-01 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / jnlib / argparse.c
blob15a7c546ed74f8cade479c8a90c11dbdb365b913
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19 * USA.
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
28 #include "libjnlib-config.h"
29 #include "mischelp.h"
30 #include "stringhelp.h"
31 #include "logging.h"
32 #include "argparse.h"
35 /*********************************
36 * @Summary arg_parse
37 * #include <wk/lib.h>
39 * typedef struct {
40 * char *argc; pointer to argc (value subject to change)
41 * char ***argv; pointer to argv (value subject to change)
42 * unsigned flags; Global flags (DO NOT CHANGE)
43 * int err; print error about last option
44 * 1 = warning, 2 = abort
45 * int r_opt; return option
46 * int r_type; type of return value (0 = no argument found)
47 * union {
48 * int ret_int;
49 * long ret_long
50 * ulong ret_ulong;
51 * char *ret_str;
52 * } r; Return values
53 * struct {
54 * int idx;
55 * const char *last;
56 * void *aliases;
57 * } internal; DO NOT CHANGE
58 * } ARGPARSE_ARGS;
60 * typedef struct {
61 * int short_opt;
62 * const char *long_opt;
63 * unsigned flags;
64 * } ARGPARSE_OPTS;
66 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
68 * @Description
69 * This is my replacement for getopt(). See the example for a typical usage.
70 * Global flags are:
71 * Bit 0 : Do not remove options form argv
72 * Bit 1 : Do not stop at last option but return other args
73 * with r_opt set to -1.
74 * Bit 2 : Assume options and real args are mixed.
75 * Bit 3 : Do not use -- to stop option processing.
76 * Bit 4 : Do not skip the first arg.
77 * Bit 5 : allow usage of long option with only one dash
78 * Bit 6 : ignore --version
79 * all other bits must be set to zero, this value is modified by the
80 * function, so assume this is write only.
81 * Local flags (for each option):
82 * Bit 2-0 : 0 = does not take an argument
83 * 1 = takes int argument
84 * 2 = takes string argument
85 * 3 = takes long argument
86 * 4 = takes ulong argument
87 * Bit 3 : argument is optional (r_type will the be set to 0)
88 * Bit 4 : allow 0x etc. prefixed values.
89 * Bit 7 : this is a command and not an option
90 * You stop the option processing by setting opts to NULL, the function will
91 * then return 0.
92 * @Return Value
93 * Returns the args.r_opt or 0 if ready
94 * r_opt may be -2/-7 to indicate an unknown option/command.
95 * @See Also
96 * ArgExpand
97 * @Notes
98 * You do not need to process the options 'h', '--help' or '--version'
99 * because this function includes standard help processing; but if you
100 * specify '-h', '--help' or '--version' you have to do it yourself.
101 * The option '--' stops argument processing; if bit 1 is set the function
102 * continues to return normal arguments.
103 * To process float args or unsigned args you must use a string args and do
104 * the conversion yourself.
105 * @Example
107 * ARGPARSE_OPTS opts[] = {
108 * { 'v', "verbose", 0 },
109 * { 'd', "debug", 0 },
110 * { 'o', "output", 2 },
111 * { 'c', "cross-ref", 2|8 },
112 * { 'm', "my-option", 1|8 },
113 * { 500, "have-no-short-option-for-this-long-option", 0 },
114 * {0} };
115 * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
117 * while( ArgParse( &pargs, &opts) ) {
118 * switch( pargs.r_opt ) {
119 * case 'v': opt.verbose++; break;
120 * case 'd': opt.debug++; break;
121 * case 'o': opt.outfile = pargs.r.ret_str; break;
122 * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
123 * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
124 * case 500: opt.a_long_one++; break
125 * default : pargs.err = 1; break; -- force warning output --
128 * if( argc > 1 )
129 * log_fatal( "Too many args");
133 typedef struct alias_def_s *ALIAS_DEF;
134 struct alias_def_s {
135 ALIAS_DEF next;
136 char *name; /* malloced buffer with name, \0, value */
137 const char *value; /* ptr into name */
140 static const char *(*strusage_handler)( int ) = NULL;
142 static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
143 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
144 static void show_version(void);
147 static void
148 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
150 if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
151 arg->internal.idx = 0;
152 arg->internal.last = NULL;
153 arg->internal.inarg = 0;
154 arg->internal.stopped = 0;
155 arg->internal.aliases = NULL;
156 arg->internal.cur_alias = NULL;
157 arg->err = 0;
158 arg->flags |= 1<<15; /* mark initialized */
159 if( *arg->argc < 0 )
160 jnlib_log_bug("Invalid argument for ArgParse\n");
164 if( arg->err ) { /* last option was erroneous */
165 const char *s;
167 if( filename ) {
168 if( arg->r_opt == -6 )
169 s = "argument not expected\n";
170 else if( arg->r_opt == -5 )
171 s = "read error\n";
172 else if( arg->r_opt == -4 )
173 s = "keyword too long\n";
174 else if( arg->r_opt == -3 )
175 s = "missing argument\n";
176 else if( arg->r_opt == -7 )
177 s = "invalid command\n";
178 else if( arg->r_opt == -10 )
179 s = "invalid alias definition\n";
180 else
181 s = "invalid option\n";
182 jnlib_log_error("%s:%u: %s\n", filename, *lineno, s);
184 else {
185 s = arg->internal.last? arg->internal.last:"[??]";
187 if( arg->r_opt == -3 )
188 jnlib_log_error ("Missing argument for option \"%.50s\"\n", s);
189 else if( arg->r_opt == -6 )
190 jnlib_log_error ("Option \"%.50s\" does not expect an argument\n",
191 s );
192 else if( arg->r_opt == -7 )
193 jnlib_log_error ("Invalid command \"%.50s\"\n", s);
194 else if( arg->r_opt == -8 )
195 jnlib_log_error ("Option \"%.50s\" is ambiguous\n", s);
196 else if( arg->r_opt == -9 )
197 jnlib_log_error ("Command \"%.50s\" is ambiguous\n",s );
198 else
199 jnlib_log_error ("Invalid option \"%.50s\"\n", s);
201 if( arg->err != 1 )
202 exit(2);
203 arg->err = 0;
206 /* clearout the return value union */
207 arg->r.ret_str = NULL;
208 arg->r.ret_long= 0;
212 static void
213 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
215 /* TODO: replace this dummy function with a rea one
216 * and fix the probelms IRIX has with (ALIAS_DEV)arg..
217 * used as lvalue
219 #if 0
220 ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
221 a->name = name;
222 a->value = value;
223 a->next = (ALIAS_DEF)arg->internal.aliases;
224 (ALIAS_DEF)arg->internal.aliases = a;
225 #endif
228 /****************
229 * Get options from a file.
230 * Lines starting with '#' are comment lines.
231 * Syntax is simply a keyword and the argument.
232 * Valid keywords are all keywords from the long_opt list without
233 * the leading dashes. The special keywords "help", "warranty" and "version"
234 * are not valid here.
235 * The special keyword "alias" may be used to store alias definitions,
236 * which are later expanded like long options.
237 * Caller must free returned strings.
238 * If called with FP set to NULL command line args are parse instead.
240 * Q: Should we allow the syntax
241 * keyword = value
242 * and accept for boolean options a value of 1/0, yes/no or true/false?
243 * Note: Abbreviation of options is here not allowed.
246 optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
247 ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
249 int state, i, c;
250 int idx=0;
251 char keyword[100];
252 char *buffer = NULL;
253 size_t buflen = 0;
254 int inverse=0;
255 int in_alias=0;
257 if( !fp ) /* same as arg_parse() in this case */
258 return arg_parse( arg, opts );
260 initialize( arg, filename, lineno );
262 /* find the next keyword */
263 state = i = 0;
264 for(;;) {
265 c=getc(fp);
266 if( c == '\n' || c== EOF ) {
267 if( c != EOF )
268 ++*lineno;
269 if( state == -1 )
270 break;
271 else if( state == 2 ) {
272 keyword[i] = 0;
273 for(i=0; opts[i].short_opt; i++ )
274 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
275 break;
276 idx = i;
277 arg->r_opt = opts[idx].short_opt;
278 if( inverse ) /* this does not have an effect, hmmm */
279 arg->r_opt = -arg->r_opt;
280 if( !opts[idx].short_opt ) /* unknown command/option */
281 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
282 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
283 arg->r_type = 0; /* okay */
284 else if( (opts[idx].flags & 8) ) /* argument is optional */
285 arg->r_type = 0; /* okay */
286 else /* required argument */
287 arg->r_opt = -3; /* error */
288 break;
290 else if( state == 3 ) { /* no argument found */
291 if( in_alias )
292 arg->r_opt = -3; /* error */
293 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
294 arg->r_type = 0; /* okay */
295 else if( (opts[idx].flags & 8) ) /* no optional argument */
296 arg->r_type = 0; /* okay */
297 else /* no required argument */
298 arg->r_opt = -3; /* error */
299 break;
301 else if( state == 4 ) { /* have an argument */
302 if( in_alias ) {
303 if( !buffer )
304 arg->r_opt = -6;
305 else {
306 char *p;
308 buffer[i] = 0;
309 p = strpbrk( buffer, " \t" );
310 if( p ) {
311 *p++ = 0;
312 trim_spaces( p );
314 if( !p || !*p ) {
315 jnlib_free( buffer );
316 arg->r_opt = -10;
318 else {
319 store_alias( arg, buffer, p );
323 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
324 arg->r_opt = -6; /* error */
325 else {
326 char *p;
327 if( !buffer ) {
328 keyword[i] = 0;
329 buffer = jnlib_xstrdup(keyword);
331 else
332 buffer[i] = 0;
334 trim_spaces( buffer );
335 p = buffer;
336 if( *p == '"' ) { /* remove quotes */
337 p++;
338 if( *p && p[strlen(p)-1] == '"' )
339 p[strlen(p)-1] = 0;
341 if( !set_opt_arg(arg, opts[idx].flags, p) )
342 jnlib_free(buffer);
344 break;
346 else if( c == EOF ) {
347 if( ferror(fp) )
348 arg->r_opt = -5; /* read error */
349 else
350 arg->r_opt = 0; /* eof */
351 break;
353 state = 0;
354 i = 0;
356 else if( state == -1 )
357 ; /* skip */
358 else if( !state && isspace(c) )
359 ; /* skip leading white space */
360 else if( !state && c == '#' )
361 state = 1; /* start of a comment */
362 else if( state == 1 )
363 ; /* skip comments */
364 else if( state == 2 && isspace(c) ) {
365 keyword[i] = 0;
366 for(i=0; opts[i].short_opt; i++ )
367 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
368 break;
369 idx = i;
370 arg->r_opt = opts[idx].short_opt;
371 if( !opts[idx].short_opt ) {
372 if( !strcmp( keyword, "alias" ) ) {
373 in_alias = 1;
374 state = 3;
376 else {
377 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
378 state = -1; /* skip rest of line and leave */
381 else
382 state = 3;
384 else if( state == 3 ) { /* skip leading spaces of the argument */
385 if( !isspace(c) ) {
386 i = 0;
387 keyword[i++] = c;
388 state = 4;
391 else if( state == 4 ) { /* collect the argument */
392 if( buffer ) {
393 if( i < buflen-1 )
394 buffer[i++] = c;
395 else {
396 buflen += 50;
397 buffer = jnlib_xrealloc(buffer, buflen);
398 buffer[i++] = c;
401 else if( i < DIM(keyword)-1 )
402 keyword[i++] = c;
403 else {
404 buflen = DIM(keyword)+50;
405 buffer = jnlib_xmalloc(buflen);
406 memcpy(buffer, keyword, i);
407 buffer[i++] = c;
410 else if( i >= DIM(keyword)-1 ) {
411 arg->r_opt = -4; /* keyword to long */
412 state = -1; /* skip rest of line and leave */
414 else {
415 keyword[i++] = c;
416 state = 2;
420 return arg->r_opt;
425 static int
426 find_long_option( ARGPARSE_ARGS *arg,
427 ARGPARSE_OPTS *opts, const char *keyword )
429 int i;
430 size_t n;
432 /* Would be better if we can do a binary search, but it is not
433 possible to reorder our option table because we would mess
434 up our help strings - What we can do is: Build a nice option
435 lookup table wehn this function is first invoked */
436 if( !*keyword )
437 return -1;
438 for(i=0; opts[i].short_opt; i++ )
439 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
440 return i;
441 #if 0
443 ALIAS_DEF a;
444 /* see whether it is an alias */
445 for( a = args->internal.aliases; a; a = a->next ) {
446 if( !strcmp( a->name, keyword) ) {
447 /* todo: must parse the alias here */
448 args->internal.cur_alias = a;
449 return -3; /* alias available */
453 #endif
454 /* not found, see whether it is an abbreviation */
455 /* aliases may not be abbreviated */
456 n = strlen( keyword );
457 for(i=0; opts[i].short_opt; i++ ) {
458 if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
459 int j;
460 for(j=i+1; opts[j].short_opt; j++ ) {
461 if( opts[j].long_opt
462 && !strncmp( opts[j].long_opt, keyword, n ) )
463 return -2; /* abbreviation is ambiguous */
465 return i;
468 return -1;
472 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
474 int idx;
475 int argc;
476 char **argv;
477 char *s, *s2;
478 int i;
480 initialize( arg, NULL, NULL );
481 argc = *arg->argc;
482 argv = *arg->argv;
483 idx = arg->internal.idx;
485 if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
486 argc--; argv++; idx++;
489 next_one:
490 if( !argc ) { /* no more args */
491 arg->r_opt = 0;
492 goto leave; /* ready */
495 s = *argv;
496 arg->internal.last = s;
498 if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
499 arg->r_opt = -1; /* not an option but a argument */
500 arg->r_type = 2;
501 arg->r.ret_str = s;
502 argc--; argv++; idx++; /* set to next one */
504 else if( arg->internal.stopped ) { /* ready */
505 arg->r_opt = 0;
506 goto leave;
508 else if( *s == '-' && s[1] == '-' ) { /* long option */
509 char *argpos;
511 arg->internal.inarg = 0;
512 if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
513 arg->internal.stopped = 1;
514 argc--; argv++; idx++;
515 goto next_one;
518 argpos = strchr( s+2, '=' );
519 if( argpos )
520 *argpos = 0;
521 i = find_long_option( arg, opts, s+2 );
522 if( argpos )
523 *argpos = '=';
525 if( i < 0 && !strcmp( "help", s+2) )
526 show_help(opts, arg->flags);
527 else if( i < 0 && !strcmp( "version", s+2) ) {
528 if( !(arg->flags & (1<<6)) ) {
529 show_version();
530 exit(0);
533 else if( i < 0 && !strcmp( "warranty", s+2) ) {
534 puts( strusage(16) );
535 exit(0);
537 else if( i < 0 && !strcmp( "dump-options", s+2) ) {
538 for(i=0; opts[i].short_opt; i++ ) {
539 if( opts[i].long_opt )
540 printf( "--%s\n", opts[i].long_opt );
542 fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
543 exit(0);
546 if( i == -2 ) /* ambiguous option */
547 arg->r_opt = -8;
548 else if( i == -1 ) {
549 arg->r_opt = -2;
550 arg->r.ret_str = s+2;
552 else
553 arg->r_opt = opts[i].short_opt;
554 if( i < 0 )
556 else if( (opts[i].flags & 7) ) {
557 if( argpos ) {
558 s2 = argpos+1;
559 if( !*s2 )
560 s2 = NULL;
562 else
563 s2 = argv[1];
564 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
565 arg->r_type = 0; /* because it is optional */
567 else if( !s2 ) {
568 arg->r_opt = -3; /* missing argument */
570 else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
571 /* the argument is optional and the next seems to be
572 * an option. We do not check this possible option
573 * but assume no argument */
574 arg->r_type = 0;
576 else {
577 set_opt_arg(arg, opts[i].flags, s2);
578 if( !argpos ) {
579 argc--; argv++; idx++; /* skip one */
583 else { /* does not take an argument */
584 if( argpos )
585 arg->r_type = -6; /* argument not expected */
586 else
587 arg->r_type = 0;
589 argc--; argv++; idx++; /* set to next one */
591 else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
592 int dash_kludge = 0;
593 i = 0;
594 if( !arg->internal.inarg ) {
595 arg->internal.inarg++;
596 if( arg->flags & (1<<5) ) {
597 for(i=0; opts[i].short_opt; i++ )
598 if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
599 dash_kludge=1;
600 break;
604 s += arg->internal.inarg;
606 if( !dash_kludge ) {
607 for(i=0; opts[i].short_opt; i++ )
608 if( opts[i].short_opt == *s )
609 break;
612 if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
613 show_help(opts, arg->flags);
615 arg->r_opt = opts[i].short_opt;
616 if( !opts[i].short_opt ) {
617 arg->r_opt = (opts[i].flags & 256)? -7:-2;
618 arg->internal.inarg++; /* point to the next arg */
619 arg->r.ret_str = s;
621 else if( (opts[i].flags & 7) ) {
622 if( s[1] && !dash_kludge ) {
623 s2 = s+1;
624 set_opt_arg(arg, opts[i].flags, s2);
626 else {
627 s2 = argv[1];
628 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
629 arg->r_type = 0; /* because it is optional */
631 else if( !s2 ) {
632 arg->r_opt = -3; /* missing argument */
634 else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
635 /* the argument is optional and the next seems to be
636 * an option. We do not check this possible option
637 * but assume no argument */
638 arg->r_type = 0;
640 else {
641 set_opt_arg(arg, opts[i].flags, s2);
642 argc--; argv++; idx++; /* skip one */
645 s = "x"; /* so that !s[1] yields false */
647 else { /* does not take an argument */
648 arg->r_type = 0;
649 arg->internal.inarg++; /* point to the next arg */
651 if( !s[1] || dash_kludge ) { /* no more concatenated short options */
652 arg->internal.inarg = 0;
653 argc--; argv++; idx++;
656 else if( arg->flags & (1<<2) ) {
657 arg->r_opt = -1; /* not an option but a argument */
658 arg->r_type = 2;
659 arg->r.ret_str = s;
660 argc--; argv++; idx++; /* set to next one */
662 else {
663 arg->internal.stopped = 1; /* stop option processing */
664 goto next_one;
667 leave:
668 *arg->argc = argc;
669 *arg->argv = argv;
670 arg->internal.idx = idx;
671 return arg->r_opt;
676 static int
677 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
679 int base = (flags & 16)? 0 : 10;
681 switch( arg->r_type = (flags & 7) ) {
682 case 1: /* takes int argument */
683 arg->r.ret_int = (int)strtol(s,NULL,base);
684 return 0;
685 case 3: /* takes long argument */
686 arg->r.ret_long= strtol(s,NULL,base);
687 return 0;
688 case 4: /* takes ulong argument */
689 arg->r.ret_ulong= strtoul(s,NULL,base);
690 return 0;
691 case 2: /* takes string argument */
692 default:
693 arg->r.ret_str = s;
694 return 1;
699 static size_t
700 long_opt_strlen( ARGPARSE_OPTS *o )
702 size_t n = strlen(o->long_opt);
704 if( o->description && *o->description == '|' ) {
705 const char *s;
707 s=o->description+1;
708 if( *s != '=' )
709 n++;
710 for(; *s && *s != '|'; s++ )
711 n++;
713 return n;
716 /****************
717 * Print formatted help. The description string has some special
718 * meanings:
719 * - A description string which is "@" suppresses help output for
720 * this option
721 * - a description,ine which starts with a '@' and is followed by
722 * any other characters is printed as is; this may be used for examples
723 * ans such.
724 * - A description which starts with a '|' outputs the string between this
725 * bar and the next one as arguments of the long option.
727 static void
728 show_help( ARGPARSE_OPTS *opts, unsigned flags )
730 const char *s;
732 show_version();
733 putchar('\n');
734 s = strusage(41);
735 puts(s);
736 if( opts[0].description ) { /* auto format the option description */
737 int i,j, indent;
738 /* get max. length of long options */
739 for(i=indent=0; opts[i].short_opt; i++ ) {
740 if( opts[i].long_opt )
741 if( !opts[i].description || *opts[i].description != '@' )
742 if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
743 indent = j;
745 /* example: " -v, --verbose Viele Sachen ausgeben" */
746 indent += 10;
747 if( *opts[0].description != '@' )
748 puts("Options:");
749 for(i=0; opts[i].short_opt; i++ ) {
750 s = _( opts[i].description );
751 if( s && *s== '@' && !s[1] ) /* hide this line */
752 continue;
753 if( s && *s == '@' ) { /* unindented comment only line */
754 for(s++; *s; s++ ) {
755 if( *s == '\n' ) {
756 if( s[1] )
757 putchar('\n');
759 else
760 putchar(*s);
762 putchar('\n');
763 continue;
766 j = 3;
767 if( opts[i].short_opt < 256 ) {
768 printf(" -%c", opts[i].short_opt );
769 if( !opts[i].long_opt ) {
770 if(s && *s == '|' ) {
771 putchar(' '); j++;
772 for(s++ ; *s && *s != '|'; s++, j++ )
773 putchar(*s);
774 if( *s )
775 s++;
779 else
780 fputs(" ", stdout);
781 if( opts[i].long_opt ) {
782 j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
783 opts[i].long_opt );
784 if(s && *s == '|' ) {
785 if( *++s != '=' ) {
786 putchar(' ');
787 j++;
789 for( ; *s && *s != '|'; s++, j++ )
790 putchar(*s);
791 if( *s )
792 s++;
794 fputs(" ", stdout);
795 j += 3;
797 for(;j < indent; j++ )
798 putchar(' ');
799 if( s ) {
800 if( *s && j > indent ) {
801 putchar('\n');
802 for(j=0;j < indent; j++ )
803 putchar(' ');
805 for(; *s; s++ ) {
806 if( *s == '\n' ) {
807 if( s[1] ) {
808 putchar('\n');
809 for(j=0;j < indent; j++ )
810 putchar(' ');
813 else
814 putchar(*s);
817 putchar('\n');
819 if( flags & 32 )
820 puts("\n(A single dash may be used instead of the double ones)");
822 if( (s=strusage(19)) ) { /* bug reports to ... */
823 putchar('\n');
824 fputs(s, stdout);
826 fflush(stdout);
827 exit(0);
830 static void
831 show_version()
833 const char *s;
834 int i;
835 /* version line */
836 fputs(strusage(11), stdout);
837 if( (s=strusage(12)) )
838 printf(" (%s)", s );
839 printf(" %s\n", strusage(13) );
840 /* additional version lines */
841 for(i=20; i < 30; i++ )
842 if( (s=strusage(i)) )
843 printf("%s\n", s );
844 /* copyright string */
845 if( (s=strusage(14)) )
846 printf("%s\n", s );
847 /* copying conditions */
848 if( (s=strusage(15)) )
849 fputs(s, stdout);
850 /* thanks */
851 if( (s=strusage(18)) )
852 fputs(s, stdout);
853 /* additional program info */
854 for(i=30; i < 40; i++ )
855 if( (s=strusage(i)) )
856 fputs (s, stdout);
857 fflush(stdout);
861 void
862 usage( int level )
864 if( !level ) {
865 fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
866 strusage(14) );
867 fflush(stderr);
869 else if( level == 1 ) {
870 fputs(strusage(40),stderr);
871 exit(2);
873 else if( level == 2 ) {
874 puts(strusage(41));
875 exit(0);
879 /* Level
880 * 0: Copyright String auf stderr ausgeben
881 * 1: Kurzusage auf stderr ausgeben und beenden
882 * 2: Langusage auf stdout ausgeben und beenden
883 * 11: name of program
884 * 12: optional name of package which includes this program.
885 * 13: version string
886 * 14: copyright string
887 * 15: Short copying conditions (with LFs)
888 * 16: Long copying conditions (with LFs)
889 * 17: Optional printable OS name
890 * 18: Optional thanks list (with LFs)
891 * 19: Bug report info
892 *20..29: Additional lib version strings.
893 *30..39: Additional program info (with LFs)
894 * 40: short usage note (with LF)
895 * 41: long usage note (with LF)
897 const char *
898 strusage( int level )
900 const char *p = strusage_handler? strusage_handler(level) : NULL;
902 if( p )
903 return p;
905 switch( level ) {
906 case 11: p = "foo"; break;
907 case 13: p = "0.0"; break;
908 case 14: p = "Copyright (C) 2006 Free Software Foundation, Inc."; break;
909 case 15: p =
910 "This program comes with ABSOLUTELY NO WARRANTY.\n"
911 "This is free software, and you are welcome to redistribute it\n"
912 "under certain conditions. See the file COPYING for details.\n"; break;
913 case 16: p =
914 "This is free software; you can redistribute it and/or modify\n"
915 "it under the terms of the GNU General Public License as published by\n"
916 "the Free Software Foundation; either version 2 of the License, or\n"
917 "(at your option) any later version.\n\n"
918 "It is distributed in the hope that it will be useful,\n"
919 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
920 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
921 "GNU General Public License for more details.\n\n"
922 "You should have received a copy of the GNU General Public License\n"
923 "along with this program; if not, write to the Free Software\n"
924 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n"
925 "USA.\n";
926 break;
927 case 40: /* short and long usage */
928 case 41: p = ""; break;
931 return p;
934 void
935 set_strusage( const char *(*f)( int ) )
937 strusage_handler = f;
941 #ifdef TEST
942 static struct {
943 int verbose;
944 int debug;
945 char *outfile;
946 char *crf;
947 int myopt;
948 int echo;
949 int a_long_one;
950 }opt;
953 main(int argc, char **argv)
955 ARGPARSE_OPTS opts[] = {
956 { 'v', "verbose", 0 , "Laut sein"},
957 { 'e', "echo" , 0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
958 { 'd', "debug", 0 , "Debug\nfalls mal etasws\nSchief geht"},
959 { 'o', "output", 2 },
960 { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
961 { 'm', "my-option", 1|8 },
962 { 500, "a-long-option", 0 },
963 {0} };
964 ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
965 int i;
967 while( ArgParse( &pargs, opts) ) {
968 switch( pargs.r_opt ) {
969 case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
970 case 'v': opt.verbose++; break;
971 case 'e': opt.echo++; break;
972 case 'd': opt.debug++; break;
973 case 'o': opt.outfile = pargs.r.ret_str; break;
974 case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
975 case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
976 case 500: opt.a_long_one++; break;
977 default : pargs.err = 1; break; /* force warning output */
980 for(i=0; i < argc; i++ )
981 printf("%3d -> (%s)\n", i, argv[i] );
982 puts("Options:");
983 if( opt.verbose )
984 printf(" verbose=%d\n", opt.verbose );
985 if( opt.debug )
986 printf(" debug=%d\n", opt.debug );
987 if( opt.outfile )
988 printf(" outfile=`%s'\n", opt.outfile );
989 if( opt.crf )
990 printf(" crffile=`%s'\n", opt.crf );
991 if( opt.myopt )
992 printf(" myopt=%d\n", opt.myopt );
993 if( opt.a_long_one )
994 printf(" a-long-one=%d\n", opt.a_long_one );
995 if( opt.echo )
996 printf(" echo=%d\n", opt.echo );
997 return 0;
999 #endif
1001 /**** bottom of file ****/