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