Subversion Repositories HelenOS

Rev

Rev 3317 | Rev 3336 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3317 post 1
/*  $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $    */
2
 
3
/*-
4
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5
 * All rights reserved.
6
 *
7
 * This code is derived from software contributed to The NetBSD Foundation
8
 * by Dieter Baron and Thomas Klausner.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 */
31
 
32
/* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
33
 
34
#include <assert.h>
35
#include <stdarg.h>
36
#include <err.h>
37
#include <errno.h>
38
#include <getopt.h>
39
#include <stdlib.h>
40
#include <string.h>
41
 
42
/* HelenOS Port: Something similar to warnx(), however we just print
43
 * to the console */
44
 
45
void warnx(const char *fmt, ...);
46
 
47
void warnx(const char *fmt, ...)
48
{
49
    va_list vargs;
50
    va_start(vargs, fmt);
51
    vprintf(fmt, vargs);
52
    va_end(vargs);
53
    printf("\n");
54
    return;
55
}
56
 
57
/* HelenOS Port : We're incorporating only the modern getopt_long with wrappers
58
 * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
59
 * are dropped, we just include the code */
60
 
61
int opterr = 1;     /* if error message should be printed */
62
int optind = 1;     /* index into parent argv vector */
63
int optopt = '?';       /* character checked for validity */
64
int optreset;       /* reset getopt */
65
char *optarg;       /* argument associated with option */
66
 
67
 
68
#define IGNORE_FIRST    (*options == '-' || *options == '+')
69
#define PRINT_ERROR ((opterr) && ((*options != ':') \
70
                      || (IGNORE_FIRST && options[1] != ':')))
71
/*HelenOS Port - POSIXLY_CORRECT is always false */
72
#define IS_POSIXLY_CORRECT 0
73
#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
74
/* XXX: GNU ignores PC if *options == '-' */
75
#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
76
 
77
/* return values */
78
#define BADCH   (int)'?'
79
#define BADARG      ((IGNORE_FIRST && options[1] == ':') \
80
             || (*options == ':') ? (int)':' : (int)'?')
81
#define INORDER (int)1
82
 
83
#define EMSG    ""
84
 
85
static int getopt_internal(int, char **, const char *);
86
static int gcd(int, int);
87
static void permute_args(int, int, int, char **);
88
 
89
static const char *place = EMSG; /* option letter processing */
90
 
91
/* XXX: set optreset to 1 rather than these two */
92
static int nonopt_start = -1; /* first non option argument (for permute) */
93
static int nonopt_end = -1;   /* first option after non options (for permute) */
94
 
95
/* Error messages */
96
static const char recargchar[] = "option requires an argument -- %c";
97
static const char recargstring[] = "option requires an argument -- %s";
98
static const char ambig[] = "ambiguous option -- %.*s";
99
static const char noarg[] = "option doesn't take an argument -- %.*s";
100
static const char illoptchar[] = "unknown option -- %c";
101
static const char illoptstring[] = "unknown option -- %s";
102
 
103
 
104
/*
105
 * Compute the greatest common divisor of a and b.
106
 */
107
static int
108
gcd(a, b)
109
    int a;
110
    int b;
111
{
112
    int c;
113
 
114
    c = a % b;
115
    while (c != 0) {
116
        a = b;
117
        b = c;
118
        c = a % b;
119
    }
120
 
121
    return b;
122
}
123
 
124
/*
125
 * Exchange the block from nonopt_start to nonopt_end with the block
126
 * from nonopt_end to opt_end (keeping the same order of arguments
127
 * in each block).
128
 */
129
static void
130
permute_args(panonopt_start, panonopt_end, opt_end, nargv)
131
    int panonopt_start;
132
    int panonopt_end;
133
    int opt_end;
134
    char **nargv;
135
{
136
    int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
137
    char *swap;
138
 
139
    assert(nargv != NULL);
140
 
141
    /*
142
     * compute lengths of blocks and number and size of cycles
143
     */
144
    nnonopts = panonopt_end - panonopt_start;
145
    nopts = opt_end - panonopt_end;
146
    ncycle = gcd(nnonopts, nopts);
147
    cyclelen = (opt_end - panonopt_start) / ncycle;
148
 
149
    for (i = 0; i < ncycle; i++) {
150
        cstart = panonopt_end+i;
151
        pos = cstart;
152
        for (j = 0; j < cyclelen; j++) {
153
            if (pos >= panonopt_end)
154
                pos -= nnonopts;
155
            else
156
                pos += nopts;
157
            swap = nargv[pos];
158
            nargv[pos] = nargv[cstart];
159
            nargv[cstart] = swap;
160
        }
161
    }
162
}
163
 
164
/*
165
 * getopt_internal --
166
 *  Parse argc/argv argument vector.  Called by user level routines.
167
 *  Returns -2 if -- is found (can be long option or end of options marker).
168
 */
169
static int
170
getopt_internal(nargc, nargv, options)
171
    int nargc;
172
    char **nargv;
173
    const char *options;
174
{
175
    char *oli;              /* option letter list index */
176
    int optchar;
177
 
178
    assert(nargv != NULL);
179
    assert(options != NULL);
180
 
181
    optarg = NULL;
182
 
183
    /*
184
     * XXX Some programs (like rsyncd) expect to be able to
185
     * XXX re-initialize optind to 0 and have getopt_long(3)
186
     * XXX properly function again.  Work around this braindamage.
187
     */
188
    if (optind == 0)
189
        optind = 1;
190
 
191
    if (optreset)
192
        nonopt_start = nonopt_end = -1;
193
start:
194
    if (optreset || !*place) {      /* update scanning pointer */
195
        optreset = 0;
196
        if (optind >= nargc) {          /* end of argument vector */
197
            place = EMSG;
198
            if (nonopt_end != -1) {
199
                /* do permutation, if we have to */
200
                permute_args(nonopt_start, nonopt_end,
201
                    optind, nargv);
202
                optind -= nonopt_end - nonopt_start;
203
            }
204
            else if (nonopt_start != -1) {
205
                /*
206
                 * If we skipped non-options, set optind
207
                 * to the first of them.
208
                 */
209
                optind = nonopt_start;
210
            }
211
            nonopt_start = nonopt_end = -1;
212
            return -1;
213
        }
214
        if ((*(place = nargv[optind]) != '-')
215
            || (place[1] == '\0')) {    /* found non-option */
216
            place = EMSG;
217
            if (IN_ORDER) {
218
                /*
219
                 * GNU extension:
220
                 * return non-option as argument to option 1
221
                 */
222
                optarg = nargv[optind++];
223
                return INORDER;
224
            }
225
            if (!PERMUTE) {
226
                /*
227
                 * if no permutation wanted, stop parsing
228
                 * at first non-option
229
                 */
230
                return -1;
231
            }
232
            /* do permutation */
233
            if (nonopt_start == -1)
234
                nonopt_start = optind;
235
            else if (nonopt_end != -1) {
236
                permute_args(nonopt_start, nonopt_end,
237
                    optind, nargv);
238
                nonopt_start = optind -
239
                    (nonopt_end - nonopt_start);
240
                nonopt_end = -1;
241
            }
242
            optind++;
243
            /* process next argument */
244
            goto start;
245
        }
246
        if (nonopt_start != -1 && nonopt_end == -1)
247
            nonopt_end = optind;
248
        if (place[1] && *++place == '-') {  /* found "--" */
249
            place++;
250
            return -2;
251
        }
252
    }
253
    if ((optchar = (int)*place++) == (int)':' ||
254
        (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
255
        /* option letter unknown or ':' */
256
        if (!*place)
257
            ++optind;
258
        if (PRINT_ERROR)
259
            warnx(illoptchar, optchar);
260
        optopt = optchar;
261
        return BADCH;
262
    }
263
    if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */
264
        /* XXX: what if no long options provided (called by getopt)? */
265
        if (*place)
266
            return -2;
267
 
268
        if (++optind >= nargc) {    /* no arg */
269
            place = EMSG;
270
            if (PRINT_ERROR)
271
                warnx(recargchar, optchar);
272
            optopt = optchar;
273
            return BADARG;
274
        } else              /* white space */
275
            place = nargv[optind];
276
        /*
277
         * Handle -W arg the same as --arg (which causes getopt to
278
         * stop parsing).
279
         */
280
        return -2;
281
    }
282
    if (*++oli != ':') {            /* doesn't take argument */
283
        if (!*place)
284
            ++optind;
285
    } else {                /* takes (optional) argument */
286
        optarg = NULL;
287
        if (*place)         /* no white space */
288
            optarg = *place;
289
        /* XXX: disable test for :: if PC? (GNU doesn't) */
290
        else if (oli[1] != ':') {   /* arg not optional */
291
            if (++optind >= nargc) {    /* no arg */
292
                place = EMSG;
293
                if (PRINT_ERROR)
294
                    warnx(recargchar, optchar);
295
                optopt = optchar;
296
                return BADARG;
297
            } else
298
                optarg = nargv[optind];
299
        }
300
        place = EMSG;
301
        ++optind;
302
    }
303
    /* dump back option letter */
304
    return optchar;
305
}
306
 
307
/*
308
 * getopt --
309
 *  Parse argc/argv argument vector.
310
 */
311
int
312
getopt(nargc, nargv, options)
313
    int nargc;
314
    char * const *nargv;
315
    const char *options;
316
{
317
    int retval;
318
 
319
    assert(nargv != NULL);
320
    assert(options != NULL);
321
 
322
    retval = getopt_internal(nargc, (char **)nargv, options);
323
    if (retval == -2) {
324
        ++optind;
325
        /*
326
         * We found an option (--), so if we skipped non-options,
327
         * we have to permute.
328
         */
329
        if (nonopt_end != -1) {
330
            permute_args(nonopt_start, nonopt_end, optind,
331
                       (char **)nargv);
332
            optind -= nonopt_end - nonopt_start;
333
        }
334
        nonopt_start = nonopt_end = -1;
335
        retval = -1;
336
    }
337
    return retval;
338
}
339
 
340
/*
341
 * getopt_long --
342
 *  Parse argc/argv argument vector.
343
 */
344
int
345
getopt_long(nargc, nargv, options, long_options, idx)
346
    int nargc;
347
    char * const *nargv;
348
    const char *options;
349
    const struct option *long_options;
350
    int *idx;
351
{
352
    int retval;
353
 
354
#define IDENTICAL_INTERPRETATION(_x, _y)                \
355
    (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
356
     long_options[(_x)].flag == long_options[(_y)].flag &&      \
357
     long_options[(_x)].val == long_options[(_y)].val)
358
 
359
    assert(nargv != NULL);
360
    assert(options != NULL);
361
    assert(long_options != NULL);
362
    /* idx may be NULL */
363
 
364
    retval = getopt_internal(nargc, (char **)nargv, options);
365
    if (retval == -2) {
366
        char *current_argv, *has_equal;
367
        size_t current_argv_len;
368
        int i, ambiguous, match;
369
 
370
        current_argv = (char *)place;
371
        match = -1;
372
        ambiguous = 0;
373
 
374
        optind++;
375
        place = EMSG;
376
 
377
        if (*current_argv == '\0') {        /* found "--" */
378
            /*
379
             * We found an option (--), so if we skipped
380
             * non-options, we have to permute.
381
             */
382
            if (nonopt_end != -1) {
383
                permute_args(nonopt_start, nonopt_end,
384
                    optind, (char **)nargv);
385
                optind -= nonopt_end - nonopt_start;
386
            }
387
            nonopt_start = nonopt_end = -1;
388
            return -1;
389
        }
390
        if ((has_equal = strchr(current_argv, '=')) != NULL) {
391
            /* argument found (--option=arg) */
392
            current_argv_len = has_equal - current_argv;
393
            has_equal++;
394
        } else
395
            current_argv_len = strlen(current_argv);
396
 
397
        for (i = 0; long_options[i].name; i++) {
398
            /* find matching long option */
399
            if (strncmp(current_argv, long_options[i].name,
400
                current_argv_len))
401
                continue;
402
 
403
            if (strlen(long_options[i].name) ==
404
                (unsigned)current_argv_len) {
405
                /* exact match */
406
                match = i;
407
                ambiguous = 0;
408
                break;
409
            }
410
            if (match == -1)        /* partial match */
411
                match = i;
412
            else if (!IDENTICAL_INTERPRETATION(i, match))
413
                ambiguous = 1;
414
        }
415
        if (ambiguous) {
416
            /* ambiguous abbreviation */
417
            if (PRINT_ERROR)
418
                warnx(ambig, (int)current_argv_len,
419
                     current_argv);
420
            optopt = 0;
421
            return BADCH;
422
        }
423
        if (match != -1) {          /* option found */
424
                if (long_options[match].has_arg == no_argument
425
                && has_equal) {
426
                if (PRINT_ERROR)
427
                    warnx(noarg, (int)current_argv_len,
428
                         current_argv);
429
                /*
430
                 * XXX: GNU sets optopt to val regardless of
431
                 * flag
432
                 */
433
                if (long_options[match].flag == NULL)
434
                    optopt = long_options[match].val;
435
                else
436
                    optopt = 0;
437
                return BADARG;
438
            }
439
            if (long_options[match].has_arg == required_argument ||
440
                long_options[match].has_arg == optional_argument) {
441
                if (has_equal)
442
                    optarg = has_equal;
443
                else if (long_options[match].has_arg ==
444
                    required_argument) {
445
                    /*
446
                     * optional argument doesn't use
447
                     * next nargv
448
                     */
449
                    optarg = nargv[optind++];
450
                }
451
            }
452
            if ((long_options[match].has_arg == required_argument)
453
                && (optarg == NULL)) {
454
                /*
455
                 * Missing argument; leading ':'
456
                 * indicates no error should be generated
457
                 */
458
                if (PRINT_ERROR)
459
                    warnx(recargstring, current_argv);
460
                /*
461
                 * XXX: GNU sets optopt to val regardless
462
                 * of flag
463
                 */
464
                if (long_options[match].flag == NULL)
465
                    optopt = long_options[match].val;
466
                else
467
                    optopt = 0;
468
                --optind;
469
                return BADARG;
470
            }
471
        } else {            /* unknown option */
472
            if (PRINT_ERROR)
473
                warnx(illoptstring, current_argv);
474
            optopt = 0;
475
            return BADCH;
476
        }
477
        if (long_options[match].flag) {
478
            *long_options[match].flag = long_options[match].val;
479
            retval = 0;
480
        } else
481
            retval = long_options[match].val;
482
        if (idx)
483
            *idx = match;
484
    }
485
    return retval;
486
#undef IDENTICAL_INTERPRETATION
487
}
488