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