Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

klish / libc / getopt.c @ 1a733d28

History | View | Annotate | Download (11.9 KB)

1

    
2
/*-
3
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
4
 * All rights reserved.
5
 *
6
 * This code is derived from software contributed to The NetBSD Foundation
7
 * by Dieter Baron and Thomas Klausner.
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
 * POSSIBILITY OF SUCH DAMAGE.
29
 *
30
 *      $NetBSD: getopt_long.c,v 1.3 2008/04/29 05:46:09 martin Exp $
31
 */
32

    
33
#include <assert.h>
34
#include <errno.h>
35
#include <stdarg.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include "getopt.h"
40

    
41
#ifndef _DIAGASSERT
42
#define _DIAGASSERT(e)
43
#endif
44

    
45
int        opterr = 1;                /* if error message should be printed */
46
int        optind = 1;                /* index into parent argv vector */
47
int        optopt = '?';                /* character checked for validity */
48
int        optreset;                /* reset getopt */
49
char    *optarg;                /* argument associated with option */
50

    
51

    
52
#define IGNORE_FIRST        (*options == '-' || *options == '+')
53
#define PRINT_ERROR        ((opterr) && ((*options != ':') \
54
                                      || (IGNORE_FIRST && options[1] != ':')))
55
#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
56
#define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
57
/* XXX: GNU ignores PC if *options == '-' */
58
#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
59

    
60
/* return values */
61
#define        BADCH        (int)'?'
62
#define        BADARG        (int)':'
63
#define INORDER (int)1
64

    
65
#define        EMSG        ""
66

    
67
static int getopt_internal(int, char * const *, const char *);
68
static int gcd(int, int);
69
static void permute_args(int, int, int, char * const *);
70
static void xwarnx(const char *, ...);
71

    
72
static char *place = EMSG; /* option letter processing */
73

    
74
/* XXX: set optreset to 1 rather than these two */
75
static int nonopt_start = -1; /* first non option argument (for permute) */
76
static int nonopt_end = -1;   /* first option after non options (for permute) */
77

    
78
/* Error messages */
79
static const char recargchar[] = "option requires an argument -- %c";
80
static const char recargstring[] = "option requires an argument -- %s";
81
static const char ambig[] = "ambiguous option -- %.*s";
82
static const char noarg[] = "option doesn't take an argument -- %.*s";
83
static const char illoptchar[] = "illegal option -- %c";
84
static const char illoptstring[] = "illegal option -- %s";
85

    
86
static const char *progname;
87

    
88

    
89
/* Replacement for warnx(3) for systems without it. */
90
static void xwarnx(const char *fmt, ...) {
91
        va_list ap;
92

    
93
        va_start(ap, fmt);
94
        if (progname)
95
                (void) fprintf(stderr, "%s: ", progname);
96
        if (fmt)
97
                (void) vfprintf(stderr, fmt, ap);
98
        (void) fprintf(stderr, "\n");
99
        va_end(ap);
100
}
101

    
102
/*
103
 * Compute the greatest common divisor of a and b.
104
 */
105
static int
106
gcd(int a, int b)
107
{
108
        int c;
109

    
110
        c = a % b;
111
        while (c != 0) {
112
                a = b;
113
                b = c;
114
                c = a % b;
115
        }
116

    
117
        return b;
118
}
119

    
120
/*
121
 * Exchange the block from nonopt_start to nonopt_end with the block
122
 * from nonopt_end to opt_end (keeping the same order of arguments
123
 * in each block).
124
 */
125
static void
126
permute_args(int nonopt_start, int nonopt_end, int opt_end, char * const *nargv)
127
{
128
        int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
129
        char *swap;
130

    
131
        /*
132
         * compute lengths of blocks and number and size of cycles
133
         */
134
        nnonopts = nonopt_end - nonopt_start;
135
        nopts = opt_end - nonopt_end;
136
        ncycle = gcd(nnonopts, nopts);
137
        cyclelen = (opt_end - nonopt_start) / ncycle;
138

    
139
        for (i = 0; i < ncycle; i++) {
140
                cstart = nonopt_end+i;
141
                pos = cstart;
142
                for (j = 0; j < cyclelen; j++) {
143
                        if (pos >= nonopt_end)
144
                                pos -= nnonopts;
145
                        else
146
                                pos += nopts;
147
                        swap = nargv[pos];
148
                        /* LINTED const cast */
149
                        ((char **) nargv)[pos] = nargv[cstart];
150
                        /* LINTED const cast */
151
                        ((char **)nargv)[cstart] = swap;
152
                }
153
        }
154
}
155

    
156
/*
157
 * getopt_internal --
158
 *        Parse argc/argv argument vector.  Called by user level routines.
159
 *  Returns -2 if -- is found (can be long option or end of options marker).
160
 */
161
static int
162
getopt_internal(int nargc, char * const *nargv, const char *options)
163
{
164
        char *oli;                                /* option letter list index */
165
        int optchar;
166

    
167
        _DIAGASSERT(nargv != NULL);
168
        _DIAGASSERT(options != NULL);
169

    
170
        optarg = NULL;
171

    
172
        /*
173
         * XXX Some programs (like rsyncd) expect to be able to
174
         * XXX re-initialize optind to 0 and have getopt_long(3)
175
         * XXX properly function again.  Work around this braindamage.
176
         */
177
        if (optind == 0)
178
                optind = 1;
179

    
180
        if (optreset)
181
                nonopt_start = nonopt_end = -1;
182
start:
183
        if (optreset || !*place) {                /* update scanning pointer */
184
                optreset = 0;
185
                if (optind >= nargc) {          /* end of argument vector */
186
                        place = EMSG;
187
                        if (nonopt_end != -1) {
188
                                /* do permutation, if we have to */
189
                                permute_args(nonopt_start, nonopt_end,
190
                                    optind, nargv);
191
                                optind -= nonopt_end - nonopt_start;
192
                        }
193
                        else if (nonopt_start != -1) {
194
                                /*
195
                                 * If we skipped non-options, set optind
196
                                 * to the first of them.
197
                                 */
198
                                optind = nonopt_start;
199
                        }
200
                        nonopt_start = nonopt_end = -1;
201
                        return -1;
202
                }
203
                if (*(place = nargv[optind]) != '-') {  /* found non-option */
204
                        place = EMSG;
205
                        if (IN_ORDER) {
206
                                /*
207
                                 * GNU extension:
208
                                 * return non-option as argument to option 1
209
                                 */
210
                                optarg = nargv[optind++];
211
                                return INORDER;
212
                        }
213
                        if (!PERMUTE) {
214
                                /*
215
                                 * if no permutation wanted, stop parsing
216
                                 * at first non-option
217
                                 */
218
                                return -1;
219
                        }
220
                        /* do permutation */
221
                        if (nonopt_start == -1)
222
                                nonopt_start = optind;
223
                        else if (nonopt_end != -1) {
224
                                permute_args(nonopt_start, nonopt_end,
225
                                    optind, nargv);
226
                                nonopt_start = optind -
227
                                    (nonopt_end - nonopt_start);
228
                                nonopt_end = -1;
229
                        }
230
                        optind++;
231
                        /* process next argument */
232
                        goto start;
233
                }
234
                if (nonopt_start != -1 && nonopt_end == -1)
235
                        nonopt_end = optind;
236
                if (place[1] && *++place == '-') {        /* found "--" */
237
                        place++;
238
                        return -2;
239
                }
240
        }
241
        if ((optchar = (int)*place++) == (int)':' ||
242
            (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
243
                /* option letter unknown or ':' */
244
                if (!*place)
245
                        ++optind;
246
                if (PRINT_ERROR)
247
                        xwarnx(illoptchar, optchar);
248
                optopt = optchar;
249
                return BADCH;
250
        }
251
        if (optchar == 'W' && oli[1] == ';') {                /* -W long-option */
252
                /* XXX: what if no long options provided (called by getopt)? */
253
                if (*place)
254
                        return -2;
255

    
256
                if (++optind >= nargc) {        /* no arg */
257
                        place = EMSG;
258
                        if (PRINT_ERROR)
259
                                xwarnx(recargchar, optchar);
260
                        optopt = optchar;
261
                        /* XXX: GNU returns '?' if options[0] != ':' */
262
                        return BADARG;
263
                } else                                /* white space */
264
                        place = nargv[optind];
265
                /*
266
                 * Handle -W arg the same as --arg (which causes getopt to
267
                 * stop parsing).
268
                 */
269
                return -2;
270
        }
271
        if (*++oli != ':') {                        /* doesn't take argument */
272
                if (!*place)
273
                        ++optind;
274
        } else {                                /* takes (optional) argument */
275
                optarg = NULL;
276
                if (*place)                        /* no white space */
277
                        optarg = place;
278
                /* XXX: disable test for :: if PC? (GNU doesn't) */
279
                else if (oli[1] != ':') {        /* arg not optional */
280
                        if (++optind >= nargc) {        /* no arg */
281
                                place = EMSG;
282
                                if (PRINT_ERROR)
283
                                        xwarnx(recargchar, optchar);
284
                                optopt = optchar;
285
                                /* XXX: GNU returns '?' if options[0] != ':' */
286
                                return BADARG;
287
                        } else
288
                                optarg = nargv[optind];
289
                }
290
                place = EMSG;
291
                ++optind;
292
        }
293
        /* dump back option letter */
294
        return optchar;
295
}
296

    
297
/*
298
 * getopt --
299
 *        Parse argc/argv argument vector.
300
 *
301
 * [eventually this will replace the real getopt]
302
 */
303
int
304
getopt(int nargc, char * const *nargv, const char *options)
305
{
306
        int retval;
307

    
308
        progname = nargv[0];
309

    
310
        if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
311
                ++optind;
312
                /*
313
                 * We found an option (--), so if we skipped non-options,
314
                 * we have to permute.
315
                 */
316
                if (nonopt_end != -1) {
317
                        permute_args(nonopt_start, nonopt_end, optind,
318
                                       nargv);
319
                        optind -= nonopt_end - nonopt_start;
320
                }
321
                nonopt_start = nonopt_end = -1;
322
                retval = -1;
323
        }
324
        return retval;
325
}
326

    
327
/*
328
 * getopt_long --
329
 *        Parse argc/argv argument vector.
330
 */
331
int
332
getopt_long(int nargc,
333
            char * const *nargv,
334
            const char *options,
335
            const struct option *long_options,
336
            int *idx)
337
{
338
        int retval;
339

    
340
        _DIAGASSERT(nargv != NULL);
341
        _DIAGASSERT(options != NULL);
342
        _DIAGASSERT(long_options != NULL);
343
        /* idx may be NULL */
344

    
345
        progname = nargv[0];
346

    
347
        if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
348
                char *current_argv, *has_equal;
349
                size_t current_argv_len;
350
                int i, match;
351

    
352
                current_argv = place;
353
                match = -1;
354

    
355
                optind++;
356
                place = EMSG;
357

    
358
                if (*current_argv == '\0') {                /* found "--" */
359
                        /*
360
                         * We found an option (--), so if we skipped
361
                         * non-options, we have to permute.
362
                         */
363
                        if (nonopt_end != -1) {
364
                                permute_args(nonopt_start, nonopt_end,
365
                                    optind, nargv);
366
                                optind -= nonopt_end - nonopt_start;
367
                        }
368
                        nonopt_start = nonopt_end = -1;
369
                        return -1;
370
                }
371
                if ((has_equal = strchr(current_argv, '=')) != NULL) {
372
                        /* argument found (--option=arg) */
373
                        current_argv_len = has_equal - current_argv;
374
                        has_equal++;
375
                } else
376
                        current_argv_len = strlen(current_argv);
377

    
378
                for (i = 0; long_options[i].name; i++) {
379
                        /* find matching long option */
380
                        if (strncmp(current_argv, long_options[i].name,
381
                            current_argv_len))
382
                                continue;
383

    
384
                        if (strlen(long_options[i].name) ==
385
                            (unsigned)current_argv_len) {
386
                                /* exact match */
387
                                match = i;
388
                                break;
389
                        }
390
                        if (match == -1)                /* partial match */
391
                                match = i;
392
                        else {
393
                                /* ambiguous abbreviation */
394
                                if (PRINT_ERROR)
395
                                        xwarnx(ambig, (int)current_argv_len,
396
                                             current_argv);
397
                                optopt = 0;
398
                                return BADCH;
399
                        }
400
                }
401
                if (match != -1) {                        /* option found */
402
                        if (long_options[match].has_arg == no_argument
403
                            && has_equal) {
404
                                if (PRINT_ERROR)
405
                                        xwarnx(noarg, (int)current_argv_len,
406
                                             current_argv);
407
                                /*
408
                                 * XXX: GNU sets optopt to val regardless of
409
                                 * flag
410
                                 */
411
                                if (long_options[match].flag == NULL)
412
                                        optopt = long_options[match].val;
413
                                else
414
                                        optopt = 0;
415
                                /* XXX: GNU returns '?' if options[0] != ':' */
416
                                return BADARG;
417
                        }
418
                        if (long_options[match].has_arg == required_argument ||
419
                            long_options[match].has_arg == optional_argument) {
420
                                if (has_equal)
421
                                        optarg = has_equal;
422
                                else if (long_options[match].has_arg ==
423
                                    required_argument) {
424
                                        /*
425
                                         * optional argument doesn't use
426
                                         * next nargv
427
                                         */
428
                                        optarg = nargv[optind++];
429
                                }
430
                        }
431
                        if ((long_options[match].has_arg == required_argument)
432
                            && (optarg == NULL)) {
433
                                /*
434
                                 * Missing argument; leading ':'
435
                                 * indicates no error should be generated
436
                                 */
437
                                if (PRINT_ERROR)
438
                                        xwarnx(recargstring, current_argv);
439
                                /*
440
                                 * XXX: GNU sets optopt to val regardless
441
                                 * of flag
442
                                 */
443
                                if (long_options[match].flag == NULL)
444
                                        optopt = long_options[match].val;
445
                                else
446
                                        optopt = 0;
447
                                /* XXX: GNU returns '?' if options[0] != ':' */
448
                                --optind;
449
                                return BADARG;
450
                        }
451
                } else {                        /* unknown option */
452
                        if (PRINT_ERROR)
453
                                xwarnx(illoptstring, current_argv);
454
                        optopt = 0;
455
                        return BADCH;
456
                }
457
                if (long_options[match].flag) {
458
                        *long_options[match].flag = long_options[match].val;
459
                        retval = 0;
460
                } else
461
                        retval = long_options[match].val;
462
                if (idx)
463
                        *idx = match;
464
        }
465
        return retval;
466
}