xref: /openbsd-src/gnu/llvm/lldb/source/Host/common/GetOptInc.cpp (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1dda28197Spatrick //===-- GetOptInc.cpp -----------------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9061da546Spatrick #include "lldb/Host/common/GetOptInc.h"
10061da546Spatrick 
11061da546Spatrick #if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) ||                 \
12061da546Spatrick     defined(REPLACE_GETOPT_LONG_ONLY)
13061da546Spatrick 
14061da546Spatrick // getopt.cpp
15*be691f3bSpatrick #include <cerrno>
16*be691f3bSpatrick #include <cstdlib>
17*be691f3bSpatrick #include <cstring>
18061da546Spatrick 
19061da546Spatrick #if defined(REPLACE_GETOPT)
20061da546Spatrick int opterr = 1;   /* if error message should be printed */
21061da546Spatrick int optind = 1;   /* index into parent argv vector */
22061da546Spatrick int optopt = '?'; /* character checked for validity */
23061da546Spatrick int optreset;     /* reset getopt */
24061da546Spatrick char *optarg;     /* argument associated with option */
25061da546Spatrick #endif
26061da546Spatrick 
27061da546Spatrick #define PRINT_ERROR ((opterr) && (*options != ':'))
28061da546Spatrick 
29061da546Spatrick #define FLAG_PERMUTE 0x01  /* permute non-options to the end of argv */
30061da546Spatrick #define FLAG_ALLARGS 0x02  /* treat non-options as args to option "-1" */
31061da546Spatrick #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
32061da546Spatrick 
33061da546Spatrick /* return values */
34061da546Spatrick #define BADCH (int)'?'
35061da546Spatrick #define BADARG ((*options == ':') ? (int)':' : (int)'?')
36061da546Spatrick #define INORDER (int)1
37061da546Spatrick 
38061da546Spatrick #define EMSG ""
39061da546Spatrick 
40061da546Spatrick static int getopt_internal(int, char *const *, const char *,
41061da546Spatrick                            const struct option *, int *, int);
42061da546Spatrick static int parse_long_options(char *const *, const char *,
43061da546Spatrick                               const struct option *, int *, int);
44061da546Spatrick static int gcd(int, int);
45061da546Spatrick static void permute_args(int, int, int, char *const *);
46061da546Spatrick 
47061da546Spatrick static const char *place = EMSG; /* option letter processing */
48061da546Spatrick 
49061da546Spatrick /* XXX: set optreset to 1 rather than these two */
50061da546Spatrick static int nonopt_start = -1; /* first non option argument (for permute) */
51061da546Spatrick static int nonopt_end = -1;   /* first option after non options (for permute) */
52061da546Spatrick 
53061da546Spatrick /*
54061da546Spatrick * Compute the greatest common divisor of a and b.
55061da546Spatrick */
gcd(int a,int b)56061da546Spatrick static int gcd(int a, int b) {
57061da546Spatrick   int c;
58061da546Spatrick 
59061da546Spatrick   c = a % b;
60061da546Spatrick   while (c != 0) {
61061da546Spatrick     a = b;
62061da546Spatrick     b = c;
63061da546Spatrick     c = a % b;
64061da546Spatrick   }
65061da546Spatrick 
66061da546Spatrick   return (b);
67061da546Spatrick }
68061da546Spatrick 
pass()69061da546Spatrick static void pass() {}
70061da546Spatrick #define warnx(a, ...) pass();
71061da546Spatrick 
72061da546Spatrick /*
73061da546Spatrick * Exchange the block from nonopt_start to nonopt_end with the block
74061da546Spatrick * from nonopt_end to opt_end (keeping the same order of arguments
75061da546Spatrick * in each block).
76061da546Spatrick */
permute_args(int panonopt_start,int panonopt_end,int opt_end,char * const * nargv)77061da546Spatrick static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
78061da546Spatrick                          char *const *nargv) {
79061da546Spatrick   int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
80061da546Spatrick   char *swap;
81061da546Spatrick 
82061da546Spatrick   /*
83061da546Spatrick   * compute lengths of blocks and number and size of cycles
84061da546Spatrick   */
85061da546Spatrick   nnonopts = panonopt_end - panonopt_start;
86061da546Spatrick   nopts = opt_end - panonopt_end;
87061da546Spatrick   ncycle = gcd(nnonopts, nopts);
88061da546Spatrick   cyclelen = (opt_end - panonopt_start) / ncycle;
89061da546Spatrick 
90061da546Spatrick   for (i = 0; i < ncycle; i++) {
91061da546Spatrick     cstart = panonopt_end + i;
92061da546Spatrick     pos = cstart;
93061da546Spatrick     for (j = 0; j < cyclelen; j++) {
94061da546Spatrick       if (pos >= panonopt_end)
95061da546Spatrick         pos -= nnonopts;
96061da546Spatrick       else
97061da546Spatrick         pos += nopts;
98061da546Spatrick       swap = nargv[pos];
99061da546Spatrick       /* LINTED const cast */
100061da546Spatrick       const_cast<char **>(nargv)[pos] = nargv[cstart];
101061da546Spatrick       /* LINTED const cast */
102061da546Spatrick       const_cast<char **>(nargv)[cstart] = swap;
103061da546Spatrick     }
104061da546Spatrick   }
105061da546Spatrick }
106061da546Spatrick 
107061da546Spatrick /*
108061da546Spatrick * parse_long_options --
109061da546Spatrick *  Parse long options in argc/argv argument vector.
110061da546Spatrick * Returns -1 if short_too is set and the option does not match long_options.
111061da546Spatrick */
parse_long_options(char * const * nargv,const char * options,const struct option * long_options,int * idx,int short_too)112061da546Spatrick static int parse_long_options(char *const *nargv, const char *options,
113061da546Spatrick                               const struct option *long_options, int *idx,
114061da546Spatrick                               int short_too) {
115061da546Spatrick   char *current_argv, *has_equal;
116061da546Spatrick   size_t current_argv_len;
117061da546Spatrick   int i, match;
118061da546Spatrick 
119061da546Spatrick   current_argv = const_cast<char *>(place);
120061da546Spatrick   match = -1;
121061da546Spatrick 
122061da546Spatrick   optind++;
123061da546Spatrick 
124061da546Spatrick   if ((has_equal = strchr(current_argv, '=')) != NULL) {
125061da546Spatrick     /* argument found (--option=arg) */
126061da546Spatrick     current_argv_len = has_equal - current_argv;
127061da546Spatrick     has_equal++;
128061da546Spatrick   } else
129061da546Spatrick     current_argv_len = strlen(current_argv);
130061da546Spatrick 
131061da546Spatrick   for (i = 0; long_options[i].name; i++) {
132061da546Spatrick     /* find matching long option */
133061da546Spatrick     if (strncmp(current_argv, long_options[i].name, current_argv_len))
134061da546Spatrick       continue;
135061da546Spatrick 
136061da546Spatrick     if (strlen(long_options[i].name) == current_argv_len) {
137061da546Spatrick       /* exact match */
138061da546Spatrick       match = i;
139061da546Spatrick       break;
140061da546Spatrick     }
141061da546Spatrick     /*
142061da546Spatrick     * If this is a known short option, don't allow
143061da546Spatrick     * a partial match of a single character.
144061da546Spatrick     */
145061da546Spatrick     if (short_too && current_argv_len == 1)
146061da546Spatrick       continue;
147061da546Spatrick 
148061da546Spatrick     if (match == -1) /* partial match */
149061da546Spatrick       match = i;
150061da546Spatrick     else {
151061da546Spatrick       /* ambiguous abbreviation */
152061da546Spatrick       if (PRINT_ERROR)
153061da546Spatrick         warnx(ambig, (int)current_argv_len, current_argv);
154061da546Spatrick       optopt = 0;
155061da546Spatrick       return (BADCH);
156061da546Spatrick     }
157061da546Spatrick   }
158061da546Spatrick   if (match != -1) { /* option found */
159061da546Spatrick     if (long_options[match].has_arg == no_argument && has_equal) {
160061da546Spatrick       if (PRINT_ERROR)
161061da546Spatrick         warnx(noarg, (int)current_argv_len, current_argv);
162061da546Spatrick       /*
163061da546Spatrick       * XXX: GNU sets optopt to val regardless of flag
164061da546Spatrick       */
165061da546Spatrick       if (long_options[match].flag == NULL)
166061da546Spatrick         optopt = long_options[match].val;
167061da546Spatrick       else
168061da546Spatrick         optopt = 0;
169061da546Spatrick       return (BADARG);
170061da546Spatrick     }
171061da546Spatrick     if (long_options[match].has_arg == required_argument ||
172061da546Spatrick         long_options[match].has_arg == optional_argument) {
173061da546Spatrick       if (has_equal)
174061da546Spatrick         optarg = has_equal;
175061da546Spatrick       else if (long_options[match].has_arg == required_argument) {
176061da546Spatrick         /*
177061da546Spatrick         * optional argument doesn't use next nargv
178061da546Spatrick         */
179061da546Spatrick         optarg = nargv[optind++];
180061da546Spatrick       }
181061da546Spatrick     }
182061da546Spatrick     if ((long_options[match].has_arg == required_argument) &&
183061da546Spatrick         (optarg == NULL)) {
184061da546Spatrick       /*
185061da546Spatrick       * Missing argument; leading ':' indicates no error
186061da546Spatrick       * should be generated.
187061da546Spatrick       */
188061da546Spatrick       if (PRINT_ERROR)
189061da546Spatrick         warnx(recargstring, current_argv);
190061da546Spatrick       /*
191061da546Spatrick       * XXX: GNU sets optopt to val regardless of flag
192061da546Spatrick       */
193061da546Spatrick       if (long_options[match].flag == NULL)
194061da546Spatrick         optopt = long_options[match].val;
195061da546Spatrick       else
196061da546Spatrick         optopt = 0;
197061da546Spatrick       --optind;
198061da546Spatrick       return (BADARG);
199061da546Spatrick     }
200061da546Spatrick   } else { /* unknown option */
201061da546Spatrick     if (short_too) {
202061da546Spatrick       --optind;
203061da546Spatrick       return (-1);
204061da546Spatrick     }
205061da546Spatrick     if (PRINT_ERROR)
206061da546Spatrick       warnx(illoptstring, current_argv);
207061da546Spatrick     optopt = 0;
208061da546Spatrick     return (BADCH);
209061da546Spatrick   }
210061da546Spatrick   if (idx)
211061da546Spatrick     *idx = match;
212061da546Spatrick   if (long_options[match].flag) {
213061da546Spatrick     *long_options[match].flag = long_options[match].val;
214061da546Spatrick     return (0);
215061da546Spatrick   } else
216061da546Spatrick     return (long_options[match].val);
217061da546Spatrick }
218061da546Spatrick 
219061da546Spatrick /*
220061da546Spatrick * getopt_internal --
221061da546Spatrick *  Parse argc/argv argument vector.  Called by user level routines.
222061da546Spatrick */
getopt_internal(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx,int flags)223061da546Spatrick static int getopt_internal(int nargc, char *const *nargv, const char *options,
224061da546Spatrick                            const struct option *long_options, int *idx,
225061da546Spatrick                            int flags) {
226061da546Spatrick   const char *oli; /* option letter list index */
227061da546Spatrick   int optchar, short_too;
228061da546Spatrick   static int posixly_correct = -1;
229061da546Spatrick 
230061da546Spatrick   if (options == NULL)
231061da546Spatrick     return (-1);
232061da546Spatrick 
233061da546Spatrick   /*
234061da546Spatrick   * XXX Some GNU programs (like cvs) set optind to 0 instead of
235061da546Spatrick   * XXX using optreset.  Work around this braindamage.
236061da546Spatrick   */
237061da546Spatrick   if (optind == 0)
238061da546Spatrick     optind = optreset = 1;
239061da546Spatrick 
240061da546Spatrick   /*
241061da546Spatrick   * Disable GNU extensions if POSIXLY_CORRECT is set or options
242061da546Spatrick   * string begins with a '+'.
243061da546Spatrick   */
244061da546Spatrick   if (posixly_correct == -1 || optreset)
245061da546Spatrick     posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
246061da546Spatrick   if (*options == '-')
247061da546Spatrick     flags |= FLAG_ALLARGS;
248061da546Spatrick   else if (posixly_correct || *options == '+')
249061da546Spatrick     flags &= ~FLAG_PERMUTE;
250061da546Spatrick   if (*options == '+' || *options == '-')
251061da546Spatrick     options++;
252061da546Spatrick 
253061da546Spatrick   optarg = NULL;
254061da546Spatrick   if (optreset)
255061da546Spatrick     nonopt_start = nonopt_end = -1;
256061da546Spatrick start:
257061da546Spatrick   if (optreset || !*place) { /* update scanning pointer */
258061da546Spatrick     optreset = 0;
259061da546Spatrick     if (optind >= nargc) { /* end of argument vector */
260061da546Spatrick       place = EMSG;
261061da546Spatrick       if (nonopt_end != -1) {
262061da546Spatrick         /* do permutation, if we have to */
263061da546Spatrick         permute_args(nonopt_start, nonopt_end, optind, nargv);
264061da546Spatrick         optind -= nonopt_end - nonopt_start;
265061da546Spatrick       } else if (nonopt_start != -1) {
266061da546Spatrick         /*
267061da546Spatrick         * If we skipped non-options, set optind
268061da546Spatrick         * to the first of them.
269061da546Spatrick         */
270061da546Spatrick         optind = nonopt_start;
271061da546Spatrick       }
272061da546Spatrick       nonopt_start = nonopt_end = -1;
273061da546Spatrick       return (-1);
274061da546Spatrick     }
275061da546Spatrick     if (*(place = nargv[optind]) != '-' ||
276061da546Spatrick         (place[1] == '\0' && strchr(options, '-') == NULL)) {
277061da546Spatrick       place = EMSG; /* found non-option */
278061da546Spatrick       if (flags & FLAG_ALLARGS) {
279061da546Spatrick         /*
280061da546Spatrick         * GNU extension:
281061da546Spatrick         * return non-option as argument to option 1
282061da546Spatrick         */
283061da546Spatrick         optarg = nargv[optind++];
284061da546Spatrick         return (INORDER);
285061da546Spatrick       }
286061da546Spatrick       if (!(flags & FLAG_PERMUTE)) {
287061da546Spatrick         /*
288061da546Spatrick         * If no permutation wanted, stop parsing
289061da546Spatrick         * at first non-option.
290061da546Spatrick         */
291061da546Spatrick         return (-1);
292061da546Spatrick       }
293061da546Spatrick       /* do permutation */
294061da546Spatrick       if (nonopt_start == -1)
295061da546Spatrick         nonopt_start = optind;
296061da546Spatrick       else if (nonopt_end != -1) {
297061da546Spatrick         permute_args(nonopt_start, nonopt_end, optind, nargv);
298061da546Spatrick         nonopt_start = optind - (nonopt_end - nonopt_start);
299061da546Spatrick         nonopt_end = -1;
300061da546Spatrick       }
301061da546Spatrick       optind++;
302061da546Spatrick       /* process next argument */
303061da546Spatrick       goto start;
304061da546Spatrick     }
305061da546Spatrick     if (nonopt_start != -1 && nonopt_end == -1)
306061da546Spatrick       nonopt_end = optind;
307061da546Spatrick 
308061da546Spatrick     /*
309061da546Spatrick     * If we have "-" do nothing, if "--" we are done.
310061da546Spatrick     */
311061da546Spatrick     if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
312061da546Spatrick       optind++;
313061da546Spatrick       place = EMSG;
314061da546Spatrick       /*
315061da546Spatrick       * We found an option (--), so if we skipped
316061da546Spatrick       * non-options, we have to permute.
317061da546Spatrick       */
318061da546Spatrick       if (nonopt_end != -1) {
319061da546Spatrick         permute_args(nonopt_start, nonopt_end, optind, nargv);
320061da546Spatrick         optind -= nonopt_end - nonopt_start;
321061da546Spatrick       }
322061da546Spatrick       nonopt_start = nonopt_end = -1;
323061da546Spatrick       return (-1);
324061da546Spatrick     }
325061da546Spatrick   }
326061da546Spatrick 
327061da546Spatrick   /*
328061da546Spatrick   * Check long options if:
329061da546Spatrick   *  1) we were passed some
330061da546Spatrick   *  2) the arg is not just "-"
331061da546Spatrick   *  3) either the arg starts with -- we are getopt_long_only()
332061da546Spatrick   */
333061da546Spatrick   if (long_options != NULL && place != nargv[optind] &&
334061da546Spatrick       (*place == '-' || (flags & FLAG_LONGONLY))) {
335061da546Spatrick     short_too = 0;
336061da546Spatrick     if (*place == '-')
337061da546Spatrick       place++; /* --foo long option */
338061da546Spatrick     else if (*place != ':' && strchr(options, *place) != NULL)
339061da546Spatrick       short_too = 1; /* could be short option too */
340061da546Spatrick 
341061da546Spatrick     optchar = parse_long_options(nargv, options, long_options, idx, short_too);
342061da546Spatrick     if (optchar != -1) {
343061da546Spatrick       place = EMSG;
344061da546Spatrick       return (optchar);
345061da546Spatrick     }
346061da546Spatrick   }
347061da546Spatrick 
348061da546Spatrick   if ((optchar = (int)*place++) == (int)':' ||
349061da546Spatrick       (optchar == (int)'-' && *place != '\0') ||
350061da546Spatrick       (oli = strchr(options, optchar)) == NULL) {
351061da546Spatrick     /*
352061da546Spatrick     * If the user specified "-" and  '-' isn't listed in
353061da546Spatrick     * options, return -1 (non-option) as per POSIX.
354061da546Spatrick     * Otherwise, it is an unknown option character (or ':').
355061da546Spatrick     */
356061da546Spatrick     if (optchar == (int)'-' && *place == '\0')
357061da546Spatrick       return (-1);
358061da546Spatrick     if (!*place)
359061da546Spatrick       ++optind;
360061da546Spatrick     if (PRINT_ERROR)
361061da546Spatrick       warnx(illoptchar, optchar);
362061da546Spatrick     optopt = optchar;
363061da546Spatrick     return (BADCH);
364061da546Spatrick   }
365061da546Spatrick   if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
366061da546Spatrick     /* -W long-option */
367061da546Spatrick     if (*place) /* no space */
368061da546Spatrick       /* NOTHING */;
369061da546Spatrick     else if (++optind >= nargc) { /* no arg */
370061da546Spatrick       place = EMSG;
371061da546Spatrick       if (PRINT_ERROR)
372061da546Spatrick         warnx(recargchar, optchar);
373061da546Spatrick       optopt = optchar;
374061da546Spatrick       return (BADARG);
375061da546Spatrick     } else /* white space */
376061da546Spatrick       place = nargv[optind];
377061da546Spatrick     optchar = parse_long_options(nargv, options, long_options, idx, 0);
378061da546Spatrick     place = EMSG;
379061da546Spatrick     return (optchar);
380061da546Spatrick   }
381061da546Spatrick   if (*++oli != ':') { /* doesn't take argument */
382061da546Spatrick     if (!*place)
383061da546Spatrick       ++optind;
384061da546Spatrick   } else { /* takes (optional) argument */
385061da546Spatrick     optarg = NULL;
386061da546Spatrick     if (*place) /* no white space */
387061da546Spatrick       optarg = const_cast<char *>(place);
388061da546Spatrick     else if (oli[1] != ':') {  /* arg not optional */
389061da546Spatrick       if (++optind >= nargc) { /* no arg */
390061da546Spatrick         place = EMSG;
391061da546Spatrick         if (PRINT_ERROR)
392061da546Spatrick           warnx(recargchar, optchar);
393061da546Spatrick         optopt = optchar;
394061da546Spatrick         return (BADARG);
395061da546Spatrick       } else
396061da546Spatrick         optarg = nargv[optind];
397061da546Spatrick     }
398061da546Spatrick     place = EMSG;
399061da546Spatrick     ++optind;
400061da546Spatrick   }
401061da546Spatrick   /* dump back option letter */
402061da546Spatrick   return (optchar);
403061da546Spatrick }
404061da546Spatrick 
405061da546Spatrick /*
406061da546Spatrick * getopt --
407061da546Spatrick *  Parse argc/argv argument vector.
408061da546Spatrick *
409061da546Spatrick * [eventually this will replace the BSD getopt]
410061da546Spatrick */
411061da546Spatrick #if defined(REPLACE_GETOPT)
getopt(int nargc,char * const * nargv,const char * options)412061da546Spatrick int getopt(int nargc, char *const *nargv, const char *options) {
413061da546Spatrick 
414061da546Spatrick   /*
415061da546Spatrick   * We don't pass FLAG_PERMUTE to getopt_internal() since
416061da546Spatrick   * the BSD getopt(3) (unlike GNU) has never done this.
417061da546Spatrick   *
418061da546Spatrick   * Furthermore, since many privileged programs call getopt()
419061da546Spatrick   * before dropping privileges it makes sense to keep things
420061da546Spatrick   * as simple (and bug-free) as possible.
421061da546Spatrick   */
422061da546Spatrick   return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
423061da546Spatrick }
424061da546Spatrick #endif
425061da546Spatrick 
426061da546Spatrick /*
427061da546Spatrick * getopt_long --
428061da546Spatrick *  Parse argc/argv argument vector.
429061da546Spatrick */
430061da546Spatrick #if defined(REPLACE_GETOPT_LONG)
getopt_long(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx)431061da546Spatrick int getopt_long(int nargc, char *const *nargv, const char *options,
432061da546Spatrick                 const struct option *long_options, int *idx) {
433061da546Spatrick   return (
434061da546Spatrick       getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));
435061da546Spatrick }
436061da546Spatrick #endif
437061da546Spatrick 
438061da546Spatrick /*
439061da546Spatrick * getopt_long_only --
440061da546Spatrick *  Parse argc/argv argument vector.
441061da546Spatrick */
442061da546Spatrick #if defined(REPLACE_GETOPT_LONG_ONLY)
getopt_long_only(int nargc,char * const * nargv,const char * options,const struct option * long_options,int * idx)443061da546Spatrick int getopt_long_only(int nargc, char *const *nargv, const char *options,
444061da546Spatrick                      const struct option *long_options, int *idx) {
445061da546Spatrick 
446061da546Spatrick   return (getopt_internal(nargc, nargv, options, long_options, idx,
447061da546Spatrick                           FLAG_PERMUTE | FLAG_LONGONLY));
448061da546Spatrick }
449061da546Spatrick #endif
450061da546Spatrick 
451061da546Spatrick #endif
452