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