Subversion Repositories HelenOS

Rev

Rev 3318 | Go to most recent revision | Details | 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
 * [eventually this will replace the real getopt]
312
 */
313
int
314
getopt(nargc, nargv, options)
315
    int nargc;
316
    char * const *nargv;
317
    const char *options;
318
{
319
    int retval;
320
 
321
    assert(nargv != NULL);
322
    assert(options != NULL);
323
 
324
    retval = getopt_internal(nargc, (char **)nargv, options);
325
    if (retval == -2) {
326
        ++optind;
327
        /*
328
         * We found an option (--), so if we skipped non-options,
329
         * we have to permute.
330
         */
331
        if (nonopt_end != -1) {
332
            permute_args(nonopt_start, nonopt_end, optind,
333
                       (char **)nargv);
334
            optind -= nonopt_end - nonopt_start;
335
        }
336
        nonopt_start = nonopt_end = -1;
337
        retval = -1;
338
    }
339
    return retval;
340
}
341
 
342
/*
343
 * getopt_long --
344
 *  Parse argc/argv argument vector.
345
 */
346
int
347
getopt_long(nargc, nargv, options, long_options, idx)
348
    int nargc;
349
    char * const *nargv;
350
    const char *options;
351
    const struct option *long_options;
352
    int *idx;
353
{
354
    int retval;
355
 
356
#define IDENTICAL_INTERPRETATION(_x, _y)                \
357
    (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
358
     long_options[(_x)].flag == long_options[(_y)].flag &&      \
359
     long_options[(_x)].val == long_options[(_y)].val)
360
 
361
    assert(nargv != NULL);
362
    assert(options != NULL);
363
    assert(long_options != NULL);
364
    /* idx may be NULL */
365
 
366
    retval = getopt_internal(nargc, (char **)nargv, options);
367
    if (retval == -2) {
368
        char *current_argv, *has_equal;
369
        size_t current_argv_len;
370
        int i, ambiguous, match;
371
 
372
        current_argv = (char *)place;
373
        match = -1;
374
        ambiguous = 0;
375
 
376
        optind++;
377
        place = EMSG;
378
 
379
        if (*current_argv == '\0') {        /* found "--" */
380
            /*
381
             * We found an option (--), so if we skipped
382
             * non-options, we have to permute.
383
             */
384
            if (nonopt_end != -1) {
385
                permute_args(nonopt_start, nonopt_end,
386
                    optind, (char **)nargv);
387
                optind -= nonopt_end - nonopt_start;
388
            }
389
            nonopt_start = nonopt_end = -1;
390
            return -1;
391
        }
392
        if ((has_equal = strchr(current_argv, '=')) != NULL) {
393
            /* argument found (--option=arg) */
394
            current_argv_len = has_equal - current_argv;
395
            has_equal++;
396
        } else
397
            current_argv_len = strlen(current_argv);
398
 
399
        for (i = 0; long_options[i].name; i++) {
400
            /* find matching long option */
401
            if (strncmp(current_argv, long_options[i].name,
402
                current_argv_len))
403
                continue;
404
 
405
            if (strlen(long_options[i].name) ==
406
                (unsigned)current_argv_len) {
407
                /* exact match */
408
                match = i;
409
                ambiguous = 0;
410
                break;
411
            }
412
            if (match == -1)        /* partial match */
413
                match = i;
414
            else if (!IDENTICAL_INTERPRETATION(i, match))
415
                ambiguous = 1;
416
        }
417
        if (ambiguous) {
418
            /* ambiguous abbreviation */
419
            if (PRINT_ERROR)
420
                warnx(ambig, (int)current_argv_len,
421
                     current_argv);
422
            optopt = 0;
423
            return BADCH;
424
        }
425
        if (match != -1) {          /* option found */
426
                if (long_options[match].has_arg == no_argument
427
                && has_equal) {
428
                if (PRINT_ERROR)
429
                    warnx(noarg, (int)current_argv_len,
430
                         current_argv);
431
                /*
432
                 * XXX: GNU sets optopt to val regardless of
433
                 * flag
434
                 */
435
                if (long_options[match].flag == NULL)
436
                    optopt = long_options[match].val;
437
                else
438
                    optopt = 0;
439
                return BADARG;
440
            }
441
            if (long_options[match].has_arg == required_argument ||
442
                long_options[match].has_arg == optional_argument) {
443
                if (has_equal)
444
                    optarg = has_equal;
445
                else if (long_options[match].has_arg ==
446
                    required_argument) {
447
                    /*
448
                     * optional argument doesn't use
449
                     * next nargv
450
                     */
451
                    optarg = nargv[optind++];
452
                }
453
            }
454
            if ((long_options[match].has_arg == required_argument)
455
                && (optarg == NULL)) {
456
                /*
457
                 * Missing argument; leading ':'
458
                 * indicates no error should be generated
459
                 */
460
                if (PRINT_ERROR)
461
                    warnx(recargstring, current_argv);
462
                /*
463
                 * XXX: GNU sets optopt to val regardless
464
                 * of flag
465
                 */
466
                if (long_options[match].flag == NULL)
467
                    optopt = long_options[match].val;
468
                else
469
                    optopt = 0;
470
                --optind;
471
                return BADARG;
472
            }
473
        } else {            /* unknown option */
474
            if (PRINT_ERROR)
475
                warnx(illoptstring, current_argv);
476
            optopt = 0;
477
            return BADCH;
478
        }
479
        if (long_options[match].flag) {
480
            *long_options[match].flag = long_options[match].val;
481
            retval = 0;
482
        } else
483
            retval = long_options[match].val;
484
        if (idx)
485
            *idx = match;
486
    }
487
    return retval;
488
#undef IDENTICAL_INTERPRETATION
489
}
490