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