~ubuntu-branches/ubuntu/karmic/gnupg2/karmic-updates

« back to all changes in this revision

Viewing changes to util/argparse.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Urlichs
  • Date: 2006-01-24 04:31:42 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060124043142-pbg192or6qxv3yk2
Tags: 1.9.20-1
* New Upstream version. Closes:#306890,#344530
  * Closes:#320490: gpg-protect-tool fails to decrypt PKCS-12 files 
* Depend on libopensc2-dev, not -1-. Closes:#348106

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* [argparse.c wk 17.06.97] Argument Parser for option handling
 
2
 *  Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
3
 *
 
4
 *  This file is part of GnuPG.
 
5
 *
 
6
 *  GnuPG is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  GnuPG is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
19
 *
 
20
 *
 
21
 * Note: This is an independent version of the one in WkLib
 
22
 */
 
23
 
 
24
#include <config.h>
 
25
#include <stdio.h>
 
26
#include <stdlib.h>
 
27
#include <ctype.h>
 
28
#include <string.h>
 
29
 
 
30
#include "util.h"
 
31
#include "i18n.h"
 
32
 
 
33
 
 
34
/*********************************
 
35
 * @Summary arg_parse
 
36
 *  #include <wk/lib.h>
 
37
 *
 
38
 *  typedef struct {
 
39
 *      char *argc;               pointer to argc (value subject to change)
 
40
 *      char ***argv;             pointer to argv (value subject to change)
 
41
 *      unsigned flags;           Global flags (DO NOT CHANGE)
 
42
 *      int err;                  print error about last option
 
43
 *                                1 = warning, 2 = abort
 
44
 *      int r_opt;                return option
 
45
 *      int r_type;               type of return value (0 = no argument found)
 
46
 *      union {
 
47
 *          int   ret_int;
 
48
 *          long  ret_long
 
49
 *          ulong ret_ulong;
 
50
 *          char *ret_str;
 
51
 *      } r;                      Return values
 
52
 *      struct {
 
53
 *          int idx;
 
54
 *          const char *last;
 
55
 *          void *aliases;
 
56
 *      } internal;               DO NOT CHANGE
 
57
 *  } ARGPARSE_ARGS;
 
58
 *
 
59
 *  typedef struct {
 
60
 *      int         short_opt;
 
61
 *      const char *long_opt;
 
62
 *      unsigned flags;
 
63
 *  } ARGPARSE_OPTS;
 
64
 *
 
65
 *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
 
66
 *
 
67
 * @Description
 
68
 *  This is my replacement for getopt(). See the example for a typical usage.
 
69
 *  Global flags are:
 
70
 *     Bit 0 : Do not remove options form argv
 
71
 *     Bit 1 : Do not stop at last option but return other args
 
72
 *             with r_opt set to -1.
 
73
 *     Bit 2 : Assume options and real args are mixed.
 
74
 *     Bit 3 : Do not use -- to stop option processing.
 
75
 *     Bit 4 : Do not skip the first arg.
 
76
 *     Bit 5 : allow usage of long option with only one dash
 
77
 *     Bit 6 : ignore --version and --help
 
78
 *     all other bits must be set to zero, this value is modified by the
 
79
 *     function, so assume this is write only.
 
80
 *  Local flags (for each option):
 
81
 *     Bit 2-0 : 0 = does not take an argument
 
82
 *               1 = takes int argument
 
83
 *               2 = takes string argument
 
84
 *               3 = takes long argument
 
85
 *               4 = takes ulong argument
 
86
 *     Bit 3 : argument is optional (r_type will the be set to 0)
 
87
 *     Bit 4 : allow 0x etc. prefixed values.
 
88
 *     Bit 7 : this is a command and not an option
 
89
 *  You stop the option processing by setting opts to NULL, the function will
 
90
 *  then return 0.
 
91
 * @Return Value
 
92
 *   Returns the args.r_opt or 0 if ready
 
93
 *   r_opt may be -2/-7 to indicate an unknown option/command.
 
94
 * @See Also
 
95
 *   ArgExpand
 
96
 * @Notes
 
97
 *  You do not need to process the options 'h', '--help' or '--version'
 
98
 *  because this function includes standard help processing; but if you
 
99
 *  specify '-h', '--help' or '--version' you have to do it yourself.
 
100
 *  The option '--' stops argument processing; if bit 1 is set the function
 
101
 *  continues to return normal arguments.
 
102
 *  To process float args or unsigned args you must use a string args and do
 
103
 *  the conversion yourself.
 
104
 * @Example
 
105
 *
 
106
 *     ARGPARSE_OPTS opts[] = {
 
107
 *     { 'v', "verbose",   0 },
 
108
 *     { 'd', "debug",     0 },
 
109
 *     { 'o', "output",    2 },
 
110
 *     { 'c', "cross-ref", 2|8 },
 
111
 *     { 'm', "my-option", 1|8 },
 
112
 *     { 500, "have-no-short-option-for-this-long-option", 0 },
 
113
 *     {0} };
 
114
 *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
 
115
 *
 
116
 *     while( ArgParse( &pargs, &opts) ) {
 
117
 *         switch( pargs.r_opt ) {
 
118
 *           case 'v': opt.verbose++; break;
 
119
 *           case 'd': opt.debug++; break;
 
120
 *           case 'o': opt.outfile = pargs.r.ret_str; break;
 
121
 *           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
 
122
 *           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
 
123
 *           case 500: opt.a_long_one++;  break
 
124
 *           default : pargs.err = 1; break; -- force warning output --
 
125
 *         }
 
126
 *     }
 
127
 *     if( argc > 1 )
 
128
 *         log_fatal( "Too many args");
 
129
 *
 
130
 */
 
131
 
 
132
typedef struct alias_def_s *ALIAS_DEF;
 
133
struct alias_def_s {
 
134
    ALIAS_DEF next;
 
135
    char *name;   /* malloced buffer with name, \0, value */
 
136
    const char *value; /* ptr into name */
 
137
};
 
138
 
 
139
static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
 
140
static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
 
141
static void show_version(void);
 
142
 
 
143
static void
 
144
initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
 
145
{
 
146
    if( !(arg->flags & (1<<15)) ) { /* initialize this instance */
 
147
        arg->internal.idx = 0;
 
148
        arg->internal.last = NULL;
 
149
        arg->internal.inarg = 0;
 
150
        arg->internal.stopped = 0;
 
151
        arg->internal.aliases = NULL;
 
152
        arg->internal.cur_alias = NULL;
 
153
        arg->err = 0;
 
154
        arg->flags |= 1<<15; /* mark initialized */
 
155
        if( *arg->argc < 0 )
 
156
            log_bug("Invalid argument for ArgParse\n");
 
157
    }
 
158
 
 
159
 
 
160
    if( arg->err ) { /* last option was erroneous */
 
161
        const char *s;
 
162
 
 
163
        if( filename ) {
 
164
            if( arg->r_opt == -6 )
 
165
                s = "%s:%u: argument not expected\n";
 
166
            else if( arg->r_opt == -5 )
 
167
                s = "%s:%u: read error\n";
 
168
            else if( arg->r_opt == -4 )
 
169
                s = "%s:%u: keyword too long\n";
 
170
            else if( arg->r_opt == -3 )
 
171
                s = "%s:%u: missing argument\n";
 
172
            else if( arg->r_opt == -7 )
 
173
                s = "%s:%u: invalid command\n";
 
174
            else if( arg->r_opt == -10 )
 
175
                s = "%s:%u: invalid alias definition\n";
 
176
            else
 
177
                s = "%s:%u: invalid option\n";
 
178
            log_error(s, filename, *lineno );
 
179
        }
 
180
        else {
 
181
            if( arg->r_opt == -3 )
 
182
                s = "Missing argument for option \"%.50s\"\n";
 
183
            else if( arg->r_opt == -6 )
 
184
                s = "Option \"%.50s\" does not expect an argument\n";
 
185
            else if( arg->r_opt == -7 )
 
186
                s = "Invalid command \"%.50s\"\n";
 
187
            else if( arg->r_opt == -8 )
 
188
                s = "Option \"%.50s\" is ambiguous\n";
 
189
            else if( arg->r_opt == -9 )
 
190
                s = "Command \"%.50s\" is ambiguous\n";
 
191
            else
 
192
                s = "Invalid option \"%.50s\"\n";
 
193
            log_error(s, arg->internal.last? arg->internal.last:"[??]" );
 
194
        }
 
195
        if( arg->err != 1 || arg->r_opt == -5 )
 
196
            exit(2);
 
197
        arg->err = 0;
 
198
    }
 
199
 
 
200
    /* clearout the return value union */
 
201
    arg->r.ret_str = NULL;
 
202
    arg->r.ret_long= 0;
 
203
}
 
204
 
 
205
 
 
206
static void
 
207
store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
 
208
{
 
209
    /* TODO: replace this dummy function with a rea one
 
210
     * and fix the probelms IRIX has with (ALIAS_DEV)arg..
 
211
     * used as lvalue
 
212
     */
 
213
#if 0
 
214
    ALIAS_DEF a = m_alloc( sizeof *a );
 
215
    a->name = name;
 
216
    a->value = value;
 
217
    a->next = (ALIAS_DEF)arg->internal.aliases;
 
218
    (ALIAS_DEF)arg->internal.aliases = a;
 
219
#endif
 
220
}
 
221
 
 
222
/****************
 
223
 * Get options from a file.
 
224
 * Lines starting with '#' are comment lines.
 
225
 * Syntax is simply a keyword and the argument.
 
226
 * Valid keywords are all keywords from the long_opt list without
 
227
 * the leading dashes. The special keywords "help", "warranty" and "version"
 
228
 * are not valid here.
 
229
 * The special keyword "alias" may be used to store alias definitions,
 
230
 * which are later expanded like long options.
 
231
 * Caller must free returned strings.
 
232
 * If called with FP set to NULL command line args are parse instead.
 
233
 *
 
234
 * Q: Should we allow the syntax
 
235
 *     keyword = value
 
236
 *    and accept for boolean options a value of 1/0, yes/no or true/false?
 
237
 * Note: Abbreviation of options is here not allowed.
 
238
 */
 
239
int
 
240
optfile_parse( FILE *fp, const char *filename, unsigned *lineno,
 
241
               ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 
242
{
 
243
    int state, i, c;
 
244
    int idx=0;
 
245
    char keyword[100];
 
246
    char *buffer = NULL;
 
247
    size_t buflen = 0;
 
248
    int inverse=0;
 
249
    int in_alias=0;
 
250
 
 
251
    if( !fp ) /* same as arg_parse() in this case */
 
252
        return arg_parse( arg, opts );
 
253
 
 
254
    initialize( arg, filename, lineno );
 
255
 
 
256
    /* find the next keyword */
 
257
    state = i = 0;
 
258
    for(;;) {
 
259
        c=getc(fp);
 
260
        if( c == '\n' || c== EOF ) {
 
261
            if( c != EOF )
 
262
                ++*lineno;
 
263
            if( state == -1 )
 
264
                break;
 
265
            else if( state == 2 ) {
 
266
                keyword[i] = 0;
 
267
                for(i=0; opts[i].short_opt; i++ )
 
268
                    if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
 
269
                        break;
 
270
                idx = i;
 
271
                arg->r_opt = opts[idx].short_opt;
 
272
                if( inverse ) /* this does not have an effect, hmmm */
 
273
                    arg->r_opt = -arg->r_opt;
 
274
                if( !opts[idx].short_opt )   /* unknown command/option */
 
275
                    arg->r_opt = (opts[idx].flags & 256)? -7:-2;
 
276
                else if( !(opts[idx].flags & 7) ) /* does not take an arg */
 
277
                    arg->r_type = 0;           /* okay */
 
278
                else if( (opts[idx].flags & 8) )  /* argument is optional */
 
279
                    arg->r_type = 0;           /* okay */
 
280
                else                           /* required argument */
 
281
                    arg->r_opt = -3;           /* error */
 
282
                break;
 
283
            }
 
284
            else if( state == 3 ) {            /* no argument found */
 
285
                if( in_alias )
 
286
                    arg->r_opt = -3;           /* error */
 
287
                else if( !(opts[idx].flags & 7) ) /* does not take an arg */
 
288
                    arg->r_type = 0;           /* okay */
 
289
                else if( (opts[idx].flags & 8) )  /* no optional argument */
 
290
                    arg->r_type = 0;           /* okay */
 
291
                else                           /* no required argument */
 
292
                    arg->r_opt = -3;           /* error */
 
293
                break;
 
294
            }
 
295
            else if( state == 4 ) {     /* have an argument */
 
296
                if( in_alias ) {
 
297
                    if( !buffer )
 
298
                        arg->r_opt = -6;
 
299
                    else {
 
300
                        char *p;
 
301
 
 
302
                        buffer[i] = 0;
 
303
                        p = strpbrk( buffer, " \t" );
 
304
                        if( p ) {
 
305
                            *p++ = 0;
 
306
                            trim_spaces( p );
 
307
                        }
 
308
                        if( !p || !*p ) {
 
309
                            m_free( buffer );
 
310
                            arg->r_opt = -10;
 
311
                        }
 
312
                        else {
 
313
                            store_alias( arg, buffer, p );
 
314
                        }
 
315
                    }
 
316
                }
 
317
                else if( !(opts[idx].flags & 7) )  /* does not take an arg */
 
318
                    arg->r_opt = -6;        /* error */
 
319
                else {
 
320
                    char *p;
 
321
                    if( !buffer ) {
 
322
                        keyword[i] = 0;
 
323
                        buffer = m_strdup(keyword);
 
324
                    }
 
325
                    else
 
326
                        buffer[i] = 0;
 
327
 
 
328
                    trim_spaces( buffer );
 
329
                    p = buffer;
 
330
                    /* remove quotes if they totally enclose the
 
331
                       string, and do not occur within the string */
 
332
                    if( *p == '"' && p[strlen(p)-1]=='"') {
 
333
                        char *p2=p;
 
334
 
 
335
                        while(*(++p2))
 
336
                          if(*p2=='"')
 
337
                            break;
 
338
 
 
339
                        if(*p2=='"' && *(p2+1)=='\0') {
 
340
                          p[strlen(p)-1] = 0;
 
341
                          p++;
 
342
                        }
 
343
                    }
 
344
                    if( !set_opt_arg(arg, opts[idx].flags, p) )
 
345
                        m_free(buffer);
 
346
                }
 
347
                break;
 
348
            }
 
349
            else if( c == EOF ) {
 
350
                if( ferror(fp) )
 
351
                    arg->r_opt = -5;   /* read error */
 
352
                else
 
353
                    arg->r_opt = 0;    /* eof */
 
354
                break;
 
355
            }
 
356
            state = 0;
 
357
            i = 0;
 
358
        }
 
359
        else if( state == -1 )
 
360
            ; /* skip */
 
361
        else if( !state && isspace(c) )
 
362
            ; /* skip leading white space */
 
363
        else if( !state && c == '#' )
 
364
            state = 1;  /* start of a comment */
 
365
        else if( state == 1 )
 
366
            ; /* skip comments */
 
367
        else if( state == 2 && isspace(c) ) {
 
368
            keyword[i] = 0;
 
369
            for(i=0; opts[i].short_opt; i++ )
 
370
                if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
 
371
                    break;
 
372
            idx = i;
 
373
            arg->r_opt = opts[idx].short_opt;
 
374
            if( !opts[idx].short_opt ) {
 
375
                if( !strcmp( keyword, "alias" ) ) {
 
376
                    in_alias = 1;
 
377
                    state = 3;
 
378
                }
 
379
                else {
 
380
                    arg->r_opt = (opts[idx].flags & 256)? -7:-2;
 
381
                    state = -1;        /* skip rest of line and leave */
 
382
                }
 
383
            }
 
384
            else
 
385
                state = 3;
 
386
        }
 
387
        else if( state == 3 ) { /* skip leading spaces of the argument */
 
388
            if( !isspace(c) ) {
 
389
                i = 0;
 
390
                keyword[i++] = c;
 
391
                state = 4;
 
392
            }
 
393
        }
 
394
        else if( state == 4 ) { /* collect the argument */
 
395
            if( buffer ) {
 
396
                if( i < buflen-1 )
 
397
                    buffer[i++] = c;
 
398
                else {
 
399
                    buflen += 50;
 
400
                    buffer = m_realloc(buffer, buflen);
 
401
                    buffer[i++] = c;
 
402
                }
 
403
            }
 
404
            else if( i < DIM(keyword)-1 )
 
405
                keyword[i++] = c;
 
406
            else {
 
407
                buflen = DIM(keyword)+50;
 
408
                buffer = m_alloc(buflen);
 
409
                memcpy(buffer, keyword, i);
 
410
                buffer[i++] = c;
 
411
            }
 
412
        }
 
413
        else if( i >= DIM(keyword)-1 ) {
 
414
            arg->r_opt = -4;   /* keyword to long */
 
415
            state = -1;        /* skip rest of line and leave */
 
416
        }
 
417
        else {
 
418
            keyword[i++] = c;
 
419
            state = 2;
 
420
        }
 
421
    }
 
422
 
 
423
    return arg->r_opt;
 
424
}
 
425
 
 
426
 
 
427
 
 
428
static int
 
429
find_long_option( ARGPARSE_ARGS *arg,
 
430
                  ARGPARSE_OPTS *opts, const char *keyword )
 
431
{
 
432
    int i;
 
433
    size_t n;
 
434
 
 
435
    /* Would be better if we can do a binary search, but it is not
 
436
       possible to reorder our option table because we would mess
 
437
       up our help strings - What we can do is: Build a nice option
 
438
       lookup table wehn this function is first invoked */
 
439
    if( !*keyword )
 
440
        return -1;
 
441
    for(i=0; opts[i].short_opt; i++ )
 
442
        if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
 
443
            return i;
 
444
  #if 0
 
445
    {
 
446
        ALIAS_DEF a;
 
447
        /* see whether it is an alias */
 
448
        for( a = args->internal.aliases; a; a = a->next ) {
 
449
            if( !strcmp( a->name, keyword) ) {
 
450
                /* todo: must parse the alias here */
 
451
                args->internal.cur_alias = a;
 
452
                return -3; /* alias available */
 
453
            }
 
454
        }
 
455
    }
 
456
  #endif
 
457
    /* not found, see whether it is an abbreviation */
 
458
    /* aliases may not be abbreviated */
 
459
    n = strlen( keyword );
 
460
    for(i=0; opts[i].short_opt; i++ ) {
 
461
        if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
 
462
            int j;
 
463
            for(j=i+1; opts[j].short_opt; j++ ) {
 
464
                if( opts[j].long_opt
 
465
                    && !strncmp( opts[j].long_opt, keyword, n ) )
 
466
                    return -2;  /* abbreviation is ambiguous */
 
467
            }
 
468
            return i;
 
469
        }
 
470
    }
 
471
    return -1;
 
472
}
 
473
 
 
474
int
 
475
arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
 
476
{
 
477
    int idx;
 
478
    int argc;
 
479
    char **argv;
 
480
    char *s, *s2;
 
481
    int i;
 
482
 
 
483
    initialize( arg, NULL, NULL );
 
484
    argc = *arg->argc;
 
485
    argv = *arg->argv;
 
486
    idx = arg->internal.idx;
 
487
 
 
488
    if( !idx && argc && !(arg->flags & (1<<4)) ) { /* skip the first entry */
 
489
        argc--; argv++; idx++;
 
490
    }
 
491
 
 
492
  next_one:
 
493
    if( !argc ) { /* no more args */
 
494
        arg->r_opt = 0;
 
495
        goto leave; /* ready */
 
496
    }
 
497
 
 
498
    s = *argv;
 
499
    arg->internal.last = s;
 
500
 
 
501
    if( arg->internal.stopped && (arg->flags & (1<<1)) ) {
 
502
        arg->r_opt = -1;  /* not an option but a argument */
 
503
        arg->r_type = 2;
 
504
        arg->r.ret_str = s;
 
505
        argc--; argv++; idx++; /* set to next one */
 
506
    }
 
507
    else if( arg->internal.stopped ) { /* ready */
 
508
        arg->r_opt = 0;
 
509
        goto leave;
 
510
    }
 
511
    else if( *s == '-' && s[1] == '-' ) { /* long option */
 
512
        char *argpos;
 
513
 
 
514
        arg->internal.inarg = 0;
 
515
        if( !s[2] && !(arg->flags & (1<<3)) ) { /* stop option processing */
 
516
            arg->internal.stopped = 1;
 
517
            argc--; argv++; idx++;
 
518
            goto next_one;
 
519
        }
 
520
 
 
521
        argpos = strchr( s+2, '=' );
 
522
        if( argpos )
 
523
            *argpos = 0;
 
524
        i = find_long_option( arg, opts, s+2 );
 
525
        if( argpos )
 
526
            *argpos = '=';
 
527
 
 
528
        if( i < 0 && !strcmp( "help", s+2) ) {
 
529
            if( !(arg->flags & (1<<6)) ) {
 
530
                show_help(opts, arg->flags);
 
531
            }
 
532
        }
 
533
        else if( i < 0 && !strcmp( "version", s+2) ) {
 
534
            if( !(arg->flags & (1<<6)) ) {
 
535
                show_version();
 
536
                exit(0);
 
537
            }
 
538
        }
 
539
        else if( i < 0 && !strcmp( "warranty", s+2) ) {
 
540
            puts( strusage(16) );
 
541
            exit(0);
 
542
        }
 
543
        else if( i < 0 && !strcmp( "dump-options", s+2) ) {
 
544
            for(i=0; opts[i].short_opt; i++ ) {
 
545
                if( opts[i].long_opt )
 
546
                    printf( "--%s\n", opts[i].long_opt );
 
547
            }
 
548
            fputs("--dump-options\n--help\n--version\n--warranty\n", stdout );
 
549
            exit(0);
 
550
        }
 
551
 
 
552
        if( i == -2 ) /* ambiguous option */
 
553
            arg->r_opt = -8;
 
554
        else if( i == -1 ) {
 
555
            arg->r_opt = -2;
 
556
            arg->r.ret_str = s+2;
 
557
        }
 
558
        else
 
559
            arg->r_opt = opts[i].short_opt;
 
560
        if( i < 0 )
 
561
            ;
 
562
        else if( (opts[i].flags & 7) ) {
 
563
            if( argpos ) {
 
564
                s2 = argpos+1;
 
565
                if( !*s2 )
 
566
                    s2 = NULL;
 
567
            }
 
568
            else
 
569
                s2 = argv[1];
 
570
            if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
 
571
                arg->r_type = 0;               /* because it is optional */
 
572
            }
 
573
            else if( !s2 ) {
 
574
                arg->r_opt = -3; /* missing argument */
 
575
            }
 
576
            else if( !argpos && *s2 == '-' && (opts[i].flags & 8) ) {
 
577
                /* the argument is optional and the next seems to be
 
578
                 * an option. We do not check this possible option
 
579
                 * but assume no argument */
 
580
                arg->r_type = 0;
 
581
            }
 
582
            else {
 
583
                set_opt_arg(arg, opts[i].flags, s2);
 
584
                if( !argpos ) {
 
585
                    argc--; argv++; idx++; /* skip one */
 
586
                }
 
587
            }
 
588
        }
 
589
        else { /* does not take an argument */
 
590
            if( argpos )
 
591
                arg->r_type = -6; /* argument not expected */
 
592
            else
 
593
                arg->r_type = 0;
 
594
        }
 
595
        argc--; argv++; idx++; /* set to next one */
 
596
    }
 
597
    else if( (*s == '-' && s[1]) || arg->internal.inarg ) { /* short option */
 
598
        int dash_kludge = 0;
 
599
        i = 0;
 
600
        if( !arg->internal.inarg ) {
 
601
            arg->internal.inarg++;
 
602
            if( arg->flags & (1<<5) ) {
 
603
                for(i=0; opts[i].short_opt; i++ )
 
604
                    if( opts[i].long_opt && !strcmp( opts[i].long_opt, s+1)) {
 
605
                        dash_kludge=1;
 
606
                        break;
 
607
                    }
 
608
            }
 
609
        }
 
610
        s += arg->internal.inarg;
 
611
 
 
612
        if( !dash_kludge ) {
 
613
            for(i=0; opts[i].short_opt; i++ )
 
614
                if( opts[i].short_opt == *s )
 
615
                    break;
 
616
        }
 
617
 
 
618
        if( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) {
 
619
            if( !(arg->flags & (1<<6)) ) {
 
620
                show_help(opts, arg->flags);
 
621
            }
 
622
        }
 
623
 
 
624
        arg->r_opt = opts[i].short_opt;
 
625
        if( !opts[i].short_opt ) {
 
626
            arg->r_opt = (opts[i].flags & 256)? -7:-2;
 
627
            arg->internal.inarg++; /* point to the next arg */
 
628
            arg->r.ret_str = s;
 
629
        }
 
630
        else if( (opts[i].flags & 7) ) {
 
631
            if( s[1] && !dash_kludge ) {
 
632
                s2 = s+1;
 
633
                set_opt_arg(arg, opts[i].flags, s2);
 
634
            }
 
635
            else {
 
636
                s2 = argv[1];
 
637
                if( !s2 && (opts[i].flags & 8) ) { /* no argument but it is okay*/
 
638
                    arg->r_type = 0;               /* because it is optional */
 
639
                }
 
640
                else if( !s2 ) {
 
641
                    arg->r_opt = -3; /* missing argument */
 
642
                }
 
643
                else if( *s2 == '-' && s2[1] && (opts[i].flags & 8) ) {
 
644
                    /* the argument is optional and the next seems to be
 
645
                     * an option. We do not check this possible option
 
646
                     * but assume no argument */
 
647
                    arg->r_type = 0;
 
648
                }
 
649
                else {
 
650
                    set_opt_arg(arg, opts[i].flags, s2);
 
651
                    argc--; argv++; idx++; /* skip one */
 
652
                }
 
653
            }
 
654
            s = "x"; /* so that !s[1] yields false */
 
655
        }
 
656
        else { /* does not take an argument */
 
657
            arg->r_type = 0;
 
658
            arg->internal.inarg++; /* point to the next arg */
 
659
        }
 
660
        if( !s[1] || dash_kludge ) { /* no more concatenated short options */
 
661
            arg->internal.inarg = 0;
 
662
            argc--; argv++; idx++;
 
663
        }
 
664
    }
 
665
    else if( arg->flags & (1<<2) ) {
 
666
        arg->r_opt = -1;  /* not an option but a argument */
 
667
        arg->r_type = 2;
 
668
        arg->r.ret_str = s;
 
669
        argc--; argv++; idx++; /* set to next one */
 
670
    }
 
671
    else {
 
672
        arg->internal.stopped = 1; /* stop option processing */
 
673
        goto next_one;
 
674
    }
 
675
 
 
676
  leave:
 
677
    *arg->argc = argc;
 
678
    *arg->argv = argv;
 
679
    arg->internal.idx = idx;
 
680
    return arg->r_opt;
 
681
}
 
682
 
 
683
 
 
684
 
 
685
static int
 
686
set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
 
687
{
 
688
    int base = (flags & 16)? 0 : 10;
 
689
 
 
690
    switch( arg->r_type = (flags & 7) ) {
 
691
      case 1: /* takes int argument */
 
692
        arg->r.ret_int = (int)strtol(s,NULL,base);
 
693
        return 0;
 
694
      case 3: /* takes long argument   */
 
695
        arg->r.ret_long= strtol(s,NULL,base);
 
696
        return 0;
 
697
      case 4: /* takes ulong argument  */
 
698
        arg->r.ret_ulong= strtoul(s,NULL,base);
 
699
        return 0;
 
700
      case 2: /* takes string argument */
 
701
      default:
 
702
        arg->r.ret_str = s;
 
703
        return 1;
 
704
    }
 
705
}
 
706
 
 
707
 
 
708
static size_t
 
709
long_opt_strlen( ARGPARSE_OPTS *o )
 
710
{
 
711
    size_t n = strlen(o->long_opt);
 
712
 
 
713
    if( o->description && *o->description == '|' ) {
 
714
        const char *s;
 
715
 
 
716
        s=o->description+1;
 
717
        if( *s != '=' )
 
718
            n++;
 
719
        for(; *s && *s != '|'; s++ )
 
720
            n++;
 
721
    }
 
722
    return n;
 
723
}
 
724
 
 
725
/****************
 
726
 * Print formatted help. The description string has some special
 
727
 * meanings:
 
728
 *  - A description string which is "@" suppresses help output for
 
729
 *    this option
 
730
 *  - a description,ine which starts with a '@' and is followed by
 
731
 *    any other characters is printed as is; this may be used for examples
 
732
 *    ans such.
 
733
 *  - A description which starts with a '|' outputs the string between this
 
734
 *    bar and the next one as arguments of the long option.
 
735
 */
 
736
static void
 
737
show_help( ARGPARSE_OPTS *opts, unsigned flags )
 
738
{
 
739
    const char *s;
 
740
 
 
741
    show_version();
 
742
    putchar('\n');
 
743
    s = strusage(41);
 
744
    puts(s);
 
745
    if( opts[0].description ) { /* auto format the option description */
 
746
        int i,j, indent;
 
747
        /* get max. length of long options */
 
748
        for(i=indent=0; opts[i].short_opt; i++ ) {
 
749
            if( opts[i].long_opt )
 
750
                if( !opts[i].description || *opts[i].description != '@' )
 
751
                    if( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
 
752
                         indent = j;
 
753
        }
 
754
        /* example: " -v, --verbose   Viele Sachen ausgeben" */
 
755
        indent += 10;
 
756
        if( *opts[0].description != '@' )
 
757
            puts("Options:");
 
758
        for(i=0; opts[i].short_opt; i++ ) {
 
759
            s = _( opts[i].description );
 
760
            if( s && *s== '@' && !s[1] ) /* hide this line */
 
761
                continue;
 
762
            if( s && *s == '@' ) { /* unindented comment only line */
 
763
                for(s++; *s; s++ ) {
 
764
                    if( *s == '\n' ) {
 
765
                        if( s[1] )
 
766
                            putchar('\n');
 
767
                    }
 
768
                    else
 
769
                        putchar(*s);
 
770
                }
 
771
                putchar('\n');
 
772
                continue;
 
773
            }
 
774
 
 
775
            j = 3;
 
776
            if( opts[i].short_opt < 256 ) {
 
777
                printf(" -%c", opts[i].short_opt );
 
778
                if( !opts[i].long_opt ) {
 
779
                    if(s && *s == '|' ) {
 
780
                        putchar(' '); j++;
 
781
                        for(s++ ; *s && *s != '|'; s++, j++ )
 
782
                            putchar(*s);
 
783
                        if( *s )
 
784
                            s++;
 
785
                    }
 
786
                }
 
787
            }
 
788
            else
 
789
                fputs("   ", stdout);
 
790
            if( opts[i].long_opt ) {
 
791
                j += printf("%c --%s", opts[i].short_opt < 256?',':' ',
 
792
                                       opts[i].long_opt );
 
793
                if(s && *s == '|' ) {
 
794
                    if( *++s != '=' ) {
 
795
                        putchar(' ');
 
796
                        j++;
 
797
                    }
 
798
                    for( ; *s && *s != '|'; s++, j++ )
 
799
                        putchar(*s);
 
800
                    if( *s )
 
801
                        s++;
 
802
                }
 
803
                fputs("   ", stdout);
 
804
                j += 3;
 
805
            }
 
806
            for(;j < indent; j++ )
 
807
                putchar(' ');
 
808
            if( s ) {
 
809
                if( *s && j > indent ) {
 
810
                    putchar('\n');
 
811
                    for(j=0;j < indent; j++ )
 
812
                        putchar(' ');
 
813
                }
 
814
                for(; *s; s++ ) {
 
815
                    if( *s == '\n' ) {
 
816
                        if( s[1] ) {
 
817
                            putchar('\n');
 
818
                            for(j=0;j < indent; j++ )
 
819
                                putchar(' ');
 
820
                        }
 
821
                    }
 
822
                    else
 
823
                        putchar(*s);
 
824
                }
 
825
            }
 
826
            putchar('\n');
 
827
        }
 
828
        if( flags & 32 )
 
829
            puts("\n(A single dash may be used instead of the double ones)");
 
830
    }
 
831
    if( (s=strusage(19)) ) {  /* bug reports to ... */
 
832
        putchar('\n');
 
833
        fputs(s, stdout);
 
834
    }
 
835
    fflush(stdout);
 
836
    exit(0);
 
837
}
 
838
 
 
839
static void
 
840
show_version()
 
841
{
 
842
    const char *s;
 
843
    int i;
 
844
    /* version line */
 
845
    fputs(strusage(11), stdout);
 
846
    if( (s=strusage(12)) )
 
847
        printf(" (%s)", s );
 
848
    printf(" %s\n", strusage(13) );
 
849
    /* additional version lines */
 
850
    for(i=20; i < 30; i++ )
 
851
        if( (s=strusage(i)) )
 
852
            printf("%s\n", s );
 
853
    /* copyright string */
 
854
    if( (s=strusage(14)) )
 
855
        printf("%s\n", s );
 
856
    /* copying conditions */
 
857
    if( (s=strusage(15)) )
 
858
        fputs(s, stdout);
 
859
    /* thanks */
 
860
    if( (s=strusage(18)) )
 
861
        fputs(s, stdout);
 
862
    /* additional program info */
 
863
    for(i=30; i < 40; i++ )
 
864
        if( (s=strusage(i)) )
 
865
            fputs( (const byte*)s, stdout);
 
866
    fflush(stdout);
 
867
}
 
868
 
 
869
 
 
870
void
 
871
usage( int level )
 
872
{
 
873
    if( !level ) {
 
874
        fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13),
 
875
                                                     strusage(14) );
 
876
        fflush(stderr);
 
877
    }
 
878
    else if( level == 1 ) {
 
879
        fputs(strusage(40),stderr);
 
880
        exit(2);
 
881
    }
 
882
    else if( level == 2 ) {
 
883
        puts(strusage(41));
 
884
        exit(0);
 
885
    }
 
886
}
 
887
 
 
888
/* Level
 
889
 *     0: Copyright String auf stderr ausgeben
 
890
 *     1: Kurzusage auf stderr ausgeben und beenden
 
891
 *     2: Langusage auf stdout ausgeben und beenden
 
892
 *    11: name of program
 
893
 *    12: optional name of package which includes this program.
 
894
 *    13: version  string
 
895
 *    14: copyright string
 
896
 *    15: Short copying conditions (with LFs)
 
897
 *    16: Long copying conditions (with LFs)
 
898
 *    17: Optional printable OS name
 
899
 *    18: Optional thanks list   (with LFs)
 
900
 *    19: Bug report info
 
901
 *20..29: Additional lib version strings.
 
902
 *30..39: Additional program info (with LFs)
 
903
 *    40: short usage note (with LF)
 
904
 *    41: long usage note (with LF)
 
905
 */
 
906
const char *
 
907
default_strusage( int level )
 
908
{
 
909
    const char *p = NULL;
 
910
    switch( level ) {
 
911
      case 11: p = "foo"; break;
 
912
      case 13: p = "0.0"; break;
 
913
      case 14: p = "Copyright (C) 2002 Free Software Foundation, Inc."; break;
 
914
      case 15: p =
 
915
"This program comes with ABSOLUTELY NO WARRANTY.\n"
 
916
"This is free software, and you are welcome to redistribute it\n"
 
917
"under certain conditions. See the file COPYING for details.\n"; break;
 
918
      case 16:  p =
 
919
"This is free software; you can redistribute it and/or modify\n"
 
920
"it under the terms of the GNU General Public License as published by\n"
 
921
"the Free Software Foundation; either version 2 of the License, or\n"
 
922
"(at your option) any later version.\n\n"
 
923
"It is distributed in the hope that it will be useful,\n"
 
924
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
 
925
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
 
926
"GNU General Public License for more details.\n\n"
 
927
"You should have received a copy of the GNU General Public License\n"
 
928
"along with this program; if not, write to the Free Software\n"
 
929
"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n";
 
930
        break;
 
931
      case 40: /* short and long usage */
 
932
      case 41: p = ""; break;
 
933
    }
 
934
 
 
935
    return p;
 
936
}
 
937
 
 
938
 
 
939
 
 
940
#ifdef TEST
 
941
static struct {
 
942
    int verbose;
 
943
    int debug;
 
944
    char *outfile;
 
945
    char *crf;
 
946
    int myopt;
 
947
    int echo;
 
948
    int a_long_one;
 
949
}opt;
 
950
 
 
951
int
 
952
main(int argc, char **argv)
 
953
{
 
954
    ARGPARSE_OPTS opts[] = {
 
955
    { 'v', "verbose",   0 , "Laut sein"},
 
956
    { 'e', "echo"   ,   0 , "Zeile ausgeben, damit wir sehen, was wir einegegeben haben"},
 
957
    { 'd', "debug",     0 , "Debug\nfalls mal etasws\nSchief geht"},
 
958
    { 'o', "output",    2   },
 
959
    { 'c', "cross-ref", 2|8, "cross-reference erzeugen\n" },
 
960
    { 'm', "my-option", 1|8 },
 
961
    { 500, "a-long-option", 0 },
 
962
    {0} };
 
963
    ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
 
964
    int i;
 
965
 
 
966
    while( ArgParse( &pargs, opts) ) {
 
967
        switch( pargs.r_opt ) {
 
968
          case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
 
969
          case 'v': opt.verbose++; break;
 
970
          case 'e': opt.echo++; break;
 
971
          case 'd': opt.debug++; break;
 
972
          case 'o': opt.outfile = pargs.r.ret_str; break;
 
973
          case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
 
974
          case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
 
975
          case 500: opt.a_long_one++;  break;
 
976
          default : pargs.err = 1; break; /* force warning output */
 
977
        }
 
978
    }
 
979
    for(i=0; i < argc; i++ )
 
980
        printf("%3d -> (%s)\n", i, argv[i] );
 
981
    puts("Options:");
 
982
    if( opt.verbose )
 
983
        printf("  verbose=%d\n", opt.verbose );
 
984
    if( opt.debug )
 
985
        printf("  debug=%d\n", opt.debug );
 
986
    if( opt.outfile )
 
987
        printf("  outfile=`%s'\n", opt.outfile );
 
988
    if( opt.crf )
 
989
        printf("  crffile=`%s'\n", opt.crf );
 
990
    if( opt.myopt )
 
991
        printf("  myopt=%d\n", opt.myopt );
 
992
    if( opt.a_long_one )
 
993
        printf("  a-long-one=%d\n", opt.a_long_one );
 
994
    if( opt.echo       )
 
995
        printf("  echo=%d\n", opt.echo );
 
996
    return 0;
 
997
}
 
998
#endif
 
999
 
 
1000
/**** bottom of file ****/