mescc: Mes C Library: Move posix bits to read.
[mes.git] / lib / posix / getopt.c
1 /* Getopt for GNU.
2    Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
3    Copyright (C) 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
18 \f
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <getopt.h>
23
24 #if __MESC__
25 #define static
26 #endif
27
28 /* For communication from `getopt' to the caller.
29    When `getopt' finds an option that takes an argument,
30    the argument value is returned here. */
31
32 char *optarg = 0;
33
34 /* Index in ARGV of the next element to be scanned.
35    This is used for communication to and from the caller
36    and for communication between successive calls to `getopt'.
37
38    On entry to `getopt', zero means this is the first call; initialize.
39
40    When `getopt' returns EOF, this is the index of the first of the
41    non-option elements that the caller should itself scan.
42
43    Otherwise, `optind' communicates from one call to the next
44    how much of ARGV has been scanned so far.  */
45
46 int optind = 0;
47
48 /* The next char to be scanned in the option-element
49    in which the last option character we returned was found.
50    This allows us to pick up the scan where we left off.
51
52    If this is zero, or a null string, it means resume the scan
53    by advancing to the next ARGV-element.  */
54
55 static char *nextchar;
56
57 /* Callers store zero here to inhibit the error message
58    for unrecognized options.  */
59
60 int opterr = 1;
61 \f
62
63 /* Handle permutation of arguments.  */
64
65 /* Describe the part of ARGV that contains non-options that have
66    been skipped.  `first_nonopt' is the index in ARGV of the first of them;
67    `last_nonopt' is the index after the last of them.  */
68
69 static int first_nonopt;
70 static int last_nonopt;
71 \f
72
73 /* Scan elements of ARGV (whose length is ARGC) for option characters
74    given in OPTSTRING.
75
76    If an element of ARGV starts with '-', and is not exactly "-" or "--",
77    then it is an option element.  The characters of this element
78    (aside from the initial '-') are option characters.  If `getopt'
79    is called repeatedly, it returns successively each of the option characters
80    from each of the option elements.
81
82    If `getopt' finds another option character, it returns that character,
83    updating `optind' and `nextchar' so that the next call to `getopt' can
84    resume the scan with the following option character or ARGV-element.
85
86    If there are no more option characters, `getopt' returns `EOF'.
87    Then `optind' is the index in ARGV of the first ARGV-element
88    that is not an option.  (The ARGV-elements have been permuted
89    so that those that are not options now come last.)
90
91    OPTSTRING is a string containing the legitimate option characters.
92    If an option character is seen that is not listed in OPTSTRING,
93    return '?' after printing an error message.  If you set `opterr' to
94    zero, the error message is suppressed but we still return '?'.
95
96    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
97    so the following text in the same ARGV-element, or the text of the following
98    ARGV-element, is returned in `optarg'.  Two colons mean an option that
99    wants an optional arg; if there is text in the current ARGV-element,
100    it is returned in `optarg', otherwise `optarg' is set to zero.
101
102    If OPTSTRING starts with `-' or `+', it requests different methods of
103    handling the non-option ARGV-elements.
104
105    Long-named options begin with `--' instead of `-'.
106    Their names may be abbreviated as long as the abbreviation is unique
107    or is an exact match for some defined option.  If they have an
108    argument, it follows the option name in the same ARGV-element, separated
109    from the option name by a `=', or else the in next ARGV-element.
110    When `getopt' finds a long-named option, it returns 0 if that option's
111    `flag' field is nonzero, the value of the option's `val' field
112    if the `flag' field is zero.
113
114    The elements of ARGV aren't really const, because we permute them.
115    But we pretend they're const in the prototype to be compatible
116    with other systems.
117
118    LONGOPTS is a vector of `struct option' terminated by an
119    element containing a name which is zero.
120
121    LONGIND returns the index in LONGOPT of the long-named option found.
122    It is only valid when a long-named option has been found by the most
123    recent call.
124
125    If LONG_ONLY is nonzero, '-' as well as '--' can introduce
126    long-named options.  */
127
128 int
129 _getopt_internal (int argc, char *const
130                   *argv, char const *optstring, struct option const *longopts, int *longind, int long_only)
131 {
132   int option_index;
133
134   optarg = 0;
135
136   /* Initialize the internal data when the first call is made.
137      Start processing options with ARGV-element 1 (since ARGV-element 0
138      is the program name); the sequence of previously skipped
139      non-option ARGV-elements is empty.  */
140
141   if (optind == 0)
142     {
143       first_nonopt = last_nonopt = optind = 1;
144
145       nextchar = NULL;
146     }
147
148   if (nextchar == NULL || *nextchar == '\0')
149     {
150       /* If we have done all the ARGV-elements, stop the scan
151          and back over any non-options that we skipped and permuted.  */
152
153       if (optind == argc)
154         {
155           /* Set the next-arg-index to point at the non-options
156              that we previously skipped, so the caller will digest them.  */
157           if (first_nonopt != last_nonopt)
158             optind = first_nonopt;
159           return EOF;
160         }
161
162       /* If we have come to a non-option and did not permute it,
163          either stop the scan or describe it to the caller and pass it by.  */
164
165       if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
166         return EOF;
167
168       /* We have found another option-ARGV-element.
169          Start decoding its characters.  */
170
171       nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-'));
172     }
173
174   if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only))))
175     {
176       const struct option *p;
177       char *s = nextchar;
178       int exact = 0;
179       int ambig = 0;
180       const struct option *pfound = NULL;
181       int indfound;
182
183       while (*s && *s != '=')
184         s++;
185
186       /* Test all options for either exact match or abbreviated matches.  */
187       for (p = longopts, option_index = 0; p->name; p++, option_index++)
188         if (!strncmp (p->name, nextchar, s - nextchar))
189           {
190             if (s - nextchar == strlen (p->name))
191               {
192                 /* Exact match found.  */
193                 pfound = p;
194                 indfound = option_index;
195                 exact = 1;
196                 break;
197               }
198             else if (pfound == NULL)
199               {
200                 /* First nonexact match found.  */
201                 pfound = p;
202                 indfound = option_index;
203               }
204             else
205               /* Second nonexact match found.  */
206               ambig = 1;
207           }
208
209       if (ambig && !exact)
210         {
211           if (opterr)
212             fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]);
213           nextchar += strlen (nextchar);
214           optind++;
215           return '?';
216         }
217
218       if (pfound != NULL)
219         {
220           option_index = indfound;
221           optind++;
222           if (*s)
223             {
224               /* Don't test has_arg with >, because some C compilers don't
225                  allow it to be used on enums. */
226               if (pfound->has_arg)
227                 optarg = s + 1;
228               else
229                 {
230                   if (opterr)
231                     {
232                       if (argv[optind - 1][1] == '-')
233                         /* --option */
234                         fprintf (stderr,
235                                  "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name);
236                       else
237                         /* +option or -option */
238                         fprintf (stderr,
239                                  "%s: option `%c%s' doesn't allow an argument\n",
240                                  argv[0], argv[optind - 1][0], pfound->name);
241                     }
242                   nextchar += strlen (nextchar);
243                   return '?';
244                 }
245             }
246           else if (pfound->has_arg == 1)
247             {
248               if (optind < argc)
249                 optarg = argv[optind++];
250               else
251                 {
252                   if (opterr)
253                     fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]);
254                   nextchar += strlen (nextchar);
255                   return '?';
256                 }
257             }
258           nextchar += strlen (nextchar);
259           if (longind != NULL)
260             *longind = option_index;
261           if (pfound->flag)
262             {
263               *(pfound->flag) = pfound->val;
264               return 0;
265             }
266           return pfound->val;
267         }
268       /* Can't find it as a long option.  If this is not getopt_long_only,
269          or the option starts with '--' or is not a valid short
270          option, then it's an error.
271          Otherwise interpret it as a short option. */
272       if (!long_only || argv[optind][1] == '-' || strchr (optstring, *nextchar) == NULL)
273         {
274           if (opterr)
275             {
276               if (argv[optind][1] == '-')
277                 /* --option */
278                 fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar);
279               else
280                 /* +option or -option */
281                 fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar);
282             }
283           nextchar += strlen (nextchar);
284           optind++;
285           return '?';
286         }
287     }
288
289   /* Look at and handle the next option-character.  */
290
291   {
292     char c = *nextchar++;
293     char *temp = strchr (optstring, c);
294
295     /* Increment `optind' when we start to process its last character.  */
296     if (*nextchar == '\0')
297       optind++;
298
299     if (temp == NULL || c == ':')
300       {
301         if (opterr)
302           {
303             if (c < 040 || c >= 0177)
304               fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c);
305             else
306               fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
307           }
308         return '?';
309       }
310     if (temp[1] == ':')
311       {
312         if (temp[2] == ':')
313           {
314             /* This is an option that accepts an argument optionally.  */
315             if (*nextchar != '\0')
316               {
317                 optarg = nextchar;
318                 optind++;
319               }
320             else
321               optarg = 0;
322             nextchar = NULL;
323           }
324         else
325           {
326             /* This is an option that requires an argument.  */
327             if (*nextchar != 0)
328               {
329                 optarg = nextchar;
330                 /* If we end this ARGV-element by taking the rest as an arg,
331                    we must advance to the next element now.  */
332                 optind++;
333               }
334             else if (optind == argc)
335               {
336                 if (opterr)
337                   fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c);
338                 c = '?';
339               }
340             else
341               /* We already incremented `optind' once;
342                  increment it again when taking next ARGV-elt as argument.  */
343               optarg = argv[optind++];
344             nextchar = NULL;
345           }
346       }
347     return c;
348   }
349 }
350
351 int
352 getopt (int argc, char *const *argv, char const *options)
353 {
354   return _getopt_internal (argc, argv, options, (const struct option *) 0, (int *) 0, 0);
355 }
356
357 int
358 getopt_long (int argc, char *const *argv, char const *options,
359              struct option const *long_options, int *opt_index)
360 {
361   return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
362 }
363
364 int
365 getopt_long_only (int argc, char *const *argv, char const *options,
366                   struct option const *long_options, int *opt_index)
367 {
368   return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
369 }