GetOptInc.cpp revision 292932
1292932Sdim#include "lldb/Host/common/GetOptInc.h"
2292932Sdim
3292932Sdim#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || defined(REPLACE_GETOPT_LONG_ONLY)
4292932Sdim
5292932Sdim// getopt.cpp
6292932Sdim#include <errno.h>
7292932Sdim#include <stdlib.h>
8292932Sdim#include <string.h>
9292932Sdim
10292932Sdim#if defined(REPLACE_GETOPT)
11292932Sdimint opterr = 1;     /* if error message should be printed */
12292932Sdimint optind = 1;     /* index into parent argv vector */
13292932Sdimint optopt = '?';   /* character checked for validity */
14292932Sdimint optreset;       /* reset getopt */
15292932Sdimchar *optarg;       /* argument associated with option */
16292932Sdim#endif
17292932Sdim
18292932Sdim#define PRINT_ERROR ((opterr) && (*options != ':'))
19292932Sdim
20292932Sdim#define FLAG_PERMUTE    0x01    /* permute non-options to the end of argv */
21292932Sdim#define FLAG_ALLARGS    0x02    /* treat non-options as args to option "-1" */
22292932Sdim#define FLAG_LONGONLY   0x04    /* operate as getopt_long_only */
23292932Sdim
24292932Sdim/* return values */
25292932Sdim#define BADCH       (int)'?'
26292932Sdim#define BADARG      ((*options == ':') ? (int)':' : (int)'?')
27292932Sdim#define INORDER     (int)1
28292932Sdim
29292932Sdim#define EMSG        ""
30292932Sdim
31292932Sdimstatic int getopt_internal(int, char * const *, const char *,
32292932Sdim    const struct option *, int *, int);
33292932Sdimstatic int parse_long_options(char * const *, const char *,
34292932Sdim    const struct option *, int *, int);
35292932Sdimstatic int gcd(int, int);
36292932Sdimstatic void permute_args(int, int, int, char * const *);
37292932Sdim
38292932Sdimstatic const char *place = EMSG; /* option letter processing */
39292932Sdim
40292932Sdim/* XXX: set optreset to 1 rather than these two */
41292932Sdimstatic int nonopt_start = -1; /* first non option argument (for permute) */
42292932Sdimstatic int nonopt_end = -1;   /* first option after non options (for permute) */
43292932Sdim
44292932Sdim/*
45292932Sdim* Compute the greatest common divisor of a and b.
46292932Sdim*/
47292932Sdimstatic int
48292932Sdimgcd(int a, int b)
49292932Sdim{
50292932Sdim    int c;
51292932Sdim
52292932Sdim    c = a % b;
53292932Sdim    while (c != 0) {
54292932Sdim        a = b;
55292932Sdim        b = c;
56292932Sdim        c = a % b;
57292932Sdim    }
58292932Sdim
59292932Sdim    return (b);
60292932Sdim}
61292932Sdim
62292932Sdimstatic void pass() {}
63292932Sdim#define warnx(a, ...) pass();
64292932Sdim
65292932Sdim/*
66292932Sdim* Exchange the block from nonopt_start to nonopt_end with the block
67292932Sdim* from nonopt_end to opt_end (keeping the same order of arguments
68292932Sdim* in each block).
69292932Sdim*/
70292932Sdimstatic void
71292932Sdimpermute_args(int panonopt_start, int panonopt_end, int opt_end,
72292932Sdimchar * const *nargv)
73292932Sdim{
74292932Sdim    int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
75292932Sdim    char *swap;
76292932Sdim
77292932Sdim    /*
78292932Sdim    * compute lengths of blocks and number and size of cycles
79292932Sdim    */
80292932Sdim    nnonopts = panonopt_end - panonopt_start;
81292932Sdim    nopts = opt_end - panonopt_end;
82292932Sdim    ncycle = gcd(nnonopts, nopts);
83292932Sdim    cyclelen = (opt_end - panonopt_start) / ncycle;
84292932Sdim
85292932Sdim    for (i = 0; i < ncycle; i++) {
86292932Sdim        cstart = panonopt_end + i;
87292932Sdim        pos = cstart;
88292932Sdim        for (j = 0; j < cyclelen; j++) {
89292932Sdim            if (pos >= panonopt_end)
90292932Sdim                pos -= nnonopts;
91292932Sdim            else
92292932Sdim                pos += nopts;
93292932Sdim            swap = nargv[pos];
94292932Sdim            /* LINTED const cast */
95292932Sdim            ((char **)nargv)[pos] = nargv[cstart];
96292932Sdim            /* LINTED const cast */
97292932Sdim            ((char **)nargv)[cstart] = swap;
98292932Sdim        }
99292932Sdim    }
100292932Sdim}
101292932Sdim
102292932Sdim/*
103292932Sdim* parse_long_options --
104292932Sdim*  Parse long options in argc/argv argument vector.
105292932Sdim* Returns -1 if short_too is set and the option does not match long_options.
106292932Sdim*/
107292932Sdimstatic int
108292932Sdimparse_long_options(char * const *nargv, const char *options,
109292932Sdimconst struct option *long_options, int *idx, int short_too)
110292932Sdim{
111292932Sdim    char *current_argv, *has_equal;
112292932Sdim    size_t current_argv_len;
113292932Sdim    int i, match;
114292932Sdim
115292932Sdim    current_argv = const_cast<char*>(place);
116292932Sdim    match = -1;
117292932Sdim
118292932Sdim    optind++;
119292932Sdim
120292932Sdim    if ((has_equal = strchr(current_argv, '=')) != NULL) {
121292932Sdim        /* argument found (--option=arg) */
122292932Sdim        current_argv_len = has_equal - current_argv;
123292932Sdim        has_equal++;
124292932Sdim    }
125292932Sdim    else
126292932Sdim        current_argv_len = strlen(current_argv);
127292932Sdim
128292932Sdim    for (i = 0; long_options[i].name; i++) {
129292932Sdim        /* find matching long option */
130292932Sdim        if (strncmp(current_argv, long_options[i].name,
131292932Sdim            current_argv_len))
132292932Sdim            continue;
133292932Sdim
134292932Sdim        if (strlen(long_options[i].name) == current_argv_len) {
135292932Sdim            /* exact match */
136292932Sdim            match = i;
137292932Sdim            break;
138292932Sdim        }
139292932Sdim        /*
140292932Sdim        * If this is a known short option, don't allow
141292932Sdim        * a partial match of a single character.
142292932Sdim        */
143292932Sdim        if (short_too && current_argv_len == 1)
144292932Sdim            continue;
145292932Sdim
146292932Sdim        if (match == -1)    /* partial match */
147292932Sdim            match = i;
148292932Sdim        else {
149292932Sdim            /* ambiguous abbreviation */
150292932Sdim            if (PRINT_ERROR)
151292932Sdim                warnx(ambig, (int)current_argv_len,
152292932Sdim                current_argv);
153292932Sdim            optopt = 0;
154292932Sdim            return (BADCH);
155292932Sdim        }
156292932Sdim    }
157292932Sdim    if (match != -1) {      /* option found */
158292932Sdim        if (long_options[match].has_arg == no_argument
159292932Sdim            && has_equal) {
160292932Sdim            if (PRINT_ERROR)
161292932Sdim                warnx(noarg, (int)current_argv_len,
162292932Sdim                current_argv);
163292932Sdim            /*
164292932Sdim            * XXX: GNU sets optopt to val regardless of flag
165292932Sdim            */
166292932Sdim            if (long_options[match].flag == NULL)
167292932Sdim                optopt = long_options[match].val;
168292932Sdim            else
169292932Sdim                optopt = 0;
170292932Sdim            return (BADARG);
171292932Sdim        }
172292932Sdim        if (long_options[match].has_arg == required_argument ||
173292932Sdim            long_options[match].has_arg == optional_argument) {
174292932Sdim            if (has_equal)
175292932Sdim                optarg = has_equal;
176292932Sdim            else if (long_options[match].has_arg ==
177292932Sdim                required_argument) {
178292932Sdim                /*
179292932Sdim                * optional argument doesn't use next nargv
180292932Sdim                */
181292932Sdim                optarg = nargv[optind++];
182292932Sdim            }
183292932Sdim        }
184292932Sdim        if ((long_options[match].has_arg == required_argument)
185292932Sdim            && (optarg == NULL)) {
186292932Sdim            /*
187292932Sdim            * Missing argument; leading ':' indicates no error
188292932Sdim            * should be generated.
189292932Sdim            */
190292932Sdim            if (PRINT_ERROR)
191292932Sdim                warnx(recargstring,
192292932Sdim                current_argv);
193292932Sdim            /*
194292932Sdim            * XXX: GNU sets optopt to val regardless of flag
195292932Sdim            */
196292932Sdim            if (long_options[match].flag == NULL)
197292932Sdim                optopt = long_options[match].val;
198292932Sdim            else
199292932Sdim                optopt = 0;
200292932Sdim            --optind;
201292932Sdim            return (BADARG);
202292932Sdim        }
203292932Sdim    }
204292932Sdim    else {            /* unknown option */
205292932Sdim        if (short_too) {
206292932Sdim            --optind;
207292932Sdim            return (-1);
208292932Sdim        }
209292932Sdim        if (PRINT_ERROR)
210292932Sdim            warnx(illoptstring, current_argv);
211292932Sdim        optopt = 0;
212292932Sdim        return (BADCH);
213292932Sdim    }
214292932Sdim    if (idx)
215292932Sdim        *idx = match;
216292932Sdim    if (long_options[match].flag) {
217292932Sdim        *long_options[match].flag = long_options[match].val;
218292932Sdim        return (0);
219292932Sdim    }
220292932Sdim    else
221292932Sdim        return (long_options[match].val);
222292932Sdim}
223292932Sdim
224292932Sdim/*
225292932Sdim* getopt_internal --
226292932Sdim*  Parse argc/argv argument vector.  Called by user level routines.
227292932Sdim*/
228292932Sdimstatic int
229292932Sdimgetopt_internal(int nargc, char * const *nargv, const char *options,
230292932Sdimconst struct option *long_options, int *idx, int flags)
231292932Sdim{
232292932Sdim    const char *oli;                /* option letter list index */
233292932Sdim    int optchar, short_too;
234292932Sdim    static int posixly_correct = -1;
235292932Sdim
236292932Sdim    if (options == NULL)
237292932Sdim        return (-1);
238292932Sdim
239292932Sdim    /*
240292932Sdim    * XXX Some GNU programs (like cvs) set optind to 0 instead of
241292932Sdim    * XXX using optreset.  Work around this braindamage.
242292932Sdim    */
243292932Sdim    if (optind == 0)
244292932Sdim        optind = optreset = 1;
245292932Sdim
246292932Sdim    /*
247292932Sdim    * Disable GNU extensions if POSIXLY_CORRECT is set or options
248292932Sdim    * string begins with a '+'.
249292932Sdim    */
250292932Sdim    if (posixly_correct == -1 || optreset)
251292932Sdim        posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
252292932Sdim    if (*options == '-')
253292932Sdim        flags |= FLAG_ALLARGS;
254292932Sdim    else if (posixly_correct || *options == '+')
255292932Sdim        flags &= ~FLAG_PERMUTE;
256292932Sdim    if (*options == '+' || *options == '-')
257292932Sdim        options++;
258292932Sdim
259292932Sdim    optarg = NULL;
260292932Sdim    if (optreset)
261292932Sdim        nonopt_start = nonopt_end = -1;
262292932Sdimstart:
263292932Sdim    if (optreset || !*place) {      /* update scanning pointer */
264292932Sdim        optreset = 0;
265292932Sdim        if (optind >= nargc) {          /* end of argument vector */
266292932Sdim            place = EMSG;
267292932Sdim            if (nonopt_end != -1) {
268292932Sdim                /* do permutation, if we have to */
269292932Sdim                permute_args(nonopt_start, nonopt_end,
270292932Sdim                    optind, nargv);
271292932Sdim                optind -= nonopt_end - nonopt_start;
272292932Sdim            }
273292932Sdim            else if (nonopt_start != -1) {
274292932Sdim                /*
275292932Sdim                * If we skipped non-options, set optind
276292932Sdim                * to the first of them.
277292932Sdim                */
278292932Sdim                optind = nonopt_start;
279292932Sdim            }
280292932Sdim            nonopt_start = nonopt_end = -1;
281292932Sdim            return (-1);
282292932Sdim        }
283292932Sdim        if (*(place = nargv[optind]) != '-' ||
284292932Sdim            (place[1] == '\0' && strchr(options, '-') == NULL)) {
285292932Sdim            place = EMSG;       /* found non-option */
286292932Sdim            if (flags & FLAG_ALLARGS) {
287292932Sdim                /*
288292932Sdim                * GNU extension:
289292932Sdim                * return non-option as argument to option 1
290292932Sdim                */
291292932Sdim                optarg = nargv[optind++];
292292932Sdim                return (INORDER);
293292932Sdim            }
294292932Sdim            if (!(flags & FLAG_PERMUTE)) {
295292932Sdim                /*
296292932Sdim                * If no permutation wanted, stop parsing
297292932Sdim                * at first non-option.
298292932Sdim                */
299292932Sdim                return (-1);
300292932Sdim            }
301292932Sdim            /* do permutation */
302292932Sdim            if (nonopt_start == -1)
303292932Sdim                nonopt_start = optind;
304292932Sdim            else if (nonopt_end != -1) {
305292932Sdim                permute_args(nonopt_start, nonopt_end,
306292932Sdim                    optind, nargv);
307292932Sdim                nonopt_start = optind -
308292932Sdim                    (nonopt_end - nonopt_start);
309292932Sdim                nonopt_end = -1;
310292932Sdim            }
311292932Sdim            optind++;
312292932Sdim            /* process next argument */
313292932Sdim            goto start;
314292932Sdim        }
315292932Sdim        if (nonopt_start != -1 && nonopt_end == -1)
316292932Sdim            nonopt_end = optind;
317292932Sdim
318292932Sdim        /*
319292932Sdim        * If we have "-" do nothing, if "--" we are done.
320292932Sdim        */
321292932Sdim        if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
322292932Sdim            optind++;
323292932Sdim            place = EMSG;
324292932Sdim            /*
325292932Sdim            * We found an option (--), so if we skipped
326292932Sdim            * non-options, we have to permute.
327292932Sdim            */
328292932Sdim            if (nonopt_end != -1) {
329292932Sdim                permute_args(nonopt_start, nonopt_end,
330292932Sdim                    optind, nargv);
331292932Sdim                optind -= nonopt_end - nonopt_start;
332292932Sdim            }
333292932Sdim            nonopt_start = nonopt_end = -1;
334292932Sdim            return (-1);
335292932Sdim        }
336292932Sdim    }
337292932Sdim
338292932Sdim    /*
339292932Sdim    * Check long options if:
340292932Sdim    *  1) we were passed some
341292932Sdim    *  2) the arg is not just "-"
342292932Sdim    *  3) either the arg starts with -- we are getopt_long_only()
343292932Sdim    */
344292932Sdim    if (long_options != NULL && place != nargv[optind] &&
345292932Sdim        (*place == '-' || (flags & FLAG_LONGONLY))) {
346292932Sdim        short_too = 0;
347292932Sdim        if (*place == '-')
348292932Sdim            place++;        /* --foo long option */
349292932Sdim        else if (*place != ':' && strchr(options, *place) != NULL)
350292932Sdim            short_too = 1;      /* could be short option too */
351292932Sdim
352292932Sdim        optchar = parse_long_options(nargv, options, long_options,
353292932Sdim            idx, short_too);
354292932Sdim        if (optchar != -1) {
355292932Sdim            place = EMSG;
356292932Sdim            return (optchar);
357292932Sdim        }
358292932Sdim    }
359292932Sdim
360292932Sdim    if ((optchar = (int)*place++) == (int)':' ||
361292932Sdim        (optchar == (int)'-' && *place != '\0') ||
362292932Sdim        (oli = strchr(options, optchar)) == NULL) {
363292932Sdim        /*
364292932Sdim        * If the user specified "-" and  '-' isn't listed in
365292932Sdim        * options, return -1 (non-option) as per POSIX.
366292932Sdim        * Otherwise, it is an unknown option character (or ':').
367292932Sdim        */
368292932Sdim        if (optchar == (int)'-' && *place == '\0')
369292932Sdim            return (-1);
370292932Sdim        if (!*place)
371292932Sdim            ++optind;
372292932Sdim        if (PRINT_ERROR)
373292932Sdim            warnx(illoptchar, optchar);
374292932Sdim        optopt = optchar;
375292932Sdim        return (BADCH);
376292932Sdim    }
377292932Sdim    if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
378292932Sdim        /* -W long-option */
379292932Sdim        if (*place)         /* no space */
380292932Sdim            /* NOTHING */;
381292932Sdim        else if (++optind >= nargc) {   /* no arg */
382292932Sdim            place = EMSG;
383292932Sdim            if (PRINT_ERROR)
384292932Sdim                warnx(recargchar, optchar);
385292932Sdim            optopt = optchar;
386292932Sdim            return (BADARG);
387292932Sdim        }
388292932Sdim        else              /* white space */
389292932Sdim            place = nargv[optind];
390292932Sdim        optchar = parse_long_options(nargv, options, long_options,
391292932Sdim            idx, 0);
392292932Sdim        place = EMSG;
393292932Sdim        return (optchar);
394292932Sdim    }
395292932Sdim    if (*++oli != ':') {            /* doesn't take argument */
396292932Sdim        if (!*place)
397292932Sdim            ++optind;
398292932Sdim    }
399292932Sdim    else {                /* takes (optional) argument */
400292932Sdim        optarg = NULL;
401292932Sdim        if (*place)         /* no white space */
402292932Sdim            optarg = const_cast<char*>(place);
403292932Sdim        else if (oli[1] != ':') {   /* arg not optional */
404292932Sdim            if (++optind >= nargc) {    /* no arg */
405292932Sdim                place = EMSG;
406292932Sdim                if (PRINT_ERROR)
407292932Sdim                    warnx(recargchar, optchar);
408292932Sdim                optopt = optchar;
409292932Sdim                return (BADARG);
410292932Sdim            }
411292932Sdim            else
412292932Sdim                optarg = nargv[optind];
413292932Sdim        }
414292932Sdim        place = EMSG;
415292932Sdim        ++optind;
416292932Sdim    }
417292932Sdim    /* dump back option letter */
418292932Sdim    return (optchar);
419292932Sdim}
420292932Sdim
421292932Sdim/*
422292932Sdim* getopt --
423292932Sdim*  Parse argc/argv argument vector.
424292932Sdim*
425292932Sdim* [eventually this will replace the BSD getopt]
426292932Sdim*/
427292932Sdim#if defined(REPLACE_GETOPT)
428292932Sdimint
429292932Sdimgetopt(int nargc, char * const *nargv, const char *options)
430292932Sdim{
431292932Sdim
432292932Sdim    /*
433292932Sdim    * We don't pass FLAG_PERMUTE to getopt_internal() since
434292932Sdim    * the BSD getopt(3) (unlike GNU) has never done this.
435292932Sdim    *
436292932Sdim    * Furthermore, since many privileged programs call getopt()
437292932Sdim    * before dropping privileges it makes sense to keep things
438292932Sdim    * as simple (and bug-free) as possible.
439292932Sdim    */
440292932Sdim    return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
441292932Sdim}
442292932Sdim#endif
443292932Sdim
444292932Sdim/*
445292932Sdim* getopt_long --
446292932Sdim*  Parse argc/argv argument vector.
447292932Sdim*/
448292932Sdim#if defined(REPLACE_GETOPT_LONG)
449292932Sdimint
450292932Sdimgetopt_long(int nargc, char * const *nargv, const char *options,
451292932Sdimconst struct option *long_options, int *idx)
452292932Sdim{
453292932Sdim    return (getopt_internal(nargc, nargv, options, long_options, idx,
454292932Sdim        FLAG_PERMUTE));
455292932Sdim}
456292932Sdim#endif
457292932Sdim
458292932Sdim/*
459292932Sdim* getopt_long_only --
460292932Sdim*  Parse argc/argv argument vector.
461292932Sdim*/
462292932Sdim#if defined(REPLACE_GETOPT_LONG_ONLY)
463292932Sdimint
464292932Sdimgetopt_long_only(int nargc, char * const *nargv, const char *options,
465292932Sdimconst struct option *long_options, int *idx)
466292932Sdim{
467292932Sdim
468292932Sdim    return (getopt_internal(nargc, nargv, options, long_options, idx,
469292932Sdim        FLAG_PERMUTE | FLAG_LONGONLY));
470292932Sdim}
471292932Sdim#endif
472292932Sdim
473292932Sdim#endif
474