bug fixes
[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( (j=strlen(opts[i].long_opt)) > indent && j < 35 )
542                     indent = j;
543         }
544         /* example: " -v, --verbose   Viele Sachen ausgeben" */
545         indent += 10;
546         puts("Options:");
547         for(i=0; opts[i].short_opt; i++ ) {
548             s = _( opts[i].description );
549             if( s && *s== '\r' ) /* hide this line */
550                 continue;
551             if( opts[i].short_opt < 256 )
552                 printf(" -%c", opts[i].short_opt );
553             else
554                 fputs("   ", stdout);
555             j = 3;
556             if( opts[i].long_opt )
557                 j += printf("%c --%s   ", opts[i].short_opt < 256?',':' ',
558                                           opts[i].long_opt );
559             for(;j < indent; j++ )
560                 putchar(' ');
561             if( s ) {
562                 for(; *s; s++ ) {
563                     if( *s == '\n' ) {
564                         if( s[1] ) {
565                             putchar('\n');
566                             for(j=0;j < indent; j++ )
567                                 putchar(' ');
568                         }
569                     }
570                     else
571                         putchar(*s);
572                 }
573             }
574             putchar('\n');
575         }
576         if( flags & 32 )
577             puts("\n(A single dash may be used instead of the double ones)");
578     }
579     if( *(s=strusage(26)) ) {  /* bug reports to ... */
580         putchar('\n');
581         fputs(s, stdout);
582     }
583     if( *(s=strusage(30)) ) {  /* special notes */
584         putchar('\n');
585         fputs(s, stdout);
586     }
587     fflush(stdout);
588     exit(0);
589 }
590
591 static void
592 show_version()
593 {
594     const char *s;
595     printf("%s version %s (%s", strusage(13), strusage(14), strusage(45) );
596     if( (s = strusage(24)) && *s ) {
597       #ifdef DEBUG
598         printf(", %s, dbg)\n", s);
599       #else
600         printf(", %s)\n", s);
601       #endif
602     }
603     else {
604       #ifdef DEBUG
605         printf(", dbg)\n");
606       #else
607         printf(")\n");
608       #endif
609     }
610     fflush(stdout);
611     exit(0);
612 }
613
614
615
616 void
617 usage( int level )
618 {
619     static int sentinel=0;
620
621     if( sentinel )
622         return;
623
624     sentinel++;
625     if( !level ) {
626         fputs( strusage(level), stderr ); putc( '\n', stderr );
627         fputs( strusage(31), stderr);
628       #if DEBUG
629         fprintf(stderr, "%s (%s - Debug)\n", strusage(32), strusage(24) );
630       #else
631         fprintf(stderr, "%s (%s)\n", strusage(32), strusage(24) );
632       #endif
633         fflush(stderr);
634     }
635     else if( level == 1 ) {
636         fputs(strusage(level),stderr);putc('\n',stderr);
637         exit(2);}
638     else if( level == 2 ) {
639         puts(strusage(level)); exit(0);}
640     sentinel--;
641 }
642
643
644 const char *
645 default_strusage( int level )
646 {
647     const char *p;
648     switch( level ) {
649       case  0:  p = strusage(10); break;
650       case  1:  p = strusage(11); break;
651       case  2:  p = strusage(12); break;
652       case 10:  p = "WkLib"
653                   #if DOS386 && __WATCOMC__
654                     " (DOS4G)"
655                   #elif DOS386
656                     " (DOSX)"
657                   #elif DOS16RM
658                     " (DOS16RM)"
659                   #elif M_I86VM
660                     " (VCM)"
661                   #elif UNIX || POSIX
662                     " (Posix)"
663                   #elif OS2
664                     " (OS/2)"
665                   #elif WINNT && __CYGWIN32__
666                     " (CygWin)"
667                   #elif WINNT
668                     " (WinNT)"
669                   #elif NETWARE
670                     " (Netware)"
671                   #elif VMS
672                     " (VMS)"
673                   #endif
674                     "; Copyright (c) 1997 by Werner Koch (dd9jn)" ; break;
675       case 11:  p = "usage: ?"; break;
676       case 16:
677       case 15:  p = "[Untitled]"; break;
678       case 23:  p = "[unknown]"; break;
679       case 24:  p = ""; break;
680       case 26:  p = ""; break;
681       case 12:  p =
682    "This is free software; you can redistribute it and/or modify\n"
683    "it under the terms of the GNU General Public License as published by\n"
684    "the Free Software Foundation; either version 2 of the License, or\n"
685    "(at your option) any later version.\n\n"
686    "It is distributed in the hope that it will be useful,\n"
687    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
688    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
689    "GNU General Public License for more details.\n\n"
690    "You should have received a copy of the GNU General Public License\n"
691    "along with this program; if not, write to the Free Software\n"
692    "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,"
693                                                                   " USA.\n" ;
694         break;
695       case 22:
696           #if MSDOS
697             #if USE_EMS
698               p = "MSDOS+EMS";
699             #else
700               p = "MSDOS";
701             #endif
702           #elif OS2
703             p = "OS/2";
704           #elif WINNT && __CYGWIN32__
705             p = "CygWin";
706           #elif WINNT
707             p = "WinNT";
708           #elif DOS386
709             p = "DOS386";
710           #elif EMX
711             p = "EMX";
712           #elif DOS16RM
713             p = "DOS16RM";
714           #elif NETWARE
715             p = "Netware";
716           #elif __linux__
717             p = "Linux";
718           #elif UNIX || M_UNIX || M_XENIX
719             p = "UNIX";
720           #elif VMS
721             p = "VMS";
722           #else
723             p = "UnknownOS";
724           #endif
725             break;
726       case 30: p = ""; break;
727       case 31: p =
728     "This program comes with ABSOLUTELY NO WARRANTY.\n"
729     "This is free software, and you are welcome to redistribute it\n"
730     "under certain conditions. See the file COPYING for details.\n";
731             break;
732       case 32: p = "["
733           #if MSDOS
734               "MSDOS Version"
735           #elif DOS386 && __ZTC__
736             "32-Bit MSDOS Version (Zortech's DOSX)"
737           #elif DOS386
738             "32-Bit MSDOS Version"
739           #elif OS20 && EMX
740             "OS/2 2.x EMX Version"
741           #elif OS20
742             "OS/2 2.x Version"
743           #elif OS2
744             "OS/2 1.x Version"
745           #elif WINNT && __CYGWIN32__
746             "Cygnus WinAPI Version"
747           #elif WINNT
748             "Windoze NT Version"
749           #elif EMX
750             "EMX Version"
751           #elif NETWARE
752             "NLM Version"
753           #elif DOS16RM
754             "DOS16RM Version"
755           #elif __linux__
756             "Linux Version"
757           #elif VMS
758             "OpenVMS Version"
759           #elif POSIX
760             "POSIX Version"
761           #elif M_UNIX || M_XENIX
762             "*IX Version"
763           #endif
764             "]";
765             break;
766       case 33: p =
767           #ifdef MULTI_THREADED
768             "mt"
769           #else
770             ""
771           #endif
772             ; break;
773       case 42:
774       case 43:
775       case 44:
776       case 45: p = ""; break;
777       default: p = "?";
778     }
779
780     return p;
781 }
782
783
784
785 #ifdef TEST
786 static struct {
787     int verbose;
788     int debug;
789     char *outfile;
790     char *crf;
791     int myopt;
792     int echo;
793     int a_long_one;
794 }opt;
795
796 int
797 main(int argc, char **argv)
798 {
799     ARGPARSE_OPTS opts[] = {
800     { 'v', "verbose",   0 , "Laut sein"},
801     { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
802     { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
803     { 'o', "output",    2   },
804     { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
805     { 'm', "my-option", 1|8 },
806     { 500, "a-long-option", 0 },
807     {0} };
808     ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
809     int i;
810
811     while( ArgParse( &pargs, opts) ) {
812         switch( pargs.r_opt ) {
813           case -1 : printf( "arg='%s'\n", pargs.r.ret_str); break;
814           case 'v': opt.verbose++; break;
815           case 'e': opt.echo++; break;
816           case 'd': opt.debug++; break;
817           case 'o': opt.outfile = pargs.r.ret_str; break;
818           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
819           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
820           case 500: opt.a_long_one++;  break;
821           default : pargs.err = 1; break; /* force warning output */
822         }
823     }
824     for(i=0; i < argc; i++ )
825         printf("%3d -> (%s)\n", i, argv[i] );
826     puts("Options:");
827     if( opt.verbose )
828         printf("  verbose=%d\n", opt.verbose );
829     if( opt.debug )
830         printf("  debug=%d\n", opt.debug );
831     if( opt.outfile )
832         printf("  outfile='%s'\n", opt.outfile );
833     if( opt.crf )
834         printf("  crffile='%s'\n", opt.crf );
835     if( opt.myopt )
836         printf("  myopt=%d\n", opt.myopt );
837     if( opt.a_long_one )
838         printf("  a-long-one=%d\n", opt.a_long_one );
839     if( opt.echo       )
840         printf("  echo=%d\n", opt.echo );
841     return 0;
842 }
843 #endif
844
845 /**** bottom of file ****/