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