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