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