source: trunk/libs/newlib/src/newlib/libc/stdlib/getopt.c @ 444

Last change on this file since 444 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 14.1 KB
Line 
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 struct option.  The last element of the array
34has 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
742012-08-26: Tried to make the error handling more sus4-like. The functions
75return a colon if getopt() and friends detect a missing argument and the
76first character of shortopts/optstring starts with a colon (`:'). If getopt()
77and friends detect a missing argument and shortopts/optstring does not start
78with a colon, the function returns a question mark (`?'). If it was a missing
79argument to a short option, optopt is set to the character in question. The
80colon goes after the ordering character (`+' or `-').
81
82COPYRIGHT NOTICE AND DISCLAIMER:
83
84Copyright (C) 1997 Gregory Pietsch
85
86This file and the accompanying getopt.h header file are hereby placed in the
87public domain without restrictions.  Just give the author credit, don't
88claim you wrote it or prevent anyone else from using it.
89
90Gregory Pietsch's current e-mail address:
91gpietsch@comcast.net
92****************************************************************************/
93
94#ifndef HAVE_GETOPT
95
96/* include files */
97#include <stdio.h>
98#include <stdlib.h>
99#include <string.h>
100#define __need_getopt_newlib
101#include <getopt.h>
102
103/* macros */
104
105/* types */
106typedef enum GETOPT_ORDERING_T
107{
108  PERMUTE,
109  RETURN_IN_ORDER,
110  REQUIRE_ORDER
111} GETOPT_ORDERING_T;
112
113/* globally-defined variables */
114char *optarg = 0;
115int optind = 0;
116int opterr = 1;
117int optopt = '?';
118
119/* static variables */
120static int optwhere = 0;
121
122/* functions */
123
124/* reverse_argv_elements:  reverses num elements starting at argv */
125static void
126reverse_argv_elements (char **argv, int num)
127{
128  int i;
129  char *tmp;
130
131  for (i = 0; i < (num >> 1); i++)
132    {
133      tmp = argv[i];
134      argv[i] = argv[num - i - 1];
135      argv[num - i - 1] = tmp;
136    }
137}
138
139/* permute: swap two blocks of argv-elements given their lengths */
140static void
141permute (char *const argv[], int len1, int len2)
142{
143  reverse_argv_elements ((char **) argv, len1);
144  reverse_argv_elements ((char **) argv, len1 + len2);
145  reverse_argv_elements ((char **) argv, len2);
146}
147
148/* is_option: is this argv-element an option or the end of the option list? */
149static int
150is_option (char *argv_element, int only)
151{
152  return ((argv_element == 0)
153          || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
154}
155
156/* read_globals: read the values from the globals into a getopt_data
157   structure */
158static void
159read_globals (struct getopt_data *data)
160{
161  data->optarg = optarg;
162  data->optind = optind;
163  data->opterr = opterr;
164  data->optopt = optopt;
165  data->optwhere = optwhere;
166}
167
168/* write_globals: write the values into the globals from a getopt_data
169   structure */
170static void
171write_globals (struct getopt_data *data)
172{
173  optarg = data->optarg;
174  optind = data->optind;
175  opterr = data->opterr;
176  optopt = data->optopt;
177  optwhere = data->optwhere;
178}
179
180/* getopt_internal:  the function that does all the dirty work
181   NOTE: to reduce the code and RAM footprint this function uses
182   fputs()/fputc() to do output to stderr instead of fprintf(). */
183static int
184getopt_internal (int argc, char *const argv[], const char *shortopts,
185                 const struct option *longopts, int *longind, int only,
186                 struct getopt_data *data)
187{
188  GETOPT_ORDERING_T ordering = PERMUTE;
189  size_t permute_from = 0;
190  int num_nonopts = 0;
191  int optindex = 0;
192  size_t match_chars = 0;
193  char *possible_arg = 0;
194  int longopt_match = -1;
195  int has_arg = -1;
196  char *cp = 0;
197  int arg_next = 0;
198  int initial_colon = 0;
199
200  /* first, deal with silly parameters and easy stuff */
201  if (argc == 0 || argv == 0 || (shortopts == 0 && longopts == 0)
202      || data->optind >= argc || argv[data->optind] == 0)
203    return EOF;
204  if (strcmp (argv[data->optind], "--") == 0)
205    {
206      data->optind++;
207      return EOF;
208    }
209
210  /* if this is our first time through */
211  if (data->optind == 0)
212    data->optind = data->optwhere = 1;
213
214  /* define ordering */
215  if (shortopts != 0 && (*shortopts == '-' || *shortopts == '+'))
216    {
217      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
218      shortopts++;
219    }
220  else
221    ordering = (getenv ("POSIXLY_CORRECT") != 0) ? REQUIRE_ORDER : PERMUTE;
222
223  /* check for initial colon in shortopts */
224  if (shortopts != 0 && *shortopts == ':')
225    {
226      ++shortopts;
227      initial_colon = 1;
228    }
229
230  /*
231   * based on ordering, find our next option, if we're at the beginning of
232   * one
233   */
234  if (data->optwhere == 1)
235    {
236      switch (ordering)
237        {
238        default:                /* shouldn't happen */
239        case PERMUTE:
240          permute_from = data->optind;
241          num_nonopts = 0;
242          while (!is_option (argv[data->optind], only))
243            {
244              data->optind++;
245              num_nonopts++;
246            }
247          if (argv[data->optind] == 0)
248            {
249              /* no more options */
250              data->optind = permute_from;
251              return EOF;
252            }
253          else if (strcmp (argv[data->optind], "--") == 0)
254            {
255              /* no more options, but have to get `--' out of the way */
256              permute (argv + permute_from, num_nonopts, 1);
257              data->optind = permute_from + 1;
258              return EOF;
259            }
260          break;
261        case RETURN_IN_ORDER:
262          if (!is_option (argv[data->optind], only))
263            {
264              data->optarg = argv[data->optind++];
265              return (data->optopt = 1);
266            }
267          break;
268        case REQUIRE_ORDER:
269          if (!is_option (argv[data->optind], only))
270            return EOF;
271          break;
272        }
273    }
274  /* End of option list? */
275  if (argv[data->optind] == 0)
276    return EOF;
277
278  /* we've got an option, so parse it */
279
280  /* first, is it a long option? */
281  if (longopts != 0
282      && (memcmp (argv[data->optind], "--", 2) == 0
283          || (only && argv[data->optind][0] == '+')) && data->optwhere == 1)
284    {
285      /* handle long options */
286      if (memcmp (argv[data->optind], "--", 2) == 0)
287        data->optwhere = 2;
288      longopt_match = -1;
289      possible_arg = strchr (argv[data->optind] + data->optwhere, '=');
290      if (possible_arg == 0)
291        {
292          /* no =, so next argv might be arg */
293          match_chars = strlen (argv[data->optind]);
294          possible_arg = argv[data->optind] + match_chars;
295          match_chars = match_chars - data->optwhere;
296        }
297      else
298        match_chars = (possible_arg - argv[data->optind]) - data->optwhere;
299      for (optindex = 0; longopts[optindex].name != 0; ++optindex)
300        {
301          if (memcmp
302              (argv[data->optind] + data->optwhere, longopts[optindex].name,
303               match_chars) == 0)
304            {
305              /* do we have an exact match? */
306              if (match_chars == strlen (longopts[optindex].name))
307                {
308                  longopt_match = optindex;
309                  break;
310                }
311              /* do any characters match? */
312              else
313                {
314                  if (longopt_match < 0)
315                    longopt_match = optindex;
316                  else
317                    {
318                      /* we have ambiguous options */
319                      if (data->opterr)
320                        {
321                          fputs (argv[0], stderr);
322                          fputs (": option `", stderr);
323                          fputs (argv[data->optind], stderr);
324                          fputs ("' is ambiguous (could be `--", stderr);
325                          fputs (longopts[longopt_match].name, stderr);
326                          fputs ("' or `--", stderr);
327                          fputs (longopts[optindex].name, stderr);
328                          fputs ("')\n", stderr);
329                        }
330                      return (data->optopt = '?');
331                    }
332                }
333            }
334        }
335      if (longopt_match >= 0)
336        has_arg = longopts[longopt_match].has_arg;
337    }
338
339  /* if we didn't find a long option, is it a short option? */
340  if (longopt_match < 0 && shortopts != 0)
341    {
342      cp = strchr (shortopts, argv[data->optind][data->optwhere]);
343      if (cp == 0)
344        {
345          /* couldn't find option in shortopts */
346          if (data->opterr)
347            {
348              fputs (argv[0], stderr);
349              fputs (": invalid option -- `-", stderr);
350              fputc (argv[data->optind][data->optwhere], stderr);
351              fputs ("'\n", stderr);
352            }
353          data->optwhere++;
354          if (argv[data->optind][data->optwhere] == '\0')
355            {
356              data->optind++;
357              data->optwhere = 1;
358            }
359          return (data->optopt = '?');
360        }
361      has_arg = ((cp[1] == ':')
362                 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
363      possible_arg = argv[data->optind] + data->optwhere + 1;
364      data->optopt = *cp;
365    }
366
367  /* get argument and reset data->optwhere */
368  arg_next = 0;
369  switch (has_arg)
370    {
371    case OPTIONAL_ARG:
372      if (*possible_arg == '=')
373        possible_arg++;
374      data->optarg = (*possible_arg != '\0') ? possible_arg : 0;
375      data->optwhere = 1;
376      break;
377    case REQUIRED_ARG:
378      if (*possible_arg == '=')
379        possible_arg++;
380      if (*possible_arg != '\0')
381        {
382          data->optarg = possible_arg;
383          data->optwhere = 1;
384        }
385      else if (data->optind + 1 >= argc)
386        {
387          if (data->opterr)
388            {
389              fputs (argv[0], stderr);
390              fputs (": argument required for option `-", stderr);
391              if (longopt_match >= 0)
392                {
393                  fputc ('-', stderr);
394                  fputs (longopts[longopt_match].name, stderr);
395                  data->optopt = initial_colon ? ':' : '\?';
396                }
397              else
398                {
399                  fputc (*cp, stderr);
400                  data->optopt = *cp;
401                }
402              fputs ("'\n", stderr);
403            }
404          data->optind++;
405          return initial_colon ? ':' : '\?';
406        }
407      else
408        {
409          data->optarg = argv[data->optind + 1];
410          arg_next = 1;
411          data->optwhere = 1;
412        }
413      break;
414    default:                    /* shouldn't happen */
415    case NO_ARG:
416      if (longopt_match < 0)
417        {
418          data->optwhere++;
419          if (argv[data->optind][data->optwhere] == '\0')
420            data->optwhere = 1;
421        }
422      else
423        data->optwhere = 1;
424      data->optarg = 0;
425      break;
426    }
427
428  /* do we have to permute or otherwise modify data->optind? */
429  if (ordering == PERMUTE && data->optwhere == 1 && num_nonopts != 0)
430    {
431      permute (argv + permute_from, num_nonopts, 1 + arg_next);
432      data->optind = permute_from + 1 + arg_next;
433    }
434  else if (data->optwhere == 1)
435    data->optind = data->optind + 1 + arg_next;
436
437  /* finally return */
438  if (longopt_match >= 0)
439    {
440      if (longind != 0)
441        *longind = longopt_match;
442      if (longopts[longopt_match].flag != 0)
443        {
444          *(longopts[longopt_match].flag) = longopts[longopt_match].val;
445          return 0;
446        }
447      else
448        return longopts[longopt_match].val;
449    }
450  else
451    return data->optopt;
452}
453
454int
455getopt (int argc, char *const argv[], const char *optstring)
456{
457  struct getopt_data data;
458  int r;
459
460  read_globals (&data);
461  r = getopt_internal (argc, argv, optstring, 0, 0, 0, &data);
462  write_globals (&data);
463  return r;
464}
465
466int
467getopt_long (int argc, char *const argv[], const char *shortopts,
468             const struct option *longopts, int *longind)
469{
470  struct getopt_data data;
471  int r;
472
473  read_globals (&data);
474  r = getopt_internal (argc, argv, shortopts, longopts, longind, 0, &data);
475  write_globals (&data);
476  return r;
477}
478
479int
480getopt_long_only (int argc, char *const argv[], const char *shortopts,
481                  const struct option *longopts, int *longind)
482{
483  struct getopt_data data;
484  int r;
485
486  read_globals (&data);
487  r = getopt_internal (argc, argv, shortopts, longopts, longind, 1, &data);
488  write_globals (&data);
489  return r;
490}
491
492int
493__getopt_r (int argc, char *const argv[], const char *optstring,
494            struct getopt_data *data)
495{
496  return getopt_internal (argc, argv, optstring, 0, 0, 0, data);
497}
498
499int
500__getopt_long_r (int argc, char *const argv[], const char *shortopts,
501                 const struct option *longopts, int *longind,
502                 struct getopt_data *data)
503{
504  return getopt_internal (argc, argv, shortopts, longopts, longind, 0, data);
505}
506
507int
508__getopt_long_only_r (int argc, char *const argv[], const char *shortopts,
509                      const struct option *longopts, int *longind,
510                      struct getopt_data *data)
511{
512  return getopt_internal (argc, argv, shortopts, longopts, longind, 1, data);
513}
514
515#endif /* !HAVE_GETOPT */
516
517/* end of file GETOPT.C */
Note: See TracBrowser for help on using the repository browser.