2007-05-19 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / jnlib / argparse.c
blob348a803a9fa55a3e88847eeba32baa012e3fd62a
1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2 * Copyright (C) 1998, 1999, 2000, 2001, 2006
3 * 2007 Free Software Foundation, Inc.
5 * This file is part of JNLIB.
7 * JNLIB is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
12 * JNLIB is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
23 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
29 #include "libjnlib-config.h"
30 #include "mischelp.h"
31 #include "stringhelp.h"
32 #include "logging.h"
33 #ifdef JNLIB_NEED_UTF8CONV
34 #include "utf8conv.h"
35 #endif
36 #include "argparse.h"
39 /*********************************
40 * @Summary arg_parse
41 * #include <wk/lib.h>
43 * typedef struct {
44 * char *argc; pointer to argc (value subject to change)
45 * char ***argv; pointer to argv (value subject to change)
46 * unsigned flags; Global flags (DO NOT CHANGE)
47 * int err; print error about last option
48 * 1 = warning, 2 = abort
49 * int r_opt; return option
50 * int r_type; type of return value (0 = no argument found)
51 * union {
52 * int ret_int;
53 * long ret_long
54 * ulong ret_ulong;
55 * char *ret_str;
56 * } r; Return values
57 * struct {
58 * int idx;
59 * const char *last;
60 * void *aliases;
61 * } internal; DO NOT CHANGE
62 * } ARGPARSE_ARGS;
64 * typedef struct {
65 * int short_opt;
66 * const char *long_opt;
67 * unsigned flags;
68 * } ARGPARSE_OPTS;
70 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
72 * @Description
73 * This is my replacement for getopt(). See the example for a typical usage.
74 * Global flags are:
75 * Bit 0 : Do not remove options form argv
76 * Bit 1 : Do not stop at last option but return other args
77 * with r_opt set to -1.
78 * Bit 2 : Assume options and real args are mixed.
79 * Bit 3 : Do not use -- to stop option processing.
80 * Bit 4 : Do not skip the first arg.
81 * Bit 5 : allow usage of long option with only one dash
82 * Bit 6 : ignore --version
83 * all other bits must be set to zero, this value is modified by the
84 * function, so assume this is write only.
85 * Local flags (for each option):
86 * Bit 2-0 : 0 = does not take an argument
87 * 1 = takes int argument
88 * 2 = takes string argument
89 * 3 = takes long argument
90 * 4 = takes ulong argument
91 * Bit 3 : argument is optional (r_type will the be set to 0)
92 * Bit 4 : allow 0x etc. prefixed values.
93 * Bit 7 : this is a command and not an option
94 * You stop the option processing by setting opts to NULL, the function will
95 * then return 0.
96 * @Return Value
97 * Returns the args.r_opt or 0 if ready
98 * r_opt may be -2/-7 to indicate an unknown option/command.
99 * @See Also
100 * ArgExpand
101 * @Notes
102 * You do not need to process the options 'h', '--help' or '--version'
103 * because this function includes standard help processing; but if you
104 * specify '-h', '--help' or '--version' you have to do it yourself.
105 * The option '--' stops argument processing; if bit 1 is set the function
106 * continues to return normal arguments.
107 * To process float args or unsigned args you must use a string args and do
108 * the conversion yourself.
109 * @Example
111 * ARGPARSE_OPTS opts[] = {
112 * { 'v', "verbose", 0 },
113 * { 'd', "debug", 0 },
114 * { 'o', "output", 2 },
115 * { 'c', "cross-ref", 2|8 },
116 * { 'm', "my-option", 1|8 },
117 * { 500, "have-no-short-option-for-this-long-option", 0 },
118 * {0} };
119 * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
121 * while( ArgParse( &pargs, &opts) ) {
122 * switch( pargs.r_opt ) {
123 * case 'v': opt.verbose++; break;
124 * case 'd': opt.debug++; break;
125 * case 'o': opt.outfile = pargs.r.ret_str; break;
126 * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
127 * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
128 * case 500: opt.a_long_one++; break
129 * default : pargs.err = 1; break; -- force warning output --
132 * if( argc > 1 )
133 * log_fatal( "Too many args");
137 typedef struct alias_def_s *ALIAS_DEF;
138 struct alias_def_s {
139 ALIAS_DEF next;
140 char *name; /* malloced buffer with name, \0, value */
141 const char *value; /* ptr into name */
144 static const char *(*strusage_handler)( int ) = NULL;
146 static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
147 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
148 static void show_version(void);
151 static void
152 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
154 if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
155 arg->internal.idx = 0;
156 arg->internal.last = NULL;
157 arg->internal.inarg = 0;
158 arg->internal.stopped = 0;
159 arg->internal.aliases = NULL;
160 arg->internal.cur_alias = NULL;
161 arg->err = 0;
162 arg->flags |= 1<<15; /* mark initialized */
163 if( *arg->argc < 0 )
164 jnlib_log_bug("Invalid argument for ArgParse\n");
168 if( arg->err ) { /* last option was erroneous */
169 const char *s;
171 if( filename ) {
172 if( arg->r_opt == -6 )
173 s = "argument not expected\n";
174 else if( arg->r_opt == -5 )
175 s = "read error\n";
176 else if( arg->r_opt == -4 )
177 s = "keyword too long\n";
178 else if( arg->r_opt == -3 )
179 s = "missing argument\n";
180 else if( arg->r_opt == -7 )
181 s = "invalid command\n";
182 else if( arg->r_opt == -10 )
183 s = "invalid alias definition\n";
184 else
185 s = "invalid option\n";
186 jnlib_log_error("%s:%u: %s\n", filename, *lineno, s);
188 else {
189 s = arg->internal.last? arg->internal.last:"[??]";
191 if( arg->r_opt == -3 )
192 jnlib_log_error ("Missing argument for option \"%.50s\"\n", s);
193 else if( arg->r_opt == -6 )
194 jnlib_log_error ("Option \"%.50s\" does not expect an argument\n",
195 s );
196 else if( arg->r_opt == -7 )
197 jnlib_log_error ("Invalid command \"%.50s\"\n", s);
198 else if( arg->r_opt == -8 )
199 jnlib_log_error ("Option \"%.50s\" is ambiguous\n", s);
200 else if( arg->r_opt == -9 )
201 jnlib_log_error ("Command \"%.50s\" is ambiguous\n",s );
202 else
203 jnlib_log_error ("Invalid option \"%.50s\"\n", s);
205 if( arg->err != 1 )
206 exit(2);
207 arg->err = 0;
210 /* clearout the return value union */
211 arg->r.ret_str = NULL;
212 arg->r.ret_long= 0;
216 static void
217 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
219 /* TODO: replace this dummy function with a rea one
220 * and fix the probelms IRIX has with (ALIAS_DEV)arg..
221 * used as lvalue
223 #if 0
224 ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
225 a->name = name;
226 a->value = value;
227 a->next = (ALIAS_DEF)arg->internal.aliases;
228 (ALIAS_DEF)arg->internal.aliases = a;
229 #endif
232 /****************
233 * Get options from a file.
234 * Lines starting with '#' are comment lines.
235 * Syntax is simply a keyword and the argument.
236 * Valid keywords are all keywords from the long_opt list without
237 * the leading dashes. The special keywords "help", "warranty" and "version"
238 * are not valid here.
239 * The special keyword "alias" may be used to store alias definitions,
240 * which are later expanded like long options.
241 * Caller must free returned strings.
242 * If called with FP set to NULL command line args are parse instead.
244 * Q: Should we allow the syntax
245 * keyword = value
246 * and accept for boolean options a value of 1/0, yes/no or true/false?
247 * Note: Abbreviation of options is here not allowed.
250 optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
251 ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
253 int state, i, c;
254 int idx=0;
255 char keyword[100];
256 char *buffer = NULL;
257 size_t buflen = 0;
258 int inverse=0;
259 int in_alias=0;
261 if( !fp ) /* same as arg_parse() in this case */
262 return arg_parse( arg, opts );
264 initialize( arg, filename, lineno );
266 /* find the next keyword */
267 state = i = 0;
268 for(;;) {
269 c=getc(fp);
270 if( c == '\n' || c== EOF ) {
271 if( c != EOF )
272 ++*lineno;
273 if( state == -1 )
274 break;
275 else if( state == 2 ) {
276 keyword[i] = 0;
277 for(i=0; opts[i].short_opt; i++ )
278 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
279 break;
280 idx = i;
281 arg->r_opt = opts[idx].short_opt;
282 if( inverse ) /* this does not have an effect, hmmm */
283 arg->r_opt = -arg->r_opt;
284 if( !opts[idx].short_opt ) /* unknown command/option */
285 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
286 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
287 arg->r_type = 0; /* okay */
288 else if( (opts[idx].flags & 8) ) /* argument is optional */
289 arg->r_type = 0; /* okay */
290 else /* required argument */
291 arg->r_opt = -3; /* error */
292 break;
294 else if( state == 3 ) { /* no argument found */
295 if( in_alias )
296 arg->r_opt = -3; /* error */
297 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
298 arg->r_type = 0; /* okay */
299 else if( (opts[idx].flags & 8) ) /* no optional argument */
300 arg->r_type = 0; /* okay */
301 else /* no required argument */
302 arg->r_opt = -3; /* error */
303 break;
305 else if( state == 4 ) { /* have an argument */
306 if( in_alias ) {
307 if( !buffer )
308 arg->r_opt = -6;
309 else {
310 char *p;
312 buffer[i] = 0;
313 p = strpbrk( buffer, " \t" );
314 if( p ) {
315 *p++ = 0;
316 trim_spaces( p );
318 if( !p || !*p ) {
319 jnlib_free( buffer );
320 arg->r_opt = -10;
322 else {
323 store_alias( arg, buffer, p );
327 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
328 arg->r_opt = -6; /* error */
329 else {
330 char *p;
331 if( !buffer ) {
332 keyword[i] = 0;
333 buffer = jnlib_xstrdup(keyword);
335 else
336 buffer[i] = 0;
338 trim_spaces( buffer );
339 p = buffer;
340 if( *p == '"' ) { /* remove quotes */
341 p++;
342 if( *p && p[strlen(p)-1] == '"' )
343 p[strlen(p)-1] = 0;
345 if( !set_opt_arg(arg, opts[idx].flags, p) )
346 jnlib_free(buffer);
348 break;
350 else if( c == EOF ) {
351 if( ferror(fp) )
352 arg->r_opt = -5; /* read error */
353 else
354 arg->r_opt = 0; /* eof */
355 break;
357 state = 0;
358 i = 0;
360 else if( state == -1 )
361 ; /* skip */
362 else if( !state && isspace(c) )
363 ; /* skip leading white space */
364 else if( !state && c == '#' )
365 state = 1; /* start of a comment */
366 else if( state == 1 )
367 ; /* skip comments */
368 else if( state == 2 && isspace(c) ) {
369 keyword[i] = 0;
370 for(i=0; opts[i].short_opt; i++ )
371 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
372 break;
373 idx = i;
374 arg->r_opt = opts[idx].short_opt;
375 if( !opts[idx].short_opt ) {
376 if( !strcmp( keyword, "alias" ) ) {
377 in_alias = 1;
378 state = 3;
380 else {
381 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
382 state = -1; /* skip rest of line and leave */
385 else
386 state = 3;
388 else if( state == 3 ) { /* skip leading spaces of the argument */
389 if( !isspace(c) ) {
390 i = 0;
391 keyword[i++] = c;
392 state = 4;
395 else if( state == 4 ) { /* collect the argument */
396 if( buffer ) {
397 if( i < buflen-1 )
398 buffer[i++] = c;
399 else {
400 buflen += 50;
401 buffer = jnlib_xrealloc(buffer, buflen);
402 buffer[i++] = c;
405 else if( i < DIM(keyword)-1 )
406 keyword[i++] = c;
407 else {
408 buflen = DIM(keyword)+50;
409 buffer = jnlib_xmalloc(buflen);
410 memcpy(buffer, keyword, i);
411 buffer[i++] = c;
414 else if( i >= DIM(keyword)-1 ) {
415 arg->r_opt = -4; /* keyword to long */
416 state = -1; /* skip rest of line and leave */
418 else {
419 keyword[i++] = c;
420 state = 2;
424 return arg->r_opt;
429 static int
430 find_long_option( ARGPARSE_ARGS *arg,
431 ARGPARSE_OPTS *opts, const char *keyword )
433 int i;
434 size_t n;
436 /* Would be better if we can do a binary search, but it is not
437 possible to reorder our option table because we would mess
438 up our help strings - What we can do is: Build a nice option
439 lookup table wehn this function is first invoked */
440 if( !*keyword )
441 return -1;
442 for(i=0; opts[i].short_opt; i++ )
443 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
444 return i;
445 #if 0
447 ALIAS_DEF a;
448 /* see whether it is an alias */
449 for( a = args->internal.aliases; a; a = a->next ) {
450 if( !strcmp( a->name, keyword) ) {
451 /* todo: must parse the alias here */
452 args->internal.cur_alias = a;
453 return -3; /* alias available */
457 #endif
458 /* not found, see whether it is an abbreviation */
459 /* aliases may not be abbreviated */
460 n = strlen( keyword );
461 for(i=0; opts[i].short_opt; i++ ) {
462 if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
463 int j;
464 for(j=i+1; opts[j].short_opt; j++ ) {
465 if( opts[j].long_opt
466 && !strncmp( opts[j].long_opt, keyword, n ) )
467 return -2; /* abbreviation is ambiguous */
469 return i;
472 return -1;
476 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
478 int idx;
479 int argc;
480 char **argv;
481 char *s, *s2;
482 int i;
484 initialize( arg, NULL, NULL );
485 argc = *arg->argc;
486 argv = *arg->argv;
487 idx = arg->internal.idx;
489 if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
490 argc--; argv++; idx++;
493 next_one:
494 if( !argc ) { /* no more args */
495 arg->r_opt = 0;
496 goto leave; /* ready */
499 s = *argv;
500 arg->internal.last = s;
502 if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
503 arg->r_opt = -1; /* not an option but a argument */
504 arg->r_type = 2;
505 arg->r.ret_str = s;
506 argc--; argv++; idx++; /* set to next one */
508 else if( arg->internal.stopped ) { /* ready */
509 arg->r_opt = 0;
510 goto leave;
512 else if( *s == '-' && s[1] == '-' ) { /* long option */
513 char *argpos;
515 arg->internal.inarg = 0;
516 if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
517 arg->internal.stopped = 1;
518 argc--; argv++; idx++;
519 goto next_one;
522 argpos = strchr( s+2, '=' );
523 if( argpos )
524 *argpos = 0;
525 i = find_long_option( arg, opts, s+2 );
526 if( argpos )
527 *argpos = '=';
529 if( i < 0 && !strcmp( "help", s+2) )
530 show_help(opts, arg->flags);
531 else if( i < 0 && !strcmp( "version", s+2) ) {
532 if( !(arg->flags & (1<<6)) ) {
533 show_version();
534 exit(0);
537 else if( i < 0 && !strcmp( "warranty", s+2) ) {
538 puts( strusage(16) );
539 exit(0);
541 else if( i < 0 && !strcmp( "dump-options", s+2) ) {
542 for(i=0; opts[i].short_opt; i++ ) {
543 if( opts[i].long_opt )
544 printf( "--%s\n", opts[i].long_opt );
546 fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
547 exit(0);
550 if( i == -2 ) /* ambiguous option */
551 arg->r_opt = -8;
552 else if( i == -1 ) {
553 arg->r_opt = -2;
554 arg->r.ret_str = s+2;
556 else
557 arg->r_opt = opts[i].short_opt;
558 if( i < 0 )
560 else if( (opts[i].flags & 7) ) {
561 if( argpos ) {
562 s2 = argpos+1;
563 if( !*s2 )
564 s2 = NULL;
566 else
567 s2 = argv[1];
568 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
569 arg->r_type = 0; /* because it is optional */
571 else if( !s2 ) {
572 arg->r_opt = -3; /* missing argument */
574 else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
575 /* the argument is optional and the next seems to be
576 * an option. We do not check this possible option
577 * but assume no argument */
578 arg->r_type = 0;
580 else {
581 set_opt_arg(arg, opts[i].flags, s2);
582 if( !argpos ) {
583 argc--; argv++; idx++; /* skip one */
587 else { /* does not take an argument */
588 if( argpos )
589 arg->r_type = -6; /* argument not expected */
590 else
591 arg->r_type = 0;
593 argc--; argv++; idx++; /* set to next one */
595 else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
596 int dash_kludge = 0;
597 i = 0;
598 if( !arg->internal.inarg ) {
599 arg->internal.inarg++;
600 if( arg->flags & (1<<5) ) {
601 for(i=0; opts[i].short_opt; i++ )
602 if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
603 dash_kludge=1;
604 break;
608 s += arg->internal.inarg;
610 if( !dash_kludge ) {
611 for(i=0; opts[i].short_opt; i++ )
612 if( opts[i].short_opt == *s )
613 break;
616 if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
617 show_help(opts, arg->flags);
619 arg->r_opt = opts[i].short_opt;
620 if( !opts[i].short_opt ) {
621 arg->r_opt = (opts[i].flags & 256)? -7:-2;
622 arg->internal.inarg++; /* point to the next arg */
623 arg->r.ret_str = s;
625 else if( (opts[i].flags & 7) ) {
626 if( s[1] && !dash_kludge ) {
627 s2 = s+1;
628 set_opt_arg(arg, opts[i].flags, s2);
630 else {
631 s2 = argv[1];
632 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
633 arg->r_type = 0; /* because it is optional */
635 else if( !s2 ) {
636 arg->r_opt = -3; /* missing argument */
638 else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
639 /* the argument is optional and the next seems to be
640 * an option. We do not check this possible option
641 * but assume no argument */
642 arg->r_type = 0;
644 else {
645 set_opt_arg(arg, opts[i].flags, s2);
646 argc--; argv++; idx++; /* skip one */
649 s = "x"; /* so that !s[1] yields false */
651 else { /* does not take an argument */
652 arg->r_type = 0;
653 arg->internal.inarg++; /* point to the next arg */
655 if( !s[1] || dash_kludge ) { /* no more concatenated short options */
656 arg->internal.inarg = 0;
657 argc--; argv++; idx++;
660 else if( arg->flags & (1<<2) ) {
661 arg->r_opt = -1; /* not an option but a argument */
662 arg->r_type = 2;
663 arg->r.ret_str = s;
664 argc--; argv++; idx++; /* set to next one */
666 else {
667 arg->internal.stopped = 1; /* stop option processing */
668 goto next_one;
671 leave:
672 *arg->argc = argc;
673 *arg->argv = argv;
674 arg->internal.idx = idx;
675 return arg->r_opt;
680 static int
681 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
683 int base = (flags & 16)? 0 : 10;
685 switch( arg->r_type = (flags & 7) ) {
686 case 1: /* takes int argument */
687 arg->r.ret_int = (int)strtol(s,NULL,base);
688 return 0;
689 case 3: /* takes long argument */
690 arg->r.ret_long= strtol(s,NULL,base);
691 return 0;
692 case 4: /* takes ulong argument */
693 arg->r.ret_ulong= strtoul(s,NULL,base);
694 return 0;
695 case 2: /* takes string argument */
696 default:
697 arg->r.ret_str = s;
698 return 1;
703 static size_t
704 long_opt_strlen( ARGPARSE_OPTS *o )
706 size_t n = strlen (o->long_opt);
708 if ( o->description && *o->description == '|' )
710 const char *s;
711 #ifdef JNLIB_NEED_UTF8CONV
712 int is_utf8 = is_native_utf8 ();
713 #endif
715 s=o->description+1;
716 if ( *s != '=' )
717 n++;
718 /* For a (mostly) correct length calculation we exclude
719 continuation bytes (10xxxxxx) if we are on a native utf8
720 terminal. */
721 for (; *s && *s != '|'; s++ )
722 #ifdef JNLIB_NEED_UTF8CONV
723 if ( is_utf8 && (*s&0xc0) != 0x80 )
724 #endif
725 n++;
727 return n;
730 /****************
731 * Print formatted help. The description string has some special
732 * meanings:
733 * - A description string which is "@" suppresses help output for
734 * this option
735 * - a description,ine which starts with a '@' and is followed by
736 * any other characters is printed as is; this may be used for examples
737 * ans such.
738 * - A description which starts with a '|' outputs the string between this
739 * bar and the next one as arguments of the long option.
741 static void
742 show_help( ARGPARSE_OPTS *opts, unsigned flags )
744 const char *s;
746 show_version();
747 putchar('\n');
748 s = strusage(41);
749 puts(s);
750 if( opts[0].description ) { /* auto format the option description */
751 int i,j, indent;
752 /* get max. length of long options */
753 for(i=indent=0; opts[i].short_opt; i++ ) {
754 if( opts[i].long_opt )
755 if( !opts[i].description || *opts[i].description != '@' )
756 if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
757 indent = j;
759 /* example: " -v, --verbose Viele Sachen ausgeben" */
760 indent += 10;
761 if( *opts[0].description != '@' )
762 puts("Options:");
763 for(i=0; opts[i].short_opt; i++ ) {
764 s = _( opts[i].description );
765 if( s && *s== '@' && !s[1] ) /* hide this line */
766 continue;
767 if( s && *s == '@' ) { /* unindented comment only line */
768 for(s++; *s; s++ ) {
769 if( *s == '\n' ) {
770 if( s[1] )
771 putchar('\n');
773 else
774 putchar(*s);
776 putchar('\n');
777 continue;
780 j = 3;
781 if( opts[i].short_opt < 256 ) {
782 printf(" -%c", opts[i].short_opt );
783 if( !opts[i].long_opt ) {
784 if(s && *s == '|' ) {
785 putchar(' '); j++;
786 for(s++ ; *s && *s != '|'; s++, j++ )
787 putchar(*s);
788 if( *s )
789 s++;
793 else
794 fputs(" ", stdout);
795 if( opts[i].long_opt ) {
796 j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
797 opts[i].long_opt );
798 if(s && *s == '|' ) {
799 if( *++s != '=' ) {
800 putchar(' ');
801 j++;
803 for( ; *s && *s != '|'; s++, j++ )
804 putchar(*s);
805 if( *s )
806 s++;
808 fputs(" ", stdout);
809 j += 3;
811 for(;j < indent; j++ )
812 putchar(' ');
813 if( s ) {
814 if( *s && j > indent ) {
815 putchar('\n');
816 for(j=0;j < indent; j++ )
817 putchar(' ');
819 for(; *s; s++ ) {
820 if( *s == '\n' ) {
821 if( s[1] ) {
822 putchar('\n');
823 for(j=0;j < indent; j++ )
824 putchar(' ');
827 else
828 putchar(*s);
831 putchar('\n');
833 if( flags & 32 )
834 puts("\n(A single dash may be used instead of the double ones)");
836 if( (s=strusage(19)) ) { /* bug reports to ... */
837 putchar('\n');
838 fputs(s, stdout);
840 fflush(stdout);
841 exit(0);
844 static void
845 show_version()
847 const char *s;
848 int i;
849 /* version line */
850 fputs(strusage(11), stdout);
851 if( (s=strusage(12)) )
852 printf(" (%s)", s );
853 printf(" %s\n", strusage(13) );
854 /* additional version lines */
855 for(i=20; i < 30; i++ )
856 if( (s=strusage(i)) )
857 printf("%s\n", s );
858 /* copyright string */
859 if( (s=strusage(14)) )
860 printf("%s\n", s );
861 /* copying conditions */
862 if( (s=strusage(15)) )
863 fputs(s, stdout);
864 /* thanks */
865 if( (s=strusage(18)) )
866 fputs(s, stdout);
867 /* additional program info */
868 for(i=30; i < 40; i++ )
869 if( (s=strusage(i)) )
870 fputs (s, stdout);
871 fflush(stdout);
875 void
876 usage( int level )
878 if( !level ) {
879 fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
880 strusage(14) );
881 fflush(stderr);
883 else if( level == 1 ) {
884 fputs(strusage(40),stderr);
885 exit(2);
887 else if( level == 2 ) {
888 puts(strusage(41));
889 exit(0);
893 /* Level
894 * 0: Copyright String auf stderr ausgeben
895 * 1: Kurzusage auf stderr ausgeben und beenden
896 * 2: Langusage auf stdout ausgeben und beenden
897 * 11: name of program
898 * 12: optional name of package which includes this program.
899 * 13: version string
900 * 14: copyright string
901 * 15: Short copying conditions (with LFs)
902 * 16: Long copying conditions (with LFs)
903 * 17: Optional printable OS name
904 * 18: Optional thanks list (with LFs)
905 * 19: Bug report info
906 *20..29: Additional lib version strings.
907 *30..39: Additional program info (with LFs)
908 * 40: short usage note (with LF)
909 * 41: long usage note (with LF)
911 const char *
912 strusage( int level )
914 const char *p = strusage_handler? strusage_handler(level) : NULL;
916 if( p )
917 return p;
919 switch( level ) {
920 case 11: p = "foo"; break;
921 case 13: p = "0.0"; break;
922 case 14: p = "Copyright (C) 2007 Free Software Foundation, Inc."; break;
923 case 15: p =
924 "This program comes with ABSOLUTELY NO WARRANTY.\n"
925 "This is free software, and you are welcome to redistribute it\n"
926 "under certain conditions. See the file COPYING for details.\n"; break;
927 case 16: p =
928 "This is free software; you can redistribute it and/or modify\n"
929 "it under the terms of the GNU General Public License as published by\n"
930 "the Free Software Foundation; either version 2 of the License, or\n"
931 "(at your option) any later version.\n\n"
932 "It is distributed in the hope that it will be useful,\n"
933 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
934 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
935 "GNU General Public License for more details.\n\n"
936 "You should have received a copy of the GNU General Public License\n"
937 "along with this program; if not, write to the Free Software\n"
938 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,\n"
939 "USA.\n";
940 break;
941 case 40: /* short and long usage */
942 case 41: p = ""; break;
945 return p;
948 void
949 set_strusage( const char *(*f)( int ) )
951 strusage_handler = f;
955 #ifdef TEST
956 static struct {
957 int verbose;
958 int debug;
959 char *outfile;
960 char *crf;
961 int myopt;
962 int echo;
963 int a_long_one;
964 }opt;
967 main(int argc, char **argv)
969 ARGPARSE_OPTS opts[] = {
970 { 'v', "verbose", 0 , "Laut sein"},
971 { 'e', "echo" , 0 , ("Zeile ausgeben, damit wir sehen, was wir ein"
972 " gegeben haben")},
973 { 'd', "debug", 0 , "Debug\nfalls mal etwas\nschief geht"},
974 { 'o', "output", 2 },
975 { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
976 /* Note that on a non-utf8 terminal the ß might garble the output. */
977 { 's', "street", 0, "|Straße|set the name of the street to Straße" },
978 { 'm', "my-option", 1|8 },
979 { 500, "a-long-option", 0 },
980 {0} };
981 ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
982 int i;
984 while( arg_parse ( &pargs, opts) ) {
985 switch( pargs.r_opt ) {
986 case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
987 case 'v': opt.verbose++; break;
988 case 'e': opt.echo++; break;
989 case 'd': opt.debug++; break;
990 case 'o': opt.outfile = pargs.r.ret_str; break;
991 case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
992 case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
993 case 500: opt.a_long_one++; break;
994 default : pargs.err = 1; break; /* force warning output */
997 for(i=0; i < argc; i++ )
998 printf("%3d -> (%s)\n", i, argv[i] );
999 puts("Options:");
1000 if( opt.verbose )
1001 printf(" verbose=%d\n", opt.verbose );
1002 if( opt.debug )
1003 printf(" debug=%d\n", opt.debug );
1004 if( opt.outfile )
1005 printf(" outfile=`%s'\n", opt.outfile );
1006 if( opt.crf )
1007 printf(" crffile=`%s'\n", opt.crf );
1008 if( opt.myopt )
1009 printf(" myopt=%d\n", opt.myopt );
1010 if( opt.a_long_one )
1011 printf(" a-long-one=%d\n", opt.a_long_one );
1012 if( opt.echo )
1013 printf(" echo=%d\n", opt.echo );
1014 return 0;
1016 #endif
1018 /**** bottom of file ****/