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