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