1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 
7 #include "getopt.h"
8 
9 
10 #ifdef  __cplusplus
11 extern "C" {
12 #endif
13 
14 /* Describe how to deal with options that follow non-option ARGV-elements.
15 
16    If the caller did not specify anything,
17    the default is REQUIRE_ORDER if the environment variable
18    POSIXLY_CORRECT is defined, PERMUTE otherwise.
19 
20    REQUIRE_ORDER means don't recognize them as options;
21    stop option processing when the first non-option is seen.
22    This is what Unix does.
23    This mode of operation is selected by either setting the environment
24    variable POSIXLY_CORRECT, or using `+' as the first character
25    of the list of option characters.
26 
27    PERMUTE is the default.  We permute the contents of ARGV as we scan,
28    so that eventually all the non-options are at the end.  This allows options
29    to be given in any order, even with programs that were not written to
30    expect this.
31 
32    RETURN_IN_ORDER is an option available to programs that were written
33    to expect options and other ARGV-elements in any order and that care about
34    the ordering of the two.  We describe each non-option ARGV-element
35    as if it were the argument of an option with character code 1.
36    Using `-' as the first character of the list of option characters
37    selects this mode of operation.
38 
39    The special argument `--' forces an end of option-scanning regardless
40    of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
41    `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
42 
43 static enum
44 {
45     REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
46 } ordering;
47 
48 #define my_index    wcschr
49 
50 #define my_strtoul  wcstoul
51 #define my_strlen   wcslen
52 #define my_strncmp  wcsncmp
53 #define my_strcpy   wcscpy
54 #define my_strcat   wcscat
55 #define my_strcmp   wcscmp
56 
57 /* Handle permutation of arguments.  */
58 
59 
60 void
getopt_init(optarg_ctx * o)61 getopt_init(optarg_ctx* o) {
62 
63     o->optarg = NULL;
64     o->optind = 0;
65     o->optopt = BAD_OPTION;
66     o->opterr = 1;
67 }
68 
69 static void
exchange(optarg_ctx * o,WCHAR ** argv)70 exchange (
71     optarg_ctx* o,
72     WCHAR **argv
73     )
74 {
75     WCHAR *temp, **first, **last;
76 
77     /* Reverse all the elements [first_nonopt, optind) */
78     first = &argv[o->first_nonopt];
79     last  = &argv[o->optind-1];
80     while (first < last) {
81         temp = *first; *first = *last; *last = temp; first++; last--;
82     }
83     /* Put back the options in order */
84     first = &argv[o->first_nonopt];
85     o->first_nonopt += (o->optind - o->last_nonopt);
86     last  = &argv[o->first_nonopt - 1];
87     while (first < last) {
88         temp = *first; *first = *last; *last = temp; first++; last--;
89     }
90 
91     /* Put back the non options in order */
92     first = &argv[o->first_nonopt];
93     o->last_nonopt = o->optind;
94     last  = &argv[o->last_nonopt-1];
95     while (first < last) {
96         temp = *first; *first = *last; *last = temp; first++; last--;
97     }
98 }
99 
100 /* Scan elements of ARGV (whose length is ARGC) for option characters
101    given in OPTSTRING.
102 
103    If an element of ARGV starts with '-', and is not exactly "-" or "--",
104    then it is an option element.  The characters of this element
105    (aside from the initial '-') are option characters.  If `getopt'
106    is called repeatedly, it returns successively each of the option characters
107    from each of the option elements.
108 
109    If `getopt' finds another option character, it returns that character,
110    updating `optind' and `nextchar' so that the next call to `getopt' can
111    resume the scan with the following option character or ARGV-element.
112 
113    If there are no more option characters, `getopt' returns `EOF'.
114    Then `optind' is the index in ARGV of the first ARGV-element
115    that is not an option.  (The ARGV-elements have been permuted
116    so that those that are not options now come last.)
117 
118    OPTSTRING is a string containing the legitimate option characters.
119    If an option character is seen that is not listed in OPTSTRING,
120    return BAD_OPTION after printing an error message.  If you set `opterr' to
121    zero, the error message is suppressed but we still return BAD_OPTION.
122 
123    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
124    so the following text in the same ARGV-element, or the text of the following
125    ARGV-element, is returned in `optarg'.  Two colons mean an option that
126    wants an optional arg; if there is text in the current ARGV-element,
127    it is returned in `optarg', otherwise `optarg' is set to zero.
128 
129    If OPTSTRING starts with `-' or `+', it requests different methods of
130    handling the non-option ARGV-elements.
131    See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
132 
133    Long-named options begin with `--' instead of `-'.
134    Their names may be abbreviated as long as the abbreviation is unique
135    or is an exact match for some defined option.  If they have an
136    argument, it follows the option name in the same ARGV-element, separated
137    from the option name by a `=', or else the in next ARGV-element.
138    When `getopt' finds a long-named option, it returns 0 if that option's
139    `flag' field is nonzero, the value of the option's `val' field
140    if the `flag' field is zero.
141 
142    The elements of ARGV aren't really const, because we permute them.
143    But we pretend they're const in the prototype to be compatible
144    with other systems.
145 
146    LONGOPTS is a vector of `struct option' terminated by an
147    element containing a name which is zero.
148 
149    LONGIND returns the index in LONGOPT of the long-named option found.
150    It is only valid when a long-named option has been found by the most
151    recent call.
152 
153    If LONG_ONLY is nonzero, '-' as well as '--' can introduce
154    long-named options.  */
155 
156 int
_getopt_internal(optarg_ctx * o,int argc,WCHAR * const * argv,const WCHAR * optstring,const struct option * longopts,int * longind,int long_only)157 _getopt_internal(
158     optarg_ctx* o,
159     int argc,
160     WCHAR *const *argv,
161     const WCHAR *optstring,
162     const struct option *longopts,
163     int *longind,
164     int long_only)
165 {
166     int option_index;
167 
168     o->optarg = 0;
169 
170     /* Initialize the internal data when the first call is made.
171        Start processing options with ARGV-element 1 (since ARGV-element 0
172        is the program name); the sequence of previously skipped
173        non-option ARGV-elements is empty.  */
174 
175     if (o->optind == 0)
176     {
177         o->first_nonopt = o->last_nonopt = o->optind = 1;
178 
179         o->nextchar = NULL;
180 
181         /* Determine how to handle the ordering of options and nonoptions.  */
182 
183         if (optstring[0] == '-') {
184             ordering = RETURN_IN_ORDER;
185             ++optstring;
186         } else if (optstring[0] == '+') {
187             ordering = REQUIRE_ORDER;
188             ++optstring;
189 /*        } else if (getenv ("POSIXLY_CORRECT") != NULL) {
190             ordering = REQUIRE_ORDER;*/
191         } else {
192             ordering = PERMUTE;
193         }
194     }
195 
196     if (o->nextchar == NULL || *(o->nextchar) == '\0')
197     {
198         if (ordering == PERMUTE)
199         {
200             /* If we have just processed some options following some non-options,
201                exchange them so that the options come first.  */
202 
203             if (o->first_nonopt != o->last_nonopt && o->last_nonopt != o->optind) {
204                 exchange (o, (WCHAR **) argv);
205             } else if (o->last_nonopt != o->optind) {
206                 o->first_nonopt = o->optind;
207             }
208 
209             /* Now skip any additional non-options
210                and extend the range of non-options previously skipped.  */
211 
212             while (o->optind < argc
213                    && (argv[o->optind][0] != '-' || argv[o->optind][1] == '\0')
214                   ) {
215                 o->optind++;
216             }
217             o->last_nonopt = o->optind;
218         }
219 
220         /* Special ARGV-element `--' means premature end of options.
221        Skip it like a null option,
222        then exchange with previous non-options as if it were an option,
223        then skip everything else like a non-option.  */
224 
225         if (o->optind != argc && !my_strcmp (argv[o->optind], L"--"))
226         {
227             o->optind++;
228 
229             if (o->first_nonopt != o->last_nonopt && o->last_nonopt != o->optind) {
230                 exchange (o, (WCHAR **) argv);
231             } else if (o->first_nonopt == o->last_nonopt) {
232                 o->first_nonopt = o->optind;
233             }
234             o->last_nonopt = argc;
235 
236             o->optind = argc;
237         }
238 
239         /* If we have done all the ARGV-elements, stop the scan
240         and back over any non-options that we skipped and permuted.  */
241 
242         if (o->optind == argc)
243         {
244             /* Set the next-arg-index to point at the non-options
245                that we previously skipped, so the caller will digest them.  */
246             if (o->first_nonopt != o->last_nonopt)
247                 o->optind = o->first_nonopt;
248             return EOF;
249         }
250 
251         /* If we have come to a non-option and did not permute it,
252        either stop the scan or describe it to the caller and pass it by.  */
253 
254         if ((argv[o->optind][0] != '-' || argv[o->optind][1] == '\0'))
255         {
256             if (ordering == REQUIRE_ORDER)
257                 return EOF;
258             o->optarg = argv[o->optind++];
259             return 1;
260         }
261 
262          /* We have found another option-ARGV-element.
263         Start decoding its characters.  */
264         o->nextchar = (argv[o->optind] + 1
265             + (longopts != NULL && argv[o->optind][1] == '-'));
266     }
267 
268     if (longopts != NULL
269         && ((argv[o->optind][0] == '-'
270          && (argv[o->optind][1] == '-' || long_only))
271         ))
272     {
273         const struct option *p;
274         WCHAR *s = o->nextchar;
275         int exact = 0;
276         int ambig = 0;
277         const struct option *pfound = NULL;
278         int indfound = 0;
279 
280         while (*s && *s != '=')
281             s++;
282 
283         /* Test all options for either exact match or abbreviated matches.  */
284         for (p = longopts, option_index = 0;
285              p->name;
286              p++, option_index++)
287         if ( (p->val) && (!my_strncmp (p->name, o->nextchar, s - o->nextchar)) )
288         {
289             if (s - o->nextchar == (int)my_strlen (p->name))
290             {
291                 /* Exact match found.  */
292                 pfound = p;
293                 indfound = option_index;
294                 exact = 1;
295                 break;
296             } else if (pfound == NULL) {
297                 /* First nonexact match found.  */
298                 pfound = p;
299                 indfound = option_index;
300             } else {
301               /* Second nonexact match found.  */
302                 ambig = 1;
303             }
304         }
305 
306         if (ambig && !exact) {
307             if (o->opterr) {
308                 UDFPrint(("%ws: option `%s' is ambiguous\n",
309                      argv[0], argv[o->optind]));
310             }
311             o->nextchar += my_strlen (o->nextchar);
312             o->optind++;
313             return BAD_OPTION;
314         }
315 
316         if (pfound != NULL)
317         {
318             option_index = indfound;
319             o->optind++;
320             if (*s) {
321                 /* Don't test has_arg with >, because some C compilers don't
322                allow it to be used on enums.  */
323                 if (pfound->has_arg) {
324                     o->optarg = s + 1;
325                 } else {
326                     if (o->opterr) {
327                         if (argv[o->optind - 1][1] == '-') {
328                             /* --option */
329                             UDFPrint((
330                                  "%ws: option `--%ws' doesn't allow an argument\n",
331                                  argv[0], pfound->name));
332                         } else {
333                             /* +option or -option */
334                             UDFPrint((
335                                  "%ws: option `%c%ws' doesn't allow an argument\n",
336                                  argv[0], argv[o->optind - 1][0], pfound->name));
337                         }
338                     }
339                     o->nextchar += my_strlen (o->nextchar);
340                     return BAD_OPTION;
341                 }
342             }
343             else if (pfound->has_arg == 1)
344             {
345                 if (o->optind < argc) {
346                     o->optarg = argv[(o->optind)++];
347                 } else {
348                     if (o->opterr)
349                         UDFPrint(("%ws: option `%ws' requires an argument\n",
350                            argv[0], argv[o->optind - 1]));
351                     o->nextchar += my_strlen (o->nextchar);
352                     return optstring[0] == ':' ? ':' : BAD_OPTION;
353                 }
354             }
355             o->nextchar += my_strlen (o->nextchar);
356             if (longind != NULL)
357                 *longind = option_index;
358             if (pfound->flag) {
359                 *(pfound->flag) = pfound->val;
360                 return 0;
361             }
362             return pfound->val;
363         }
364          /* Can't find it as a long option.  If this is not getopt_long_only,
365         or the option starts with '--' or is not a valid short
366         option, then it's an error.
367         Otherwise interpret it as a short option.  */
368         if (!long_only || argv[o->optind][1] == '-'
369             || my_index (optstring, *(o->nextchar)) == NULL)
370         {
371             if (o->opterr)
372             {
373                 if (argv[o->optind][1] == '-') {
374                     /* --option */
375                     UDFPrint(("%ws: unrecognized option `--%ws'\n",
376                          argv[0], o->nextchar));
377                 } else {
378                     /* +option or -option */
379                     UDFPrint(("%ws: unrecognized option `%c%ws'\n",
380                          argv[0], argv[o->optind][0], o->nextchar));
381                 }
382             }
383             o->nextchar = (WCHAR *) L"";
384             o->optind++;
385             return BAD_OPTION;
386         }
387     }
388 
389     /* Look at and handle the next option-character.  */
390 
391     {
392         WCHAR c = *(o->nextchar)++;
393         WCHAR *temp = my_index (optstring, c);
394 
395         /* Increment `optind' when we start to process its last character.  */
396         if (*(o->nextchar) == '\0')
397           ++(o->optind);
398 
399         if (temp == NULL || c == ':')
400         {
401             if (o->opterr)
402             {
403                 UDFPrint(("%ws: illegal option -- %c\n", argv[0], c));
404             }
405             o->optopt = c;
406             return BAD_OPTION;
407         }
408         if (temp[1] == ':')
409         {
410             if (temp[2] == ':')
411             {
412                 /* This is an option that accepts an argument optionally.  */
413                 if (*(o->nextchar) != '\0') {
414                     o->optarg = o->nextchar;
415                     o->optind++;
416                 } else {
417                     o->optarg = 0;
418                 }
419                 o->nextchar = NULL;
420             }
421             else
422             {
423                 /* This is an option that requires an argument.  */
424                 if (*(o->nextchar) != '\0')
425                 {
426                     o->optarg = o->nextchar;
427                     /* If we end this ARGV-element by taking the rest as an arg,
428                        we must advance to the next element now.  */
429                     o->optind++;
430                 }
431                 else if (o->optind == argc)
432                 {
433                     if (o->opterr)
434                     {
435                         UDFPrint(("%ws: option requires an argument -- %c\n",
436                              argv[0], c));
437                     }
438                     o->optopt = c;
439                     if (optstring[0] == ':') {
440                         c = ':';
441                     } else {
442                         c = BAD_OPTION;
443                     }
444                 }
445                 else
446                 {
447                     /* We already incremented `optind' once;
448                    increment it again when taking next ARGV-elt as argument.  */
449                     o->optarg = argv[o->optind++];
450                 }
451                 o->nextchar = NULL;
452             }
453         }
454         return c;
455     }
456 }
457 
458 int
getopt(optarg_ctx * o,int argc,WCHAR * const * argv,const WCHAR * optstring)459 getopt (
460     optarg_ctx* o,
461     int argc,
462     WCHAR *const *argv,
463     const WCHAR *optstring)
464 {
465     return _getopt_internal (o, argc, argv, optstring,
466                (const struct option *) 0,
467                (int *) 0,
468                0);
469 }
470 
471 int
getopt_long(optarg_ctx * o,int argc,WCHAR * const * argv,const WCHAR * options,const struct option * long_options,int * opt_index)472 getopt_long (
473     optarg_ctx* o,
474     int argc,
475     WCHAR *const *argv,
476     const WCHAR *options,
477     const struct option *long_options,
478     int *opt_index)
479 {
480   return _getopt_internal (o, argc, argv, options, long_options, opt_index, 0);
481 }
482 
483 
484 #ifdef  __cplusplus
485 }
486 #endif
487