2008-06-18 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / jnlib / argparse.c
blob443a1e77e7d5fda7bcd56d0de6c7152c2a6733f0
1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2 * Copyright (C) 1998, 1999, 2000, 2001, 2006
3 * 2007, 2008 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 3 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, see <http://www.gnu.org/licenses/>.
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <string.h>
27 #include "libjnlib-config.h"
28 #include "mischelp.h"
29 #include "stringhelp.h"
30 #include "logging.h"
31 #ifdef JNLIB_NEED_UTF8CONV
32 #include "utf8conv.h"
33 #endif
34 #include "argparse.h"
37 /*********************************
38 * @Summary arg_parse
39 * #include <wk/lib.h>
41 * typedef struct {
42 * char *argc; pointer to argc (value subject to change)
43 * char ***argv; pointer to argv (value subject to change)
44 * unsigned flags; Global flags (DO NOT CHANGE)
45 * int err; print error about last option
46 * 1 = warning, 2 = abort
47 * int r_opt; return option
48 * int r_type; type of return value (0 = no argument found)
49 * union {
50 * int ret_int;
51 * long ret_long
52 * ulong ret_ulong;
53 * char *ret_str;
54 * } r; Return values
55 * struct {
56 * int idx;
57 * const char *last;
58 * void *aliases;
59 * } internal; DO NOT CHANGE
60 * } ARGPARSE_ARGS;
62 * typedef struct {
63 * int short_opt;
64 * const char *long_opt;
65 * unsigned flags;
66 * } ARGPARSE_OPTS;
68 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
70 * @Description
71 * This is my replacement for getopt(). See the example for a typical usage.
72 * Global flags are:
73 * Bit 0 : Do not remove options form argv
74 * Bit 1 : Do not stop at last option but return other args
75 * with r_opt set to -1.
76 * Bit 2 : Assume options and real args are mixed.
77 * Bit 3 : Do not use -- to stop option processing.
78 * Bit 4 : Do not skip the first arg.
79 * Bit 5 : allow usage of long option with only one dash
80 * Bit 6 : ignore --version
81 * all other bits must be set to zero, this value is modified by the
82 * function, so assume this is write only.
83 * Local flags (for each option):
84 * Bit 2-0 : 0 = does not take an argument
85 * 1 = takes int argument
86 * 2 = takes string argument
87 * 3 = takes long argument
88 * 4 = takes ulong argument
89 * Bit 3 : argument is optional (r_type will the be set to 0)
90 * Bit 4 : allow 0x etc. prefixed values.
91 * Bit 7 : this is a command and not an option
92 * You stop the option processing by setting opts to NULL, the function will
93 * then return 0.
94 * @Return Value
95 * Returns the args.r_opt or 0 if ready
96 * r_opt may be -2/-7 to indicate an unknown option/command.
97 * @See Also
98 * ArgExpand
99 * @Notes
100 * You do not need to process the options 'h', '--help' or '--version'
101 * because this function includes standard help processing; but if you
102 * specify '-h', '--help' or '--version' you have to do it yourself.
103 * The option '--' stops argument processing; if bit 1 is set the function
104 * continues to return normal arguments.
105 * To process float args or unsigned args you must use a string args and do
106 * the conversion yourself.
107 * @Example
109 * ARGPARSE_OPTS opts[] = {
110 * { 'v', "verbose", 0 },
111 * { 'd', "debug", 0 },
112 * { 'o', "output", 2 },
113 * { 'c', "cross-ref", 2|8 },
114 * { 'm', "my-option", 1|8 },
115 * { 500, "have-no-short-option-for-this-long-option", 0 },
116 * {0} };
117 * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
119 * while( ArgParse( &pargs, &opts) ) {
120 * switch( pargs.r_opt ) {
121 * case 'v': opt.verbose++; break;
122 * case 'd': opt.debug++; break;
123 * case 'o': opt.outfile = pargs.r.ret_str; break;
124 * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
125 * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
126 * case 500: opt.a_long_one++; break
127 * default : pargs.err = 1; break; -- force warning output --
130 * if( argc > 1 )
131 * log_fatal( "Too many args");
135 typedef struct alias_def_s *ALIAS_DEF;
136 struct alias_def_s {
137 ALIAS_DEF next;
138 char *name; /* malloced buffer with name, \0, value */
139 const char *value; /* ptr into name */
142 static const char *(*strusage_handler)( int ) = NULL;
144 static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
145 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
146 static void show_version(void);
149 static void
150 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
152 if( !(arg->flags & (1<<15)) )
154 /* 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 as initialized. */
163 if ( *arg->argc < 0 )
164 jnlib_log_bug ("invalid argument for arg_parsee\n");
168 if (arg->err)
170 /* Last option was erroneous. */
171 const char *s;
173 if (filename)
175 if ( arg->r_opt == -6 )
176 s = _("argument not expected");
177 else if ( arg->r_opt == -5 )
178 s = _("read error");
179 else if ( arg->r_opt == -4 )
180 s = _("keyword too long");
181 else if ( arg->r_opt == -3 )
182 s = _("missing argument");
183 else if ( arg->r_opt == -7 )
184 s = _("invalid command");
185 else if ( arg->r_opt == -10 )
186 s = _("invalid alias definition");
187 else
188 s = _("invalid option");
189 jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
191 else
193 s = arg->internal.last? arg->internal.last:"[??]";
195 if ( arg->r_opt == -3 )
196 jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s);
197 else if ( arg->r_opt == -6 )
198 jnlib_log_error (_("option \"%.50s\" does not expect an "
199 "argument\n"), s );
200 else if ( arg->r_opt == -7 )
201 jnlib_log_error (_("invalid command \"%.50s\"\n"), s);
202 else if ( arg->r_opt == -8 )
203 jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s);
204 else if ( arg->r_opt == -9 )
205 jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s );
206 else
207 jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
209 if ( arg->err != 1 )
210 exit (2);
211 arg->err = 0;
214 /* Zero out the return value union. */
215 arg->r.ret_str = NULL;
216 arg->r.ret_long = 0;
220 static void
221 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
223 /* TODO: replace this dummy function with a rea one
224 * and fix the probelms IRIX has with (ALIAS_DEV)arg..
225 * used as lvalue
227 #if 0
228 ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
229 a->name = name;
230 a->value = value;
231 a->next = (ALIAS_DEF)arg->internal.aliases;
232 (ALIAS_DEF)arg->internal.aliases = a;
233 #endif
236 /****************
237 * Get options from a file.
238 * Lines starting with '#' are comment lines.
239 * Syntax is simply a keyword and the argument.
240 * Valid keywords are all keywords from the long_opt list without
241 * the leading dashes. The special keywords "help", "warranty" and "version"
242 * are not valid here.
243 * The special keyword "alias" may be used to store alias definitions,
244 * which are later expanded like long options.
245 * Caller must free returned strings.
246 * If called with FP set to NULL command line args are parse instead.
248 * Q: Should we allow the syntax
249 * keyword = value
250 * and accept for boolean options a value of 1/0, yes/no or true/false?
251 * Note: Abbreviation of options is here not allowed.
254 optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
255 ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
257 int state, i, c;
258 int idx=0;
259 char keyword[100];
260 char *buffer = NULL;
261 size_t buflen = 0;
262 int inverse=0;
263 int in_alias=0;
265 if( !fp ) /* same as arg_parse() in this case */
266 return arg_parse( arg, opts );
268 initialize( arg, filename, lineno );
270 /* find the next keyword */
271 state = i = 0;
272 for(;;) {
273 c=getc(fp);
274 if( c == '\n' || c== EOF ) {
275 if( c != EOF )
276 ++*lineno;
277 if( state == -1 )
278 break;
279 else if( state == 2 ) {
280 keyword[i] = 0;
281 for(i=0; opts[i].short_opt; i++ )
282 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
283 break;
284 idx = i;
285 arg->r_opt = opts[idx].short_opt;
286 if( inverse ) /* this does not have an effect, hmmm */
287 arg->r_opt = -arg->r_opt;
288 if( !opts[idx].short_opt ) /* unknown command/option */
289 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
290 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
291 arg->r_type = 0; /* okay */
292 else if( (opts[idx].flags & 8) ) /* argument is optional */
293 arg->r_type = 0; /* okay */
294 else /* required argument */
295 arg->r_opt = -3; /* error */
296 break;
298 else if( state == 3 ) { /* no argument found */
299 if( in_alias )
300 arg->r_opt = -3; /* error */
301 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
302 arg->r_type = 0; /* okay */
303 else if( (opts[idx].flags & 8) ) /* no optional argument */
304 arg->r_type = 0; /* okay */
305 else /* no required argument */
306 arg->r_opt = -3; /* error */
307 break;
309 else if( state == 4 ) { /* have an argument */
310 if( in_alias ) {
311 if( !buffer )
312 arg->r_opt = -6;
313 else {
314 char *p;
316 buffer[i] = 0;
317 p = strpbrk( buffer, " \t" );
318 if( p ) {
319 *p++ = 0;
320 trim_spaces( p );
322 if( !p || !*p ) {
323 jnlib_free( buffer );
324 arg->r_opt = -10;
326 else {
327 store_alias( arg, buffer, p );
331 else if( !(opts[idx].flags & 7) ) /* does not take an arg */
332 arg->r_opt = -6; /* error */
333 else {
334 char *p;
335 if( !buffer ) {
336 keyword[i] = 0;
337 buffer = jnlib_xstrdup(keyword);
339 else
340 buffer[i] = 0;
342 trim_spaces( buffer );
343 p = buffer;
344 if( *p == '"' ) { /* remove quotes */
345 p++;
346 if( *p && p[strlen(p)-1] == '"' )
347 p[strlen(p)-1] = 0;
349 if( !set_opt_arg(arg, opts[idx].flags, p) )
350 jnlib_free(buffer);
352 break;
354 else if( c == EOF ) {
355 if( ferror(fp) )
356 arg->r_opt = -5; /* read error */
357 else
358 arg->r_opt = 0; /* eof */
359 break;
361 state = 0;
362 i = 0;
364 else if( state == -1 )
365 ; /* skip */
366 else if( !state && isspace(c) )
367 ; /* skip leading white space */
368 else if( !state && c == '#' )
369 state = 1; /* start of a comment */
370 else if( state == 1 )
371 ; /* skip comments */
372 else if( state == 2 && isspace(c) ) {
373 keyword[i] = 0;
374 for(i=0; opts[i].short_opt; i++ )
375 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
376 break;
377 idx = i;
378 arg->r_opt = opts[idx].short_opt;
379 if( !opts[idx].short_opt ) {
380 if( !strcmp( keyword, "alias" ) ) {
381 in_alias = 1;
382 state = 3;
384 else {
385 arg->r_opt = (opts[idx].flags & 256)? -7:-2;
386 state = -1; /* skip rest of line and leave */
389 else
390 state = 3;
392 else if( state == 3 ) { /* skip leading spaces of the argument */
393 if( !isspace(c) ) {
394 i = 0;
395 keyword[i++] = c;
396 state = 4;
399 else if( state == 4 ) { /* collect the argument */
400 if( buffer ) {
401 if( i < buflen-1 )
402 buffer[i++] = c;
403 else {
404 buflen += 50;
405 buffer = jnlib_xrealloc(buffer, buflen);
406 buffer[i++] = c;
409 else if( i < DIM(keyword)-1 )
410 keyword[i++] = c;
411 else {
412 buflen = DIM(keyword)+50;
413 buffer = jnlib_xmalloc(buflen);
414 memcpy(buffer, keyword, i);
415 buffer[i++] = c;
418 else if( i >= DIM(keyword)-1 ) {
419 arg->r_opt = -4; /* keyword to long */
420 state = -1; /* skip rest of line and leave */
422 else {
423 keyword[i++] = c;
424 state = 2;
428 return arg->r_opt;
433 static int
434 find_long_option( ARGPARSE_ARGS *arg,
435 ARGPARSE_OPTS *opts, const char *keyword )
437 int i;
438 size_t n;
440 /* Would be better if we can do a binary search, but it is not
441 possible to reorder our option table because we would mess
442 up our help strings - What we can do is: Build a nice option
443 lookup table wehn this function is first invoked */
444 if( !*keyword )
445 return -1;
446 for(i=0; opts[i].short_opt; i++ )
447 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
448 return i;
449 #if 0
451 ALIAS_DEF a;
452 /* see whether it is an alias */
453 for( a = args->internal.aliases; a; a = a->next ) {
454 if( !strcmp( a->name, keyword) ) {
455 /* todo: must parse the alias here */
456 args->internal.cur_alias = a;
457 return -3; /* alias available */
461 #endif
462 /* not found, see whether it is an abbreviation */
463 /* aliases may not be abbreviated */
464 n = strlen( keyword );
465 for(i=0; opts[i].short_opt; i++ ) {
466 if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
467 int j;
468 for(j=i+1; opts[j].short_opt; j++ ) {
469 if( opts[j].long_opt
470 && !strncmp( opts[j].long_opt, keyword, n ) )
471 return -2; /* abbreviation is ambiguous */
473 return i;
476 return -1;
480 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
482 int idx;
483 int argc;
484 char **argv;
485 char *s, *s2;
486 int i;
488 initialize( arg, NULL, NULL );
489 argc = *arg->argc;
490 argv = *arg->argv;
491 idx = arg->internal.idx;
493 if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
494 argc--; argv++; idx++;
497 next_one:
498 if( !argc ) { /* no more args */
499 arg->r_opt = 0;
500 goto leave; /* ready */
503 s = *argv;
504 arg->internal.last = s;
506 if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
507 arg->r_opt = -1; /* not an option but a argument */
508 arg->r_type = 2;
509 arg->r.ret_str = s;
510 argc--; argv++; idx++; /* set to next one */
512 else if( arg->internal.stopped ) { /* ready */
513 arg->r_opt = 0;
514 goto leave;
516 else if( *s == '-' && s[1] == '-' ) { /* long option */
517 char *argpos;
519 arg->internal.inarg = 0;
520 if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
521 arg->internal.stopped = 1;
522 argc--; argv++; idx++;
523 goto next_one;
526 argpos = strchr( s+2, '=' );
527 if( argpos )
528 *argpos = 0;
529 i = find_long_option( arg, opts, s+2 );
530 if( argpos )
531 *argpos = '=';
533 if( i < 0 && !strcmp( "help", s+2) )
534 show_help(opts, arg->flags);
535 else if( i < 0 && !strcmp( "version", s+2) ) {
536 if( !(arg->flags & (1<<6)) ) {
537 show_version();
538 exit(0);
541 else if( i < 0 && !strcmp( "warranty", s+2) ) {
542 puts( strusage(16) );
543 exit(0);
545 else if( i < 0 && !strcmp( "dump-options", s+2) ) {
546 for(i=0; opts[i].short_opt; i++ ) {
547 if( opts[i].long_opt )
548 printf( "--%s\n", opts[i].long_opt );
550 fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
551 exit(0);
554 if( i == -2 ) /* ambiguous option */
555 arg->r_opt = -8;
556 else if( i == -1 ) {
557 arg->r_opt = -2;
558 arg->r.ret_str = s+2;
560 else
561 arg->r_opt = opts[i].short_opt;
562 if( i < 0 )
564 else if( (opts[i].flags & 7) ) {
565 if( argpos ) {
566 s2 = argpos+1;
567 if( !*s2 )
568 s2 = NULL;
570 else
571 s2 = argv[1];
572 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
573 arg->r_type = 0; /* because it is optional */
575 else if( !s2 ) {
576 arg->r_opt = -3; /* missing argument */
578 else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
579 /* the argument is optional and the next seems to be
580 * an option. We do not check this possible option
581 * but assume no argument */
582 arg->r_type = 0;
584 else {
585 set_opt_arg(arg, opts[i].flags, s2);
586 if( !argpos ) {
587 argc--; argv++; idx++; /* skip one */
591 else { /* does not take an argument */
592 if( argpos )
593 arg->r_type = -6; /* argument not expected */
594 else
595 arg->r_type = 0;
597 argc--; argv++; idx++; /* set to next one */
599 else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
600 int dash_kludge = 0;
601 i = 0;
602 if( !arg->internal.inarg ) {
603 arg->internal.inarg++;
604 if( arg->flags & (1<<5) ) {
605 for(i=0; opts[i].short_opt; i++ )
606 if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
607 dash_kludge=1;
608 break;
612 s += arg->internal.inarg;
614 if( !dash_kludge ) {
615 for(i=0; opts[i].short_opt; i++ )
616 if( opts[i].short_opt == *s )
617 break;
620 if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
621 show_help(opts, arg->flags);
623 arg->r_opt = opts[i].short_opt;
624 if( !opts[i].short_opt ) {
625 arg->r_opt = (opts[i].flags & 256)? -7:-2;
626 arg->internal.inarg++; /* point to the next arg */
627 arg->r.ret_str = s;
629 else if( (opts[i].flags & 7) ) {
630 if( s[1] && !dash_kludge ) {
631 s2 = s+1;
632 set_opt_arg(arg, opts[i].flags, s2);
634 else {
635 s2 = argv[1];
636 if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
637 arg->r_type = 0; /* because it is optional */
639 else if( !s2 ) {
640 arg->r_opt = -3; /* missing argument */
642 else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
643 /* the argument is optional and the next seems to be
644 * an option. We do not check this possible option
645 * but assume no argument */
646 arg->r_type = 0;
648 else {
649 set_opt_arg(arg, opts[i].flags, s2);
650 argc--; argv++; idx++; /* skip one */
653 s = "x"; /* so that !s[1] yields false */
655 else { /* does not take an argument */
656 arg->r_type = 0;
657 arg->internal.inarg++; /* point to the next arg */
659 if( !s[1] || dash_kludge ) { /* no more concatenated short options */
660 arg->internal.inarg = 0;
661 argc--; argv++; idx++;
664 else if( arg->flags & (1<<2) ) {
665 arg->r_opt = -1; /* not an option but a argument */
666 arg->r_type = 2;
667 arg->r.ret_str = s;
668 argc--; argv++; idx++; /* set to next one */
670 else {
671 arg->internal.stopped = 1; /* stop option processing */
672 goto next_one;
675 leave:
676 *arg->argc = argc;
677 *arg->argv = argv;
678 arg->internal.idx = idx;
679 return arg->r_opt;
684 static int
685 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
687 int base = (flags & 16)? 0 : 10;
689 switch( arg->r_type = (flags & 7) ) {
690 case 1: /* takes int argument */
691 arg->r.ret_int = (int)strtol(s,NULL,base);
692 return 0;
693 case 3: /* takes long argument */
694 arg->r.ret_long= strtol(s,NULL,base);
695 return 0;
696 case 4: /* takes ulong argument */
697 arg->r.ret_ulong= strtoul(s,NULL,base);
698 return 0;
699 case 2: /* takes string argument */
700 default:
701 arg->r.ret_str = s;
702 return 1;
707 static size_t
708 long_opt_strlen( ARGPARSE_OPTS *o )
710 size_t n = strlen (o->long_opt);
712 if ( o->description && *o->description == '|' )
714 const char *s;
715 #ifdef JNLIB_NEED_UTF8CONV
716 int is_utf8 = is_native_utf8 ();
717 #endif
719 s=o->description+1;
720 if ( *s != '=' )
721 n++;
722 /* For a (mostly) correct length calculation we exclude
723 continuation bytes (10xxxxxx) if we are on a native utf8
724 terminal. */
725 for (; *s && *s != '|'; s++ )
726 #ifdef JNLIB_NEED_UTF8CONV
727 if ( is_utf8 && (*s&0xc0) != 0x80 )
728 #endif
729 n++;
731 return n;
734 /****************
735 * Print formatted help. The description string has some special
736 * meanings:
737 * - A description string which is "@" suppresses help output for
738 * this option
739 * - a description,ine which starts with a '@' and is followed by
740 * any other characters is printed as is; this may be used for examples
741 * ans such.
742 * - A description which starts with a '|' outputs the string between this
743 * bar and the next one as arguments of the long option.
745 static void
746 show_help( ARGPARSE_OPTS *opts, unsigned flags )
748 const char *s;
750 show_version();
751 putchar('\n');
752 s = strusage(41);
753 puts(s);
754 if( opts[0].description ) { /* auto format the option description */
755 int i,j, indent;
756 /* get max. length of long options */
757 for(i=indent=0; opts[i].short_opt; i++ ) {
758 if( opts[i].long_opt )
759 if( !opts[i].description || *opts[i].description != '@' )
760 if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
761 indent = j;
763 /* example: " -v, --verbose Viele Sachen ausgeben" */
764 indent += 10;
765 if( *opts[0].description != '@' )
766 puts("Options:");
767 for(i=0; opts[i].short_opt; i++ ) {
768 s = _( opts[i].description );
769 if( s && *s== '@' && !s[1] ) /* hide this line */
770 continue;
771 if( s && *s == '@' ) { /* unindented comment only line */
772 for(s++; *s; s++ ) {
773 if( *s == '\n' ) {
774 if( s[1] )
775 putchar('\n');
777 else
778 putchar(*s);
780 putchar('\n');
781 continue;
784 j = 3;
785 if( opts[i].short_opt < 256 ) {
786 printf(" -%c", opts[i].short_opt );
787 if( !opts[i].long_opt ) {
788 if(s && *s == '|' ) {
789 putchar(' '); j++;
790 for(s++ ; *s && *s != '|'; s++, j++ )
791 putchar(*s);
792 if( *s )
793 s++;
797 else
798 fputs(" ", stdout);
799 if( opts[i].long_opt ) {
800 j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
801 opts[i].long_opt );
802 if(s && *s == '|' ) {
803 if( *++s != '=' ) {
804 putchar(' ');
805 j++;
807 for( ; *s && *s != '|'; s++, j++ )
808 putchar(*s);
809 if( *s )
810 s++;
812 fputs(" ", stdout);
813 j += 3;
815 for(;j < indent; j++ )
816 putchar(' ');
817 if( s ) {
818 if( *s && j > indent ) {
819 putchar('\n');
820 for(j=0;j < indent; j++ )
821 putchar(' ');
823 for(; *s; s++ ) {
824 if( *s == '\n' ) {
825 if( s[1] ) {
826 putchar('\n');
827 for(j=0;j < indent; j++ )
828 putchar(' ');
831 else
832 putchar(*s);
835 putchar('\n');
837 if( flags & 32 )
838 puts("\n(A single dash may be used instead of the double ones)");
840 if( (s=strusage(19)) ) { /* bug reports to ... */
841 char *s2;
843 putchar('\n');
844 s2 = strstr (s, "@EMAIL@");
845 if (s2)
847 if (s2-s)
848 fwrite (s, s2-s, 1, stdout);
849 fputs (PACKAGE_BUGREPORT, stdout);
850 s2 += 7;
851 if (*s2)
852 fputs (s2, stdout);
854 else
855 fputs(s, stdout);
857 fflush(stdout);
858 exit(0);
861 static void
862 show_version()
864 const char *s;
865 int i;
867 /* Version line. */
868 fputs (strusage (11), stdout);
869 if ((s=strusage (12)))
870 printf (" (%s)", s );
871 printf (" %s\n", strusage (13) );
872 /* Additional version lines. */
873 for (i=20; i < 30; i++)
874 if ((s=strusage (i)))
875 printf ("%s\n", s );
876 /* Copyright string. */
877 if( (s=strusage (14)) )
878 printf("%s\n", s );
879 /* Licence string. */
880 if( (s=strusage (10)) )
881 printf("%s\n", s );
882 /* Copying conditions. */
883 if ( (s=strusage(15)) )
884 fputs (s, stdout);
885 /* Thanks. */
886 if ((s=strusage(18)))
887 fputs (s, stdout);
888 /* Additional program info. */
889 for (i=30; i < 40; i++ )
890 if ( (s=strusage (i)) )
891 fputs (s, stdout);
892 fflush(stdout);
896 void
897 usage (int level)
899 const char *p;
901 if (!level)
903 fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14));
904 fflush (stderr);
906 else if (level == 1)
908 p = strusage (40);
909 fputs (p, stderr);
910 if (*p && p[strlen(p)] != '\n')
911 putc ('\n', stderr);
912 exit (2);
914 else if (level == 2)
916 puts (strusage(41));
917 exit (0);
921 /* Level
922 * 0: Print copyright string to stderr
923 * 1: Print a short usage hint to stderr and terminate
924 * 2: Print a long usage hint to stdout and terminate
925 * 10: Return license info string
926 * 11: Return the name of the program
927 * 12: Return optional name of package which includes this program.
928 * 13: version string
929 * 14: copyright string
930 * 15: Short copying conditions (with LFs)
931 * 16: Long copying conditions (with LFs)
932 * 17: Optional printable OS name
933 * 18: Optional thanks list (with LFs)
934 * 19: Bug report info
935 *20..29: Additional lib version strings.
936 *30..39: Additional program info (with LFs)
937 * 40: short usage note (with LF)
938 * 41: long usage note (with LF)
940 const char *
941 strusage( int level )
943 const char *p = strusage_handler? strusage_handler(level) : NULL;
945 if( p )
946 return p;
948 switch( level ) {
949 case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
950 "<http://gnu.org/licenses/gpl.html>");
951 break;
952 case 11: p = "foo"; break;
953 case 13: p = "0.0"; break;
954 case 14: p = "Copyright (C) 2008 Free Software Foundation, Inc."; break;
955 case 15: p =
956 "This is free software: you are free to change and redistribute it.\n"
957 "There is NO WARRANTY, to the extent permitted by law.\n";
958 break;
959 case 16: p =
960 "This is free software; you can redistribute it and/or modify\n"
961 "it under the terms of the GNU General Public License as published by\n"
962 "the Free Software Foundation; either version 3 of the License, or\n"
963 "(at your option) any later version.\n\n"
964 "It is distributed in the hope that it will be useful,\n"
965 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
966 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
967 "GNU General Public License for more details.\n\n"
968 "You should have received a copy of the GNU General Public License\n"
969 "along with this software. If not, see <http://www.gnu.org/licenses/>.\n";
970 break;
971 case 40: /* short and long usage */
972 case 41: p = ""; break;
975 return p;
978 void
979 set_strusage( const char *(*f)( int ) )
981 strusage_handler = f;
985 #ifdef TEST
986 static struct {
987 int verbose;
988 int debug;
989 char *outfile;
990 char *crf;
991 int myopt;
992 int echo;
993 int a_long_one;
994 }opt;
997 main(int argc, char **argv)
999 ARGPARSE_OPTS opts[] = {
1000 { 'v', "verbose", 0 , "Laut sein"},
1001 { 'e', "echo" , 0 , ("Zeile ausgeben, damit wir sehen, was wir ein"
1002 " gegeben haben")},
1003 { 'd', "debug", 0 , "Debug\nfalls mal etwas\nschief geht"},
1004 { 'o', "output", 2 },
1005 { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
1006 /* Note that on a non-utf8 terminal the ß might garble the output. */
1007 { 's', "street", 0, "|Straße|set the name of the street to Straße" },
1008 { 'm', "my-option", 1|8 },
1009 { 500, "a-long-option", 0 },
1010 {0} };
1011 ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
1012 int i;
1014 while( arg_parse ( &pargs, opts) ) {
1015 switch( pargs.r_opt ) {
1016 case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
1017 case 'v': opt.verbose++; break;
1018 case 'e': opt.echo++; break;
1019 case 'd': opt.debug++; break;
1020 case 'o': opt.outfile = pargs.r.ret_str; break;
1021 case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1022 case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1023 case 500: opt.a_long_one++; break;
1024 default : pargs.err = 1; break; /* force warning output */
1027 for(i=0; i < argc; i++ )
1028 printf("%3d -> (%s)\n", i, argv[i] );
1029 puts("Options:");
1030 if( opt.verbose )
1031 printf(" verbose=%d\n", opt.verbose );
1032 if( opt.debug )
1033 printf(" debug=%d\n", opt.debug );
1034 if( opt.outfile )
1035 printf(" outfile=`%s'\n", opt.outfile );
1036 if( opt.crf )
1037 printf(" crffile=`%s'\n", opt.crf );
1038 if( opt.myopt )
1039 printf(" myopt=%d\n", opt.myopt );
1040 if( opt.a_long_one )
1041 printf(" a-long-one=%d\n", opt.a_long_one );
1042 if( opt.echo )
1043 printf(" echo=%d\n", opt.echo );
1044 return 0;
1046 #endif
1048 /**** bottom of file ****/