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