1//===-- GetOptInc.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Host/common/GetOptInc.h"
10
11#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || \
12 defined(REPLACE_GETOPT_LONG_ONLY)
13
14// getopt.cpp
15#include <cerrno>
16#include <cstdlib>
17#include <cstring>
18
19#if defined(REPLACE_GETOPT)
20int opterr = 1; /* if error message should be printed */
21int optind = 1; /* index into parent argv vector */
22int optopt = '?'; /* character checked for validity */
23int optreset; /* reset getopt */
24char *optarg; /* argument associated with option */
25#endif
26
27#define PRINT_ERROR ((opterr) && (*options != ':'))
28
29#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
30#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
31#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
32
33/* return values */
34#define BADCH (int)'?'
35#define BADARG ((*options == ':') ? (int)':' : (int)'?')
36#define INORDER (int)1
37
38#define EMSG ""
39
40static int getopt_internal(int, char *const *, const char *,
41 const struct option *, int *, int);
42static int parse_long_options(char *const *, const char *,
43 const struct option *, int *, int);
44static int gcd(int, int);
45static void permute_args(int, int, int, char *const *);
46
47static const char *place = EMSG; /* option letter processing */
48
49/* XXX: set optreset to 1 rather than these two */
50static int nonopt_start = -1; /* first non option argument (for permute) */
51static int nonopt_end = -1; /* first option after non options (for permute) */
52
53/*
54* Compute the greatest common divisor of a and b.
55*/
56static int gcd(int a, int b) {
57 int c;
58
59 c = a % b;
60 while (c != 0) {
61 a = b;
62 b = c;
63 c = a % b;
64 }
65
66 return (b);
67}
68
69static void pass() {}
70#define warnx(a, ...) pass();
71
72/*
73* Exchange the block from nonopt_start to nonopt_end with the block
74* from nonopt_end to opt_end (keeping the same order of arguments
75* in each block).
76*/
77static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
78 char *const *nargv) {
79 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
80 char *swap;
81
82 /*
83 * compute lengths of blocks and number and size of cycles
84 */
85 nnonopts = panonopt_end - panonopt_start;
86 nopts = opt_end - panonopt_end;
87 ncycle = gcd(nnonopts, nopts);
88 cyclelen = (opt_end - panonopt_start) / ncycle;
89
90 for (i = 0; i < ncycle; i++) {
91 cstart = panonopt_end + i;
92 pos = cstart;
93 for (j = 0; j < cyclelen; j++) {
94 if (pos >= panonopt_end)
95 pos -= nnonopts;
96 else
97 pos += nopts;
98 swap = nargv[pos];
99 /* LINTED const cast */
100 const_cast<char **>(nargv)[pos] = nargv[cstart];
101 /* LINTED const cast */
102 const_cast<char **>(nargv)[cstart] = swap;
103 }
104 }
105}
106
107/*
108* parse_long_options --
109* Parse long options in argc/argv argument vector.
110* Returns -1 if short_too is set and the option does not match long_options.
111*/
112static int parse_long_options(char *const *nargv, const char *options,
113 const struct option *long_options, int *idx,
114 int short_too) {
115 char *current_argv, *has_equal;
116 size_t current_argv_len;
117 int i, match;
118
119 current_argv = const_cast<char *>(place);
120 match = -1;
121
122 optind++;
123
124 if ((has_equal = strchr(current_argv, '=')) != NULL) {
125 /* argument found (--option=arg) */
126 current_argv_len = has_equal - current_argv;
127 has_equal++;
128 } else
129 current_argv_len = strlen(current_argv);
130
131 for (i = 0; long_options[i].name; i++) {
132 /* find matching long option */
133 if (strncmp(current_argv, long_options[i].name, current_argv_len))
134 continue;
135
136 if (strlen(long_options[i].name) == current_argv_len) {
137 /* exact match */
138 match = i;
139 break;
140 }
141 /*
142 * If this is a known short option, don't allow
143 * a partial match of a single character.
144 */
145 if (short_too && current_argv_len == 1)
146 continue;
147
148 if (match == -1) /* partial match */
149 match = i;
150 else {
151 /* ambiguous abbreviation */
152 if (PRINT_ERROR)
153 warnx(ambig, (int)current_argv_len, current_argv);
154 optopt = 0;
155 return (BADCH);
156 }
157 }
158 if (match != -1) { /* option found */
159 if (long_options[match].has_arg == no_argument && has_equal) {
160 if (PRINT_ERROR)
161 warnx(noarg, (int)current_argv_len, current_argv);
162 /*
163 * XXX: GNU sets optopt to val regardless of flag
164 */
165 if (long_options[match].flag == NULL)
166 optopt = long_options[match].val;
167 else
168 optopt = 0;
169 return (BADARG);
170 }
171 if (long_options[match].has_arg == required_argument ||
172 long_options[match].has_arg == optional_argument) {
173 if (has_equal)
174 optarg = has_equal;
175 else if (long_options[match].has_arg == required_argument) {
176 /*
177 * optional argument doesn't use next nargv
178 */
179 optarg = nargv[optind++];
180 }
181 }
182 if ((long_options[match].has_arg == required_argument) &&
183 (optarg == NULL)) {
184 /*
185 * Missing argument; leading ':' indicates no error
186 * should be generated.
187 */
188 if (PRINT_ERROR)
189 warnx(recargstring, current_argv);
190 /*
191 * XXX: GNU sets optopt to val regardless of flag
192 */
193 if (long_options[match].flag == NULL)
194 optopt = long_options[match].val;
195 else
196 optopt = 0;
197 --optind;
198 return (BADARG);
199 }
200 } else { /* unknown option */
201 if (short_too) {
202 --optind;
203 return (-1);
204 }
205 if (PRINT_ERROR)
206 warnx(illoptstring, current_argv);
207 optopt = 0;
208 return (BADCH);
209 }
210 if (idx)
211 *idx = match;
212 if (long_options[match].flag) {
213 *long_options[match].flag = long_options[match].val;
214 return (0);
215 } else
216 return (long_options[match].val);
217}
218
219/*
220* getopt_internal --
221* Parse argc/argv argument vector. Called by user level routines.
222*/
223static int getopt_internal(int nargc, char *const *nargv, const char *options,
224 const struct option *long_options, int *idx,
225 int flags) {
226 const char *oli; /* option letter list index */
227 int optchar, short_too;
228 static int posixly_correct = -1;
229
230 if (options == NULL)
231 return (-1);
232
233 /*
234 * XXX Some GNU programs (like cvs) set optind to 0 instead of
235 * XXX using optreset. Work around this braindamage.
236 */
237 if (optind == 0)
238 optind = optreset = 1;
239
240 /*
241 * Disable GNU extensions if POSIXLY_CORRECT is set or options
242 * string begins with a '+'.
243 */
244 if (posixly_correct == -1 || optreset)
245 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
246 if (*options == '-')
247 flags |= FLAG_ALLARGS;
248 else if (posixly_correct || *options == '+')
249 flags &= ~FLAG_PERMUTE;
250 if (*options == '+' || *options == '-')
251 options++;
252
253 optarg = NULL;
254 if (optreset)
255 nonopt_start = nonopt_end = -1;
256start:
257 if (optreset || !*place) { /* update scanning pointer */
258 optreset = 0;
259 if (optind >= nargc) { /* end of argument vector */
260 place = EMSG;
261 if (nonopt_end != -1) {
262 /* do permutation, if we have to */
263 permute_args(nonopt_start, nonopt_end, optind, nargv);
264 optind -= nonopt_end - nonopt_start;
265 } else if (nonopt_start != -1) {
266 /*
267 * If we skipped non-options, set optind
268 * to the first of them.
269 */
270 optind = nonopt_start;
271 }
272 nonopt_start = nonopt_end = -1;
273 return (-1);
274 }
275 if (*(place = nargv[optind]) != '-' ||
276 (place[1] == '\0' && strchr(options, '-') == NULL)) {
277 place = EMSG; /* found non-option */
278 if (flags & FLAG_ALLARGS) {
279 /*
280 * GNU extension:
281 * return non-option as argument to option 1
282 */
283 optarg = nargv[optind++];
284 return (INORDER);
285 }
286 if (!(flags & FLAG_PERMUTE)) {
287 /*
288 * If no permutation wanted, stop parsing
289 * at first non-option.
290 */
291 return (-1);
292 }
293 /* do permutation */
294 if (nonopt_start == -1)
295 nonopt_start = optind;
296 else if (nonopt_end != -1) {
297 permute_args(nonopt_start, nonopt_end, optind, nargv);
298 nonopt_start = optind - (nonopt_end - nonopt_start);
299 nonopt_end = -1;
300 }
301 optind++;
302 /* process next argument */
303 goto start;
304 }
305 if (nonopt_start != -1 && nonopt_end == -1)
306 nonopt_end = optind;
307
308 /*
309 * If we have "-" do nothing, if "--" we are done.
310 */
311 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
312 optind++;
313 place = EMSG;
314 /*
315 * We found an option (--), so if we skipped
316 * non-options, we have to permute.
317 */
318 if (nonopt_end != -1) {
319 permute_args(nonopt_start, nonopt_end, optind, nargv);
320 optind -= nonopt_end - nonopt_start;
321 }
322 nonopt_start = nonopt_end = -1;
323 return (-1);
324 }
325 }
326
327 /*
328 * Check long options if:
329 * 1) we were passed some
330 * 2) the arg is not just "-"
331 * 3) either the arg starts with -- we are getopt_long_only()
332 */
333 if (long_options != NULL && place != nargv[optind] &&
334 (*place == '-' || (flags & FLAG_LONGONLY))) {
335 short_too = 0;
336 if (*place == '-')
337 place++; /* --foo long option */
338 else if (*place != ':' && strchr(options, *place) != NULL)
339 short_too = 1; /* could be short option too */
340
341 optchar = parse_long_options(nargv, options, long_options, idx, short_too);
342 if (optchar != -1) {
343 place = EMSG;
344 return (optchar);
345 }
346 }
347
348 if ((optchar = (int)*place++) == (int)':' ||
349 (optchar == (int)'-' && *place != '\0') ||
350 (oli = strchr(options, optchar)) == NULL) {
351 /*
352 * If the user specified "-" and '-' isn't listed in
353 * options, return -1 (non-option) as per POSIX.
354 * Otherwise, it is an unknown option character (or ':').
355 */
356 if (optchar == (int)'-' && *place == '\0')
357 return (-1);
358 if (!*place)
359 ++optind;
360 if (PRINT_ERROR)
361 warnx(illoptchar, optchar);
362 optopt = optchar;
363 return (BADCH);
364 }
365 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
366 /* -W long-option */
367 if (*place) /* no space */
368 /* NOTHING */;
369 else if (++optind >= nargc) { /* no arg */
370 place = EMSG;
371 if (PRINT_ERROR)
372 warnx(recargchar, optchar);
373 optopt = optchar;
374 return (BADARG);
375 } else /* white space */
376 place = nargv[optind];
377 optchar = parse_long_options(nargv, options, long_options, idx, 0);
378 place = EMSG;
379 return (optchar);
380 }
381 if (*++oli != ':') { /* doesn't take argument */
382 if (!*place)
383 ++optind;
384 } else { /* takes (optional) argument */
385 optarg = NULL;
386 if (*place) /* no white space */
387 optarg = const_cast<char *>(place);
388 else if (oli[1] != ':') { /* arg not optional */
389 if (++optind >= nargc) { /* no arg */
390 place = EMSG;
391 if (PRINT_ERROR)
392 warnx(recargchar, optchar);
393 optopt = optchar;
394 return (BADARG);
395 } else
396 optarg = nargv[optind];
397 }
398 place = EMSG;
399 ++optind;
400 }
401 /* dump back option letter */
402 return (optchar);
403}
404
405/*
406* getopt --
407* Parse argc/argv argument vector.
408*
409* [eventually this will replace the BSD getopt]
410*/
411#if defined(REPLACE_GETOPT)
412int getopt(int nargc, char *const *nargv, const char *options) {
413
414 /*
415 * We don't pass FLAG_PERMUTE to getopt_internal() since
416 * the BSD getopt(3) (unlike GNU) has never done this.
417 *
418 * Furthermore, since many privileged programs call getopt()
419 * before dropping privileges it makes sense to keep things
420 * as simple (and bug-free) as possible.
421 */
422 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
423}
424#endif
425
426/*
427* getopt_long --
428* Parse argc/argv argument vector.
429*/
430#if defined(REPLACE_GETOPT_LONG)
431int getopt_long(int nargc, char *const *nargv, const char *options,
432 const struct option *long_options, int *idx) {
433 return (
434 getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));
435}
436#endif
437
438/*
439* getopt_long_only --
440* Parse argc/argv argument vector.
441*/
442#if defined(REPLACE_GETOPT_LONG_ONLY)
443int getopt_long_only(int nargc, char *const *nargv, const char *options,
444 const struct option *long_options, int *idx) {
445
446 return (getopt_internal(nargc, nargv, options, long_options, idx,
447 FLAG_PERMUTE | FLAG_LONGONLY));
448}
449#endif
450
451#endif
452

source code of lldb/source/Host/common/GetOptInc.cpp