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