1314564Sdim//===-- GetOptInc.cpp -------------------------------------------*- C++ -*-===//
2314564Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6314564Sdim//
7314564Sdim//===----------------------------------------------------------------------===//
8314564Sdim
9292932Sdim#include "lldb/Host/common/GetOptInc.h"
10292932Sdim
11314564Sdim#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) ||                 \
12314564Sdim    defined(REPLACE_GETOPT_LONG_ONLY)
13292932Sdim
14292932Sdim// getopt.cpp
15292932Sdim#include <errno.h>
16292932Sdim#include <stdlib.h>
17292932Sdim#include <string.h>
18292932Sdim
19292932Sdim#if defined(REPLACE_GETOPT)
20314564Sdimint opterr = 1;   /* if error message should be printed */
21314564Sdimint optind = 1;   /* index into parent argv vector */
22314564Sdimint optopt = '?'; /* character checked for validity */
23314564Sdimint optreset;     /* reset getopt */
24314564Sdimchar *optarg;     /* argument associated with option */
25292932Sdim#endif
26292932Sdim
27292932Sdim#define PRINT_ERROR ((opterr) && (*options != ':'))
28292932Sdim
29314564Sdim#define FLAG_PERMUTE 0x01  /* permute non-options to the end of argv */
30314564Sdim#define FLAG_ALLARGS 0x02  /* treat non-options as args to option "-1" */
31314564Sdim#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
32292932Sdim
33292932Sdim/* return values */
34314564Sdim#define BADCH (int)'?'
35314564Sdim#define BADARG ((*options == ':') ? (int)':' : (int)'?')
36314564Sdim#define INORDER (int)1
37292932Sdim
38314564Sdim#define EMSG ""
39292932Sdim
40314564Sdimstatic int getopt_internal(int, char *const *, const char *,
41314564Sdim                           const struct option *, int *, int);
42314564Sdimstatic int parse_long_options(char *const *, const char *,
43314564Sdim                              const struct option *, int *, int);
44292932Sdimstatic int gcd(int, int);
45314564Sdimstatic void permute_args(int, int, int, char *const *);
46292932Sdim
47292932Sdimstatic const char *place = EMSG; /* option letter processing */
48292932Sdim
49292932Sdim/* XXX: set optreset to 1 rather than these two */
50292932Sdimstatic int nonopt_start = -1; /* first non option argument (for permute) */
51292932Sdimstatic int nonopt_end = -1;   /* first option after non options (for permute) */
52292932Sdim
53292932Sdim/*
54292932Sdim* Compute the greatest common divisor of a and b.
55292932Sdim*/
56314564Sdimstatic int gcd(int a, int b) {
57314564Sdim  int c;
58292932Sdim
59314564Sdim  c = a % b;
60314564Sdim  while (c != 0) {
61314564Sdim    a = b;
62314564Sdim    b = c;
63292932Sdim    c = a % b;
64314564Sdim  }
65292932Sdim
66314564Sdim  return (b);
67292932Sdim}
68292932Sdim
69292932Sdimstatic void pass() {}
70292932Sdim#define warnx(a, ...) pass();
71292932Sdim
72292932Sdim/*
73292932Sdim* Exchange the block from nonopt_start to nonopt_end with the block
74292932Sdim* from nonopt_end to opt_end (keeping the same order of arguments
75292932Sdim* in each block).
76292932Sdim*/
77314564Sdimstatic void permute_args(int panonopt_start, int panonopt_end, int opt_end,
78314564Sdim                         char *const *nargv) {
79314564Sdim  int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
80314564Sdim  char *swap;
81292932Sdim
82314564Sdim  /*
83314564Sdim  * compute lengths of blocks and number and size of cycles
84314564Sdim  */
85314564Sdim  nnonopts = panonopt_end - panonopt_start;
86314564Sdim  nopts = opt_end - panonopt_end;
87314564Sdim  ncycle = gcd(nnonopts, nopts);
88314564Sdim  cyclelen = (opt_end - panonopt_start) / ncycle;
89292932Sdim
90314564Sdim  for (i = 0; i < ncycle; i++) {
91314564Sdim    cstart = panonopt_end + i;
92314564Sdim    pos = cstart;
93314564Sdim    for (j = 0; j < cyclelen; j++) {
94314564Sdim      if (pos >= panonopt_end)
95314564Sdim        pos -= nnonopts;
96314564Sdim      else
97314564Sdim        pos += nopts;
98314564Sdim      swap = nargv[pos];
99314564Sdim      /* LINTED const cast */
100353358Sdim      const_cast<char **>(nargv)[pos] = nargv[cstart];
101314564Sdim      /* LINTED const cast */
102353358Sdim      const_cast<char **>(nargv)[cstart] = swap;
103292932Sdim    }
104314564Sdim  }
105292932Sdim}
106292932Sdim
107292932Sdim/*
108292932Sdim* parse_long_options --
109292932Sdim*  Parse long options in argc/argv argument vector.
110292932Sdim* Returns -1 if short_too is set and the option does not match long_options.
111292932Sdim*/
112314564Sdimstatic int parse_long_options(char *const *nargv, const char *options,
113314564Sdim                              const struct option *long_options, int *idx,
114314564Sdim                              int short_too) {
115314564Sdim  char *current_argv, *has_equal;
116314564Sdim  size_t current_argv_len;
117314564Sdim  int i, match;
118292932Sdim
119314564Sdim  current_argv = const_cast<char *>(place);
120314564Sdim  match = -1;
121292932Sdim
122314564Sdim  optind++;
123292932Sdim
124314564Sdim  if ((has_equal = strchr(current_argv, '=')) != NULL) {
125314564Sdim    /* argument found (--option=arg) */
126314564Sdim    current_argv_len = has_equal - current_argv;
127314564Sdim    has_equal++;
128314564Sdim  } else
129314564Sdim    current_argv_len = strlen(current_argv);
130314564Sdim
131314564Sdim  for (i = 0; long_options[i].name; i++) {
132314564Sdim    /* find matching long option */
133314564Sdim    if (strncmp(current_argv, long_options[i].name, current_argv_len))
134314564Sdim      continue;
135314564Sdim
136314564Sdim    if (strlen(long_options[i].name) == current_argv_len) {
137314564Sdim      /* exact match */
138314564Sdim      match = i;
139314564Sdim      break;
140292932Sdim    }
141314564Sdim    /*
142314564Sdim    * If this is a known short option, don't allow
143314564Sdim    * a partial match of a single character.
144314564Sdim    */
145314564Sdim    if (short_too && current_argv_len == 1)
146314564Sdim      continue;
147292932Sdim
148314564Sdim    if (match == -1) /* partial match */
149314564Sdim      match = i;
150314564Sdim    else {
151314564Sdim      /* ambiguous abbreviation */
152314564Sdim      if (PRINT_ERROR)
153314564Sdim        warnx(ambig, (int)current_argv_len, current_argv);
154314564Sdim      optopt = 0;
155314564Sdim      return (BADCH);
156314564Sdim    }
157314564Sdim  }
158314564Sdim  if (match != -1) { /* option found */
159314564Sdim    if (long_options[match].has_arg == no_argument && has_equal) {
160314564Sdim      if (PRINT_ERROR)
161314564Sdim        warnx(noarg, (int)current_argv_len, current_argv);
162314564Sdim      /*
163314564Sdim      * XXX: GNU sets optopt to val regardless of flag
164314564Sdim      */
165314564Sdim      if (long_options[match].flag == NULL)
166314564Sdim        optopt = long_options[match].val;
167314564Sdim      else
168314564Sdim        optopt = 0;
169314564Sdim      return (BADARG);
170314564Sdim    }
171314564Sdim    if (long_options[match].has_arg == required_argument ||
172314564Sdim        long_options[match].has_arg == optional_argument) {
173314564Sdim      if (has_equal)
174314564Sdim        optarg = has_equal;
175314564Sdim      else if (long_options[match].has_arg == required_argument) {
176292932Sdim        /*
177314564Sdim        * optional argument doesn't use next nargv
178292932Sdim        */
179314564Sdim        optarg = nargv[optind++];
180314564Sdim      }
181292932Sdim    }
182314564Sdim    if ((long_options[match].has_arg == required_argument) &&
183314564Sdim        (optarg == NULL)) {
184314564Sdim      /*
185314564Sdim      * Missing argument; leading ':' indicates no error
186314564Sdim      * should be generated.
187314564Sdim      */
188314564Sdim      if (PRINT_ERROR)
189314564Sdim        warnx(recargstring, current_argv);
190314564Sdim      /*
191314564Sdim      * XXX: GNU sets optopt to val regardless of flag
192314564Sdim      */
193314564Sdim      if (long_options[match].flag == NULL)
194314564Sdim        optopt = long_options[match].val;
195314564Sdim      else
196292932Sdim        optopt = 0;
197314564Sdim      --optind;
198314564Sdim      return (BADARG);
199292932Sdim    }
200314564Sdim  } else { /* unknown option */
201314564Sdim    if (short_too) {
202314564Sdim      --optind;
203314564Sdim      return (-1);
204292932Sdim    }
205314564Sdim    if (PRINT_ERROR)
206314564Sdim      warnx(illoptstring, current_argv);
207314564Sdim    optopt = 0;
208314564Sdim    return (BADCH);
209314564Sdim  }
210314564Sdim  if (idx)
211314564Sdim    *idx = match;
212314564Sdim  if (long_options[match].flag) {
213314564Sdim    *long_options[match].flag = long_options[match].val;
214314564Sdim    return (0);
215314564Sdim  } else
216314564Sdim    return (long_options[match].val);
217292932Sdim}
218292932Sdim
219292932Sdim/*
220292932Sdim* getopt_internal --
221292932Sdim*  Parse argc/argv argument vector.  Called by user level routines.
222292932Sdim*/
223314564Sdimstatic int getopt_internal(int nargc, char *const *nargv, const char *options,
224314564Sdim                           const struct option *long_options, int *idx,
225314564Sdim                           int flags) {
226314564Sdim  const char *oli; /* option letter list index */
227314564Sdim  int optchar, short_too;
228314564Sdim  static int posixly_correct = -1;
229292932Sdim
230314564Sdim  if (options == NULL)
231314564Sdim    return (-1);
232292932Sdim
233314564Sdim  /*
234314564Sdim  * XXX Some GNU programs (like cvs) set optind to 0 instead of
235314564Sdim  * XXX using optreset.  Work around this braindamage.
236314564Sdim  */
237314564Sdim  if (optind == 0)
238314564Sdim    optind = optreset = 1;
239292932Sdim
240314564Sdim  /*
241314564Sdim  * Disable GNU extensions if POSIXLY_CORRECT is set or options
242314564Sdim  * string begins with a '+'.
243314564Sdim  */
244314564Sdim  if (posixly_correct == -1 || optreset)
245314564Sdim    posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
246314564Sdim  if (*options == '-')
247314564Sdim    flags |= FLAG_ALLARGS;
248314564Sdim  else if (posixly_correct || *options == '+')
249314564Sdim    flags &= ~FLAG_PERMUTE;
250314564Sdim  if (*options == '+' || *options == '-')
251314564Sdim    options++;
252292932Sdim
253314564Sdim  optarg = NULL;
254314564Sdim  if (optreset)
255314564Sdim    nonopt_start = nonopt_end = -1;
256292932Sdimstart:
257314564Sdim  if (optreset || !*place) { /* update scanning pointer */
258314564Sdim    optreset = 0;
259314564Sdim    if (optind >= nargc) { /* end of argument vector */
260314564Sdim      place = EMSG;
261314564Sdim      if (nonopt_end != -1) {
262314564Sdim        /* do permutation, if we have to */
263314564Sdim        permute_args(nonopt_start, nonopt_end, optind, nargv);
264314564Sdim        optind -= nonopt_end - nonopt_start;
265314564Sdim      } else if (nonopt_start != -1) {
266292932Sdim        /*
267314564Sdim        * If we skipped non-options, set optind
268314564Sdim        * to the first of them.
269292932Sdim        */
270314564Sdim        optind = nonopt_start;
271314564Sdim      }
272314564Sdim      nonopt_start = nonopt_end = -1;
273314564Sdim      return (-1);
274292932Sdim    }
275314564Sdim    if (*(place = nargv[optind]) != '-' ||
276314564Sdim        (place[1] == '\0' && strchr(options, '-') == NULL)) {
277314564Sdim      place = EMSG; /* found non-option */
278314564Sdim      if (flags & FLAG_ALLARGS) {
279314564Sdim        /*
280314564Sdim        * GNU extension:
281314564Sdim        * return non-option as argument to option 1
282314564Sdim        */
283314564Sdim        optarg = nargv[optind++];
284314564Sdim        return (INORDER);
285314564Sdim      }
286314564Sdim      if (!(flags & FLAG_PERMUTE)) {
287314564Sdim        /*
288314564Sdim        * If no permutation wanted, stop parsing
289314564Sdim        * at first non-option.
290314564Sdim        */
291314564Sdim        return (-1);
292314564Sdim      }
293314564Sdim      /* do permutation */
294314564Sdim      if (nonopt_start == -1)
295314564Sdim        nonopt_start = optind;
296314564Sdim      else if (nonopt_end != -1) {
297314564Sdim        permute_args(nonopt_start, nonopt_end, optind, nargv);
298314564Sdim        nonopt_start = optind - (nonopt_end - nonopt_start);
299314564Sdim        nonopt_end = -1;
300314564Sdim      }
301314564Sdim      optind++;
302314564Sdim      /* process next argument */
303314564Sdim      goto start;
304314564Sdim    }
305314564Sdim    if (nonopt_start != -1 && nonopt_end == -1)
306314564Sdim      nonopt_end = optind;
307292932Sdim
308292932Sdim    /*
309314564Sdim    * If we have "-" do nothing, if "--" we are done.
310292932Sdim    */
311314564Sdim    if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
312314564Sdim      optind++;
313314564Sdim      place = EMSG;
314314564Sdim      /*
315314564Sdim      * We found an option (--), so if we skipped
316314564Sdim      * non-options, we have to permute.
317314564Sdim      */
318314564Sdim      if (nonopt_end != -1) {
319314564Sdim        permute_args(nonopt_start, nonopt_end, optind, nargv);
320314564Sdim        optind -= nonopt_end - nonopt_start;
321314564Sdim      }
322314564Sdim      nonopt_start = nonopt_end = -1;
323314564Sdim      return (-1);
324314564Sdim    }
325314564Sdim  }
326292932Sdim
327314564Sdim  /*
328314564Sdim  * Check long options if:
329314564Sdim  *  1) we were passed some
330314564Sdim  *  2) the arg is not just "-"
331314564Sdim  *  3) either the arg starts with -- we are getopt_long_only()
332314564Sdim  */
333314564Sdim  if (long_options != NULL && place != nargv[optind] &&
334314564Sdim      (*place == '-' || (flags & FLAG_LONGONLY))) {
335314564Sdim    short_too = 0;
336314564Sdim    if (*place == '-')
337314564Sdim      place++; /* --foo long option */
338314564Sdim    else if (*place != ':' && strchr(options, *place) != NULL)
339314564Sdim      short_too = 1; /* could be short option too */
340314564Sdim
341314564Sdim    optchar = parse_long_options(nargv, options, long_options, idx, short_too);
342314564Sdim    if (optchar != -1) {
343314564Sdim      place = EMSG;
344314564Sdim      return (optchar);
345292932Sdim    }
346314564Sdim  }
347292932Sdim
348314564Sdim  if ((optchar = (int)*place++) == (int)':' ||
349314564Sdim      (optchar == (int)'-' && *place != '\0') ||
350314564Sdim      (oli = strchr(options, optchar)) == NULL) {
351314564Sdim    /*
352314564Sdim    * If the user specified "-" and  '-' isn't listed in
353314564Sdim    * options, return -1 (non-option) as per POSIX.
354314564Sdim    * Otherwise, it is an unknown option character (or ':').
355314564Sdim    */
356314564Sdim    if (optchar == (int)'-' && *place == '\0')
357314564Sdim      return (-1);
358314564Sdim    if (!*place)
359314564Sdim      ++optind;
360314564Sdim    if (PRINT_ERROR)
361314564Sdim      warnx(illoptchar, optchar);
362314564Sdim    optopt = optchar;
363314564Sdim    return (BADCH);
364314564Sdim  }
365314564Sdim  if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
366314564Sdim    /* -W long-option */
367314564Sdim    if (*place) /* no space */
368314564Sdim      /* NOTHING */;
369314564Sdim    else if (++optind >= nargc) { /* no arg */
370314564Sdim      place = EMSG;
371314564Sdim      if (PRINT_ERROR)
372314564Sdim        warnx(recargchar, optchar);
373314564Sdim      optopt = optchar;
374314564Sdim      return (BADARG);
375314564Sdim    } else /* white space */
376314564Sdim      place = nargv[optind];
377314564Sdim    optchar = parse_long_options(nargv, options, long_options, idx, 0);
378314564Sdim    place = EMSG;
379314564Sdim    return (optchar);
380314564Sdim  }
381314564Sdim  if (*++oli != ':') { /* doesn't take argument */
382314564Sdim    if (!*place)
383314564Sdim      ++optind;
384314564Sdim  } else { /* takes (optional) argument */
385314564Sdim    optarg = NULL;
386314564Sdim    if (*place) /* no white space */
387314564Sdim      optarg = const_cast<char *>(place);
388314564Sdim    else if (oli[1] != ':') {  /* arg not optional */
389314564Sdim      if (++optind >= nargc) { /* no arg */
390314564Sdim        place = EMSG;
391292932Sdim        if (PRINT_ERROR)
392314564Sdim          warnx(recargchar, optchar);
393292932Sdim        optopt = optchar;
394314564Sdim        return (BADARG);
395314564Sdim      } else
396314564Sdim        optarg = nargv[optind];
397292932Sdim    }
398314564Sdim    place = EMSG;
399314564Sdim    ++optind;
400314564Sdim  }
401314564Sdim  /* dump back option letter */
402314564Sdim  return (optchar);
403292932Sdim}
404292932Sdim
405292932Sdim/*
406292932Sdim* getopt --
407292932Sdim*  Parse argc/argv argument vector.
408292932Sdim*
409292932Sdim* [eventually this will replace the BSD getopt]
410292932Sdim*/
411292932Sdim#if defined(REPLACE_GETOPT)
412314564Sdimint getopt(int nargc, char *const *nargv, const char *options) {
413292932Sdim
414314564Sdim  /*
415314564Sdim  * We don't pass FLAG_PERMUTE to getopt_internal() since
416314564Sdim  * the BSD getopt(3) (unlike GNU) has never done this.
417314564Sdim  *
418314564Sdim  * Furthermore, since many privileged programs call getopt()
419314564Sdim  * before dropping privileges it makes sense to keep things
420314564Sdim  * as simple (and bug-free) as possible.
421314564Sdim  */
422314564Sdim  return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
423292932Sdim}
424292932Sdim#endif
425292932Sdim
426292932Sdim/*
427292932Sdim* getopt_long --
428292932Sdim*  Parse argc/argv argument vector.
429292932Sdim*/
430292932Sdim#if defined(REPLACE_GETOPT_LONG)
431314564Sdimint getopt_long(int nargc, char *const *nargv, const char *options,
432314564Sdim                const struct option *long_options, int *idx) {
433314564Sdim  return (
434314564Sdim      getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));
435292932Sdim}
436292932Sdim#endif
437292932Sdim
438292932Sdim/*
439292932Sdim* getopt_long_only --
440292932Sdim*  Parse argc/argv argument vector.
441292932Sdim*/
442292932Sdim#if defined(REPLACE_GETOPT_LONG_ONLY)
443314564Sdimint getopt_long_only(int nargc, char *const *nargv, const char *options,
444314564Sdim                     const struct option *long_options, int *idx) {
445292932Sdim
446314564Sdim  return (getopt_internal(nargc, nargv, options, long_options, idx,
447314564Sdim                          FLAG_PERMUTE | FLAG_LONGONLY));
448292932Sdim}
449292932Sdim#endif
450292932Sdim
451292932Sdim#endif
452