Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/file/src/getopt_long.c
39478 views
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
#include "file.h"
33
34
#ifndef lint
35
FILE_RCSID("@(#)$File: getopt_long.c,v 1.9 2022/09/24 20:30:13 christos Exp $")
36
#endif /* lint */
37
38
#include <assert.h>
39
#ifdef HAVE_ERR_H
40
#include <err.h>
41
#else
42
#define warnx printf
43
#endif
44
#include <errno.h>
45
#if defined(HAVE_GETOPT_H) && defined(HAVE_STRUCT_OPTION)
46
#include <getopt.h>
47
#else
48
#include "mygetopt.h"
49
#endif
50
#include <stdlib.h>
51
#include <string.h>
52
53
#define REPLACE_GETOPT
54
55
#ifndef _DIAGASSERT
56
#define _DIAGASSERT assert
57
#endif
58
59
#ifdef REPLACE_GETOPT
60
#ifdef __weak_alias
61
__weak_alias(getopt,_getopt)
62
#endif
63
int opterr = 1; /* if error message should be printed */
64
int optind = 1; /* index into parent argv vector */
65
int optopt = '?'; /* character checked for validity */
66
int optreset; /* reset getopt */
67
char *optarg; /* argument associated with option */
68
#elif HAVE_NBTOOL_CONFIG_H && !HAVE_DECL_OPTRESET
69
static int optreset;
70
#endif
71
72
#ifdef __weak_alias
73
__weak_alias(getopt_long,_getopt_long)
74
#endif
75
76
#define IGNORE_FIRST (*options == '-' || *options == '+')
77
#define PRINT_ERROR ((opterr) && ((*options != ':') \
78
|| (IGNORE_FIRST && options[1] != ':')))
79
#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
80
#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
81
/* XXX: GNU ignores PC if *options == '-' */
82
#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
83
84
/* return values */
85
#define BADCH (int)'?'
86
#define BADARG ((IGNORE_FIRST && options[1] == ':') \
87
|| (*options == ':') ? (int)':' : (int)'?')
88
#define INORDER (int)1
89
90
#define EMSG ""
91
92
static int getopt_internal(int, char **, const char *);
93
static int gcd(int, int);
94
static void permute_args(int, int, int, char **);
95
96
static const char *place = EMSG; /* option letter processing */
97
98
/* XXX: set optreset to 1 rather than these two */
99
static int nonopt_start = -1; /* first non option argument (for permute) */
100
static int nonopt_end = -1; /* first option after non options (for permute) */
101
102
/* Error messages */
103
static const char recargchar[] = "option requires an argument -- %c";
104
static const char recargstring[] = "option requires an argument -- %s";
105
static const char ambig[] = "ambiguous option -- %.*s";
106
static const char noarg[] = "option doesn't take an argument -- %.*s";
107
static const char illoptchar[] = "unknown option -- %c";
108
static const char illoptstring[] = "unknown option -- %s";
109
110
111
/*
112
* Compute the greatest common divisor of a and b.
113
*/
114
static int
115
gcd(a, b)
116
int a;
117
int b;
118
{
119
int c;
120
121
c = a % b;
122
while (c != 0) {
123
a = b;
124
b = c;
125
c = a % b;
126
}
127
128
return b;
129
}
130
131
/*
132
* Exchange the block from nonopt_start to nonopt_end with the block
133
* from nonopt_end to opt_end (keeping the same order of arguments
134
* in each block).
135
*/
136
static void
137
permute_args(panonopt_start, panonopt_end, opt_end, nargv)
138
int panonopt_start;
139
int panonopt_end;
140
int opt_end;
141
char **nargv;
142
{
143
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
144
char *swap;
145
146
_DIAGASSERT(nargv != NULL);
147
148
/*
149
* compute lengths of blocks and number and size of cycles
150
*/
151
nnonopts = panonopt_end - panonopt_start;
152
nopts = opt_end - panonopt_end;
153
ncycle = gcd(nnonopts, nopts);
154
cyclelen = (opt_end - panonopt_start) / ncycle;
155
156
for (i = 0; i < ncycle; i++) {
157
cstart = panonopt_end+i;
158
pos = cstart;
159
for (j = 0; j < cyclelen; j++) {
160
if (pos >= panonopt_end)
161
pos -= nnonopts;
162
else
163
pos += nopts;
164
swap = nargv[pos];
165
nargv[pos] = nargv[cstart];
166
nargv[cstart] = swap;
167
}
168
}
169
}
170
171
/*
172
* getopt_internal --
173
* Parse argc/argv argument vector. Called by user level routines.
174
* Returns -2 if -- is found (can be long option or end of options marker).
175
*/
176
static int
177
getopt_internal(nargc, nargv, options)
178
int nargc;
179
char **nargv;
180
const char *options;
181
{
182
char *oli; /* option letter list index */
183
int optchar;
184
185
_DIAGASSERT(nargv != NULL);
186
_DIAGASSERT(options != NULL);
187
188
optarg = NULL;
189
190
/*
191
* XXX Some programs (like rsyncd) expect to be able to
192
* XXX re-initialize optind to 0 and have getopt_long(3)
193
* XXX properly function again. Work around this braindamage.
194
*/
195
if (optind == 0)
196
optind = 1;
197
198
if (optreset)
199
nonopt_start = nonopt_end = -1;
200
start:
201
if (optreset || !*place) { /* update scanning pointer */
202
optreset = 0;
203
if (optind >= nargc) { /* end of argument vector */
204
place = EMSG;
205
if (nonopt_end != -1) {
206
/* do permutation, if we have to */
207
permute_args(nonopt_start, nonopt_end,
208
optind, nargv);
209
optind -= nonopt_end - nonopt_start;
210
}
211
else if (nonopt_start != -1) {
212
/*
213
* If we skipped non-options, set optind
214
* to the first of them.
215
*/
216
optind = nonopt_start;
217
}
218
nonopt_start = nonopt_end = -1;
219
return -1;
220
}
221
if ((*(place = nargv[optind]) != '-')
222
|| (place[1] == '\0')) { /* found non-option */
223
place = EMSG;
224
if (IN_ORDER) {
225
/*
226
* GNU extension:
227
* return non-option as argument to option 1
228
*/
229
optarg = nargv[optind++];
230
return INORDER;
231
}
232
if (!PERMUTE) {
233
/*
234
* if no permutation wanted, stop parsing
235
* at first non-option
236
*/
237
return -1;
238
}
239
/* do permutation */
240
if (nonopt_start == -1)
241
nonopt_start = optind;
242
else if (nonopt_end != -1) {
243
permute_args(nonopt_start, nonopt_end,
244
optind, nargv);
245
nonopt_start = optind -
246
(nonopt_end - nonopt_start);
247
nonopt_end = -1;
248
}
249
optind++;
250
/* process next argument */
251
goto start;
252
}
253
if (nonopt_start != -1 && nonopt_end == -1)
254
nonopt_end = optind;
255
if (place[1] && *++place == '-') { /* found "--" */
256
place++;
257
return -2;
258
}
259
}
260
if ((optchar = (int)*place++) == (int)':' ||
261
(oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
262
/* option letter unknown or ':' */
263
if (!*place)
264
++optind;
265
if (PRINT_ERROR)
266
warnx(illoptchar, optchar);
267
optopt = optchar;
268
return BADCH;
269
}
270
if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
271
/* XXX: what if no long options provided (called by getopt)? */
272
if (*place)
273
return -2;
274
275
if (++optind >= nargc) { /* no arg */
276
place = EMSG;
277
if (PRINT_ERROR)
278
warnx(recargchar, optchar);
279
optopt = optchar;
280
return BADARG;
281
} else /* white space */
282
place = nargv[optind];
283
/*
284
* Handle -W arg the same as --arg (which causes getopt to
285
* stop parsing).
286
*/
287
return -2;
288
}
289
if (*++oli != ':') { /* doesn't take argument */
290
if (!*place)
291
++optind;
292
} else { /* takes (optional) argument */
293
optarg = NULL;
294
if (*place) /* no white space */
295
optarg = (char *)place;
296
/* XXX: disable test for :: if PC? (GNU doesn't) */
297
else if (oli[1] != ':') { /* arg not optional */
298
if (++optind >= nargc) { /* no arg */
299
place = EMSG;
300
if (PRINT_ERROR)
301
warnx(recargchar, optchar);
302
optopt = optchar;
303
return BADARG;
304
} else
305
optarg = nargv[optind];
306
}
307
place = EMSG;
308
++optind;
309
}
310
/* dump back option letter */
311
return optchar;
312
}
313
314
#ifdef REPLACE_GETOPT
315
/*
316
* getopt --
317
* Parse argc/argv argument vector.
318
*
319
* [eventually this will replace the real getopt]
320
*/
321
int
322
getopt(nargc, nargv, options)
323
int nargc;
324
char * const *nargv;
325
const char *options;
326
{
327
int retval;
328
329
_DIAGASSERT(nargv != NULL);
330
_DIAGASSERT(options != NULL);
331
332
retval = getopt_internal(nargc, (char **)nargv, options);
333
if (retval == -2) {
334
++optind;
335
/*
336
* We found an option (--), so if we skipped non-options,
337
* we have to permute.
338
*/
339
if (nonopt_end != -1) {
340
permute_args(nonopt_start, nonopt_end, optind,
341
(char **)nargv);
342
optind -= nonopt_end - nonopt_start;
343
}
344
nonopt_start = nonopt_end = -1;
345
retval = -1;
346
}
347
return retval;
348
}
349
#endif
350
351
/*
352
* getopt_long --
353
* Parse argc/argv argument vector.
354
*/
355
int
356
getopt_long(nargc, nargv, options, long_options, idx)
357
int nargc;
358
char * const *nargv;
359
const char *options;
360
const struct option *long_options;
361
int *idx;
362
{
363
int retval;
364
365
#define IDENTICAL_INTERPRETATION(_x, _y) \
366
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
367
long_options[(_x)].flag == long_options[(_y)].flag && \
368
long_options[(_x)].val == long_options[(_y)].val)
369
370
_DIAGASSERT(nargv != NULL);
371
_DIAGASSERT(options != NULL);
372
_DIAGASSERT(long_options != NULL);
373
/* idx may be NULL */
374
375
retval = getopt_internal(nargc, (char **)nargv, options);
376
if (retval == -2) {
377
char *current_argv, *has_equal;
378
size_t current_argv_len;
379
int i, ambiguous, match;
380
381
current_argv = (char *)place;
382
match = -1;
383
ambiguous = 0;
384
385
optind++;
386
place = EMSG;
387
388
if (*current_argv == '\0') { /* found "--" */
389
/*
390
* We found an option (--), so if we skipped
391
* non-options, we have to permute.
392
*/
393
if (nonopt_end != -1) {
394
permute_args(nonopt_start, nonopt_end,
395
optind, (char **)nargv);
396
optind -= nonopt_end - nonopt_start;
397
}
398
nonopt_start = nonopt_end = -1;
399
return -1;
400
}
401
if ((has_equal = strchr(current_argv, '=')) != NULL) {
402
/* argument found (--option=arg) */
403
current_argv_len = has_equal - current_argv;
404
has_equal++;
405
} else
406
current_argv_len = strlen(current_argv);
407
408
for (i = 0; long_options[i].name; i++) {
409
/* find matching long option */
410
if (strncmp(current_argv, long_options[i].name,
411
current_argv_len))
412
continue;
413
414
if (strlen(long_options[i].name) ==
415
(unsigned)current_argv_len) {
416
/* exact match */
417
match = i;
418
ambiguous = 0;
419
break;
420
}
421
if (match == -1) /* partial match */
422
match = i;
423
else if (!IDENTICAL_INTERPRETATION(i, match))
424
ambiguous = 1;
425
}
426
if (ambiguous) {
427
/* ambiguous abbreviation */
428
if (PRINT_ERROR)
429
warnx(ambig, (int)current_argv_len,
430
current_argv);
431
optopt = 0;
432
return BADCH;
433
}
434
if (match != -1) { /* option found */
435
if (long_options[match].has_arg == no_argument
436
&& has_equal) {
437
if (PRINT_ERROR)
438
warnx(noarg, (int)current_argv_len,
439
current_argv);
440
/*
441
* XXX: GNU sets optopt to val regardless of
442
* flag
443
*/
444
if (long_options[match].flag == NULL)
445
optopt = long_options[match].val;
446
else
447
optopt = 0;
448
return BADARG;
449
}
450
if (long_options[match].has_arg == required_argument ||
451
long_options[match].has_arg == optional_argument) {
452
if (has_equal)
453
optarg = has_equal;
454
else if (long_options[match].has_arg ==
455
required_argument) {
456
/*
457
* optional argument doesn't use
458
* next nargv
459
*/
460
optarg = nargv[optind++];
461
}
462
}
463
if ((long_options[match].has_arg == required_argument)
464
&& (optarg == NULL)) {
465
/*
466
* Missing argument; leading ':'
467
* indicates no error should be generated
468
*/
469
if (PRINT_ERROR)
470
warnx(recargstring, current_argv);
471
/*
472
* XXX: GNU sets optopt to val regardless
473
* of flag
474
*/
475
if (long_options[match].flag == NULL)
476
optopt = long_options[match].val;
477
else
478
optopt = 0;
479
--optind;
480
return BADARG;
481
}
482
} else { /* unknown option */
483
if (PRINT_ERROR)
484
warnx(illoptstring, current_argv);
485
optopt = 0;
486
return BADCH;
487
}
488
if (long_options[match].flag) {
489
*long_options[match].flag = long_options[match].val;
490
retval = 0;
491
} else
492
retval = long_options[match].val;
493
if (idx)
494
*idx = match;
495
}
496
return retval;
497
#undef IDENTICAL_INTERPRETATION
498
}
499
500