xref: /third_party/ninja/src/getopt.c (revision 695b41ee)
1/****************************************************************************
2
3getopt.c - Read command line options
4
5AUTHOR: Gregory Pietsch
6CREATED Fri Jan 10 21:13:05 1997
7
8DESCRIPTION:
9
10The getopt() function parses the command line arguments.  Its arguments argc
11and argv are the argument count and array as passed to the main() function
12on program invocation.  The argument optstring is a list of available option
13characters.  If such a character is followed by a colon (`:'), the option
14takes an argument, which is placed in optarg.  If such a character is
15followed by two colons, the option takes an optional argument, which is
16placed in optarg.  If the option does not take an argument, optarg is NULL.
17
18The external variable optind is the index of the next array element of argv
19to be processed; it communicates from one call to the next which element to
20process.
21
22The getopt_long() function works like getopt() except that it also accepts
23long options started by two dashes `--'.  If these take values, it is either
24in the form
25
26--arg=value
27
28 or
29
30--arg value
31
32It takes the additional arguments longopts which is a pointer to the first
33element of an array of type GETOPT_LONG_OPTION_T.  The last element of the
34array has to be filled with NULL for the name field.
35
36The longind pointer points to the index of the current long option relative
37to longopts if it is non-NULL.
38
39The getopt() function returns the option character if the option was found
40successfully, `:' if there was a missing parameter for one of the options,
41`?' for an unknown option character, and EOF for the end of the option list.
42
43The getopt_long() function's return value is described in the header file.
44
45The function getopt_long_only() is identical to getopt_long(), except that a
46plus sign `+' can introduce long options as well as `--'.
47
48The following describes how to deal with options that follow non-option
49argv-elements.
50
51If the caller did not specify anything, the default is REQUIRE_ORDER if the
52environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
53
54REQUIRE_ORDER means don't recognize them as options; stop option processing
55when the first non-option is seen.  This is what Unix does.  This mode of
56operation is selected by either setting the environment variable
57POSIXLY_CORRECT, or using `+' as the first character of the optstring
58parameter.
59
60PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
61eventually all the non-options are at the end.  This allows options to be
62given in any order, even with programs that were not written to expect this.
63
64RETURN_IN_ORDER is an option available to programs that were written to
65expect options and other argv-elements in any order and that care about the
66ordering of the two.  We describe each non-option argv-element as if it were
67the argument of an option with character code 1.  Using `-' as the first
68character of the optstring parameter selects this mode of operation.
69
70The special argument `--' forces an end of option-scanning regardless of the
71value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
72getopt() and friends to return EOF with optind != argc.
73
74COPYRIGHT NOTICE AND DISCLAIMER:
75
76Copyright (C) 1997 Gregory Pietsch
77
78This file and the accompanying getopt.h header file are hereby placed in the
79public domain without restrictions.  Just give the author credit, don't
80claim you wrote it or prevent anyone else from using it.
81
82Gregory Pietsch's current e-mail address:
83gpietsch@comcast.net
84****************************************************************************/
85
86/* include files */
87#include <stdio.h>
88#include <stdlib.h>
89#include <string.h>
90#ifndef GETOPT_H
91#include "getopt.h"
92#endif
93
94/* macros */
95
96/* types */
97typedef enum GETOPT_ORDERING_T
98{
99  PERMUTE,
100  RETURN_IN_ORDER,
101  REQUIRE_ORDER
102} GETOPT_ORDERING_T;
103
104/* globally-defined variables */
105char *optarg = NULL;
106int optind = 0;
107int opterr = 1;
108int optopt = '?';
109
110/* functions */
111
112/* reverse_argv_elements:  reverses num elements starting at argv */
113static void
114reverse_argv_elements (char **argv, int num)
115{
116  int i;
117  char *tmp;
118
119  for (i = 0; i < (num >> 1); i++)
120    {
121      tmp = argv[i];
122      argv[i] = argv[num - i - 1];
123      argv[num - i - 1] = tmp;
124    }
125}
126
127/* permute: swap two blocks of argv-elements given their lengths */
128static void
129permute (char **argv, int len1, int len2)
130{
131  reverse_argv_elements (argv, len1);
132  reverse_argv_elements (argv, len1 + len2);
133  reverse_argv_elements (argv, len2);
134}
135
136/* is_option: is this argv-element an option or the end of the option list? */
137static int
138is_option (char *argv_element, int only)
139{
140  return ((argv_element == NULL)
141          || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
142}
143
144/* getopt_internal:  the function that does all the dirty work */
145static int
146getopt_internal (int argc, char **argv, char *shortopts,
147                 GETOPT_LONG_OPTION_T * longopts, int *longind, int only)
148{
149  GETOPT_ORDERING_T ordering = PERMUTE;
150  static size_t optwhere = 0;
151  size_t permute_from = 0;
152  int num_nonopts = 0;
153  int optindex = 0;
154  size_t match_chars = 0;
155  char *possible_arg = NULL;
156  int longopt_match = -1;
157  int has_arg = -1;
158  char *cp = NULL;
159  int arg_next = 0;
160
161  /* first, deal with silly parameters and easy stuff */
162  if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
163    return (optopt = '?');
164  if (optind >= argc || argv[optind] == NULL)
165    return EOF;
166  if (strcmp (argv[optind], "--") == 0)
167    {
168      optind++;
169      return EOF;
170    }
171  /* if this is our first time through */
172  if (optind == 0)
173    optind = optwhere = 1;
174
175  /* define ordering */
176  if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
177    {
178      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
179      shortopts++;
180    }
181  else
182    ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
183
184  /*
185   * based on ordering, find our next option, if we're at the beginning of
186   * one
187   */
188  if (optwhere == 1)
189    {
190      switch (ordering)
191        {
192        case PERMUTE:
193          permute_from = optind;
194          num_nonopts = 0;
195          while (!is_option (argv[optind], only))
196            {
197              optind++;
198              num_nonopts++;
199            }
200          if (argv[optind] == NULL)
201            {
202              /* no more options */
203              optind = permute_from;
204              return EOF;
205            }
206          else if (strcmp (argv[optind], "--") == 0)
207            {
208              /* no more options, but have to get `--' out of the way */
209              permute (argv + permute_from, num_nonopts, 1);
210              optind = permute_from + 1;
211              return EOF;
212            }
213          break;
214        case RETURN_IN_ORDER:
215          if (!is_option (argv[optind], only))
216            {
217              optarg = argv[optind++];
218              return (optopt = 1);
219            }
220          break;
221        case REQUIRE_ORDER:
222          if (!is_option (argv[optind], only))
223            return EOF;
224          break;
225        }
226    }
227  /* we've got an option, so parse it */
228
229  /* first, is it a long option? */
230  if (longopts != NULL
231      && (memcmp (argv[optind], "--", 2) == 0
232          || (only && argv[optind][0] == '+')) && optwhere == 1)
233    {
234      /* handle long options */
235      if (memcmp (argv[optind], "--", 2) == 0)
236        optwhere = 2;
237      longopt_match = -1;
238      possible_arg = strchr (argv[optind] + optwhere, '=');
239      if (possible_arg == NULL)
240        {
241          /* no =, so next argv might be arg */
242          match_chars = strlen (argv[optind]);
243          possible_arg = argv[optind] + match_chars;
244          match_chars = match_chars - optwhere;
245        }
246      else
247        match_chars = (possible_arg - argv[optind]) - optwhere;
248      for (optindex = 0; longopts[optindex].name != NULL; optindex++)
249        {
250          if (memcmp (argv[optind] + optwhere,
251                      longopts[optindex].name, match_chars) == 0)
252            {
253              /* do we have an exact match? */
254              if (match_chars == strlen (longopts[optindex].name))
255                {
256                  longopt_match = optindex;
257                  break;
258                }
259              /* do any characters match? */
260              else
261                {
262                  if (longopt_match < 0)
263                    longopt_match = optindex;
264                  else
265                    {
266                      /* we have ambiguous options */
267                      if (opterr)
268                        fprintf (stderr, "%s: option `%s' is ambiguous "
269                                 "(could be `--%s' or `--%s')\n",
270                                 argv[0],
271                                 argv[optind],
272                                 longopts[longopt_match].name,
273                                 longopts[optindex].name);
274                      return (optopt = '?');
275                    }
276                }
277            }
278        }
279      if (longopt_match >= 0)
280        has_arg = longopts[longopt_match].has_arg;
281    }
282  /* if we didn't find a long option, is it a short option? */
283  if (longopt_match < 0 && shortopts != NULL)
284    {
285      cp = strchr (shortopts, argv[optind][optwhere]);
286      if (cp == NULL)
287        {
288          /* couldn't find option in shortopts */
289          if (opterr)
290            fprintf (stderr,
291                     "%s: invalid option -- `-%c'\n",
292                     argv[0], argv[optind][optwhere]);
293          optwhere++;
294          if (argv[optind][optwhere] == '\0')
295            {
296              optind++;
297              optwhere = 1;
298            }
299          return (optopt = '?');
300        }
301      has_arg = ((cp[1] == ':')
302                 ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);
303      possible_arg = argv[optind] + optwhere + 1;
304      optopt = *cp;
305    }
306  /* get argument and reset optwhere */
307  arg_next = 0;
308  switch (has_arg)
309    {
310    case OPTIONAL_ARG:
311      if (*possible_arg == '=')
312        possible_arg++;
313      if (*possible_arg != '\0')
314        {
315          optarg = possible_arg;
316          optwhere = 1;
317        }
318      else
319        optarg = NULL;
320      break;
321    case required_argument:
322      if (*possible_arg == '=')
323        possible_arg++;
324      if (*possible_arg != '\0')
325        {
326          optarg = possible_arg;
327          optwhere = 1;
328        }
329      else if (optind + 1 >= argc)
330        {
331          if (opterr)
332            {
333              fprintf (stderr, "%s: argument required for option `", argv[0]);
334              if (longopt_match >= 0)
335                fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
336              else
337                fprintf (stderr, "-%c'\n", *cp);
338            }
339          optind++;
340          return (optopt = ':');
341        }
342      else
343        {
344          optarg = argv[optind + 1];
345          arg_next = 1;
346          optwhere = 1;
347        }
348      break;
349    case no_argument:
350      if (longopt_match < 0)
351        {
352          optwhere++;
353          if (argv[optind][optwhere] == '\0')
354            optwhere = 1;
355        }
356      else
357        optwhere = 1;
358      optarg = NULL;
359      break;
360    }
361
362  /* do we have to permute or otherwise modify optind? */
363  if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
364    {
365      permute (argv + permute_from, num_nonopts, 1 + arg_next);
366      optind = permute_from + 1 + arg_next;
367    }
368  else if (optwhere == 1)
369    optind = optind + 1 + arg_next;
370
371  /* finally return */
372  if (longopt_match >= 0)
373    {
374      if (longind != NULL)
375        *longind = longopt_match;
376      if (longopts[longopt_match].flag != NULL)
377        {
378          *(longopts[longopt_match].flag) = longopts[longopt_match].val;
379          return 0;
380        }
381      else
382        return longopts[longopt_match].val;
383    }
384  else
385    return optopt;
386}
387
388#ifndef _AIX
389int
390getopt (int argc, char **argv, char *optstring)
391{
392  return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
393}
394#endif
395
396int
397getopt_long (int argc, char **argv, const char *shortopts,
398             const GETOPT_LONG_OPTION_T * longopts, int *longind)
399{
400  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);
401}
402
403int
404getopt_long_only (int argc, char **argv, const char *shortopts,
405                  const GETOPT_LONG_OPTION_T * longopts, int *longind)
406{
407  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);
408}
409
410/* end of file GETOPT.C */
411