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