]> wimlib.net Git - wimlib/blob - programs/wgetopt.c
Support for encrypted restore
[wimlib] / programs / wgetopt.c
1 /* 
2  * wgetopt.c:  Wide-character versions of getopt, getopt_long, and
3  * getopt_long_only.
4  *
5  * This has been modified from the original, which is
6  * Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
7  * 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
8  * 2010 Free Software Foundation, Inc.
9  *
10  * This file is part of wimlib, a library for working with WIM files.
11  *
12  * wimlib is free software; you can redistribute it and/or modify it under the
13  * terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your option)
15  * any later version.
16  *
17  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
19  * A PARTICULAR PURPOSE. See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with wimlib; if not, see http://www.gnu.org/licenses/.
24  */
25
26 #include "wgetopt.h"
27 #include <stdlib.h>
28 #include <stdio.h>
29
30 /* For communication from `getopt' to the caller.
31    When `getopt' finds an option that takes an argument,
32    the argument value is returned here.
33    Also, when `ordering' is RETURN_IN_ORDER,
34    each non-option ARGV-element is returned here.  */
35
36 wchar_t *woptarg = NULL;
37
38 /* Index in ARGV of the next element to be scanned.
39    This is used for communication to and from the caller
40    and for communication between successive calls to `getopt'.
41
42    On entry to `getopt', zero means this is the first call; initialize.
43
44    When `getopt' returns -1, this is the index of the first of the
45    non-option elements that the caller should itself scan.
46
47    Otherwise, `woptind' communicates from one call to the next
48    how much of ARGV has been scanned so far.  */
49
50 /* 1003.2 says this must be 1 before any call.  */
51 int woptind = 1;
52
53 /* Formerly, initialization of getopt depended on woptind==0, which
54    causes problems with re-calling getopt as programs generally don't
55    know that. */
56
57 int __getopt_initialized = 0;
58
59 /* The next char to be scanned in the option-element
60    in which the last option character we returned was found.
61    This allows us to pick up the scan where we left off.
62
63    If this is zero, or a null string, it means resume the scan
64    by advancing to the next ARGV-element.  */
65
66 static wchar_t *nextchar;
67
68 /* Callers store zero here to inhibit the error message
69    for unrecognized options.  */
70
71 int wopterr = 1;
72
73 /* Set to an option character which was unrecognized.
74    This must be initialized on some systems to avoid linking in the
75    system's own getopt implementation.  */
76
77 int woptopt = '?';
78
79 /* Describe how to deal with options that follow non-option ARGV-elements.
80
81    If the caller did not specify anything,
82    the default is REQUIRE_ORDER if the environment variable
83    POSIXLY_CORRECT is defined, PERMUTE otherwise.
84
85    REQUIRE_ORDER means don't recognize them as options;
86    stop option processing when the first non-option is seen.
87    This is what Unix does.
88    This mode of operation is selected by either setting the environment
89    variable POSIXLY_CORRECT, or using `+' as the first character
90    of the list of option characters.
91
92    PERMUTE is the default.  We permute the contents of ARGV as we scan,
93    so that eventually all the non-options are at the end.  This allows options
94    to be given in any order, even with programs that were not written to
95    expect this.
96
97    RETURN_IN_ORDER is an option available to programs that were written
98    to expect options and other ARGV-elements in any order and that care about
99    the ordering of the two.  We describe each non-option ARGV-element
100    as if it were the argument of an option with character code 1.
101    Using `-' as the first character of the list of option characters
102    selects this mode of operation.
103
104    The special argument `--' forces an end of option-scanning regardless
105    of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
106    `--' can cause `getopt' to return -1 with `woptind' != ARGC.  */
107
108 static enum
109 {
110   REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
111 } ordering;
112
113 /* Value of POSIXLY_CORRECT environment variable.  */
114 static char *posixly_correct;
115
116
117 /* Handle permutation of arguments.  */
118
119 /* Describe the part of ARGV that contains non-options that have
120    been skipped.  `first_nonopt' is the index in ARGV of the first of them;
121    `last_nonopt' is the index after the last of them.  */
122
123 static int first_nonopt;
124 static int last_nonopt;
125
126 /* Exchange two adjacent subsequences of ARGV.
127    One subsequence is elements [first_nonopt,last_nonopt)
128    which contains all the non-options that have been skipped so far.
129    The other is elements [last_nonopt,woptind), which contains all
130    the options processed since those non-options were skipped.
131
132    `first_nonopt' and `last_nonopt' are relocated so that they describe
133    the new indices of the non-options in ARGV after they are moved.  */
134
135 static void
136 exchange (wchar_t **argv)
137 {
138   int bottom = first_nonopt;
139   int middle = last_nonopt;
140   int top = woptind;
141   wchar_t *tem;
142
143   /* Exchange the shorter segment with the far end of the longer segment.
144      That puts the shorter segment into the right place.
145      It leaves the longer segment in the right place overall,
146      but it consists of two parts that need to be swapped next.  */
147
148   while (top > middle && middle > bottom)
149     {
150       if (top - middle > middle - bottom)
151         {
152           /* Bottom segment is the short one.  */
153           int len = middle - bottom;
154           register int i;
155
156           /* Swap it with the top part of the top segment.  */
157           for (i = 0; i < len; i++)
158             {
159               tem = argv[bottom + i];
160               argv[bottom + i] = argv[top - (middle - bottom) + i];
161               argv[top - (middle - bottom) + i] = tem;
162             }
163           /* Exclude the moved bottom segment from further swapping.  */
164           top -= len;
165         }
166       else
167         {
168           /* Top segment is the short one.  */
169           int len = top - middle;
170           register int i;
171
172           /* Swap it with the bottom part of the bottom segment.  */
173           for (i = 0; i < len; i++)
174             {
175               tem = argv[bottom + i];
176               argv[bottom + i] = argv[middle + i];
177               argv[middle + i] = tem;
178             }
179           /* Exclude the moved top segment from further swapping.  */
180           bottom += len;
181         }
182     }
183
184   /* Update records for the slots the non-options now occupy.  */
185
186   first_nonopt += (woptind - last_nonopt);
187   last_nonopt = woptind;
188 }
189
190 /* Initialize the internal data when the first call is made.  */
191
192 static const wchar_t *
193 _getopt_initialize (int argc, wchar_t *const *argv, const wchar_t *optstring)
194 {
195   /* Start processing options with ARGV-element 1 (since ARGV-element 0
196      is the program name); the sequence of previously skipped
197      non-option ARGV-elements is empty.  */
198
199   first_nonopt = last_nonopt = woptind;
200
201   nextchar = NULL;
202
203   posixly_correct = getenv ("POSIXLY_CORRECT");
204
205   /* Determine how to handle the ordering of options and nonoptions.  */
206
207   if (optstring[0] == L'-')
208     {
209       ordering = RETURN_IN_ORDER;
210       ++optstring;
211     }
212   else if (optstring[0] == L'+')
213     {
214       ordering = REQUIRE_ORDER;
215       ++optstring;
216     }
217   else if (posixly_correct != NULL)
218     ordering = REQUIRE_ORDER;
219   else
220     ordering = PERMUTE;
221
222   return optstring;
223 }
224 \f
225 /* Scan elements of ARGV (whose length is ARGC) for option characters
226    given in OPTSTRING.
227
228    If an element of ARGV starts with '-', and is not exactly "-" or "--",
229    then it is an option element.  The characters of this element
230    (aside from the initial '-') are option characters.  If `getopt'
231    is called repeatedly, it returns successively each of the option characters
232    from each of the option elements.
233
234    If `getopt' finds another option character, it returns that character,
235    updating `woptind' and `nextchar' so that the next call to `getopt' can
236    resume the scan with the following option character or ARGV-element.
237
238    If there are no more option characters, `getopt' returns -1.
239    Then `woptind' is the index in ARGV of the first ARGV-element
240    that is not an option.  (The ARGV-elements have been permuted
241    so that those that are not options now come last.)
242
243    OPTSTRING is a string containing the legitimate option characters.
244    If an option character is seen that is not listed in OPTSTRING,
245    return '?' after printing an error message.  If you set `wopterr' to
246    zero, the error message is suppressed but we still return '?'.
247
248    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
249    so the following text in the same ARGV-element, or the text of the following
250    ARGV-element, is returned in `woptarg'.  Two colons mean an option that
251    wants an optional arg; if there is text in the current ARGV-element,
252    it is returned in `woptarg', otherwise `woptarg' is set to zero.
253
254    If OPTSTRING starts with `-' or `+', it requests different methods of
255    handling the non-option ARGV-elements.
256    See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
257
258    Long-named options begin with `--' instead of `-'.
259    Their names may be abbreviated as long as the abbreviation is unique
260    or is an exact match for some defined option.  If they have an
261    argument, it follows the option name in the same ARGV-element, separated
262    from the option name by a `=', or else the in next ARGV-element.
263    When `getopt' finds a long-named option, it returns 0 if that option's
264    `flag' field is nonzero, the value of the option's `val' field
265    if the `flag' field is zero.
266
267    The elements of ARGV aren't really const, because we permute them.
268    But we pretend they're const in the prototype to be compatible
269    with other systems.
270
271    LONGOPTS is a vector of `struct woption' terminated by an
272    element containing a name which is zero.
273
274    LONGIND returns the index in LONGOPT of the long-named option found.
275    It is only valid when a long-named option has been found by the most
276    recent call.
277
278    If LONG_ONLY is nonzero, '-' as well as '--' can introduce
279    long-named options.  */
280
281 int
282 _wgetopt_internal (int argc, wchar_t *const *argv, const wchar_t *optstring,
283                   const struct woption *longopts, int *longind, int long_only)
284 {
285   woptarg = NULL;
286
287   if (woptind == 0 || !__getopt_initialized)
288     {
289       if (woptind == 0)
290         woptind = 1;    /* Don't scan ARGV[0], the program name.  */
291       optstring = _getopt_initialize (argc, argv, optstring);
292       __getopt_initialized = 1;
293     }
294
295   /* Test whether ARGV[woptind] points to a non-option argument.
296      Either it does not have option syntax, or there is an environment flag
297      from the shell indicating it is not an option.  The later information
298      is only used when the used in the GNU libc.  */
299 # define NONOPTION_P (argv[woptind][0] != L'-' || argv[woptind][1] == L'\0')
300
301   if (nextchar == NULL || *nextchar == '\0')
302     {
303       /* Advance to the next ARGV-element.  */
304
305       /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
306          moved back by the user (who may also have changed the arguments).  */
307       if (last_nonopt > woptind)
308         last_nonopt = woptind;
309       if (first_nonopt > woptind)
310         first_nonopt = woptind;
311
312       if (ordering == PERMUTE)
313         {
314           /* If we have just processed some options following some non-options,
315              exchange them so that the options come first.  */
316
317           if (first_nonopt != last_nonopt && last_nonopt != woptind)
318             exchange ((wchar_t **) argv);
319           else if (last_nonopt != woptind)
320             first_nonopt = woptind;
321
322           /* Skip any additional non-options
323              and extend the range of non-options previously skipped.  */
324
325           while (woptind < argc && NONOPTION_P)
326             woptind++;
327           last_nonopt = woptind;
328         }
329
330       /* The special ARGV-element `--' means premature end of options.
331          Skip it like a null option,
332          then exchange with previous non-options as if it were an option,
333          then skip everything else like a non-option.  */
334
335       if (woptind != argc && !wcscmp (argv[woptind], L"--"))
336         {
337           woptind++;
338
339           if (first_nonopt != last_nonopt && last_nonopt != woptind)
340             exchange ((wchar_t **) argv);
341           else if (first_nonopt == last_nonopt)
342             first_nonopt = woptind;
343           last_nonopt = argc;
344
345           woptind = argc;
346         }
347
348       /* If we have done all the ARGV-elements, stop the scan
349          and back over any non-options that we skipped and permuted.  */
350
351       if (woptind == argc)
352         {
353           /* Set the next-arg-index to point at the non-options
354              that we previously skipped, so the caller will digest them.  */
355           if (first_nonopt != last_nonopt)
356             woptind = first_nonopt;
357           return -1;
358         }
359
360       /* If we have come to a non-option and did not permute it,
361          either stop the scan or describe it to the caller and pass it by.  */
362
363       if (NONOPTION_P)
364         {
365           if (ordering == REQUIRE_ORDER)
366             return -1;
367           woptarg = argv[woptind++];
368           return 1;
369         }
370
371       /* We have found another option-ARGV-element.
372          Skip the initial punctuation.  */
373
374       nextchar = (argv[woptind] + 1
375                   + (longopts != NULL && argv[woptind][1] == L'-'));
376     }
377
378   /* Decode the current option-ARGV-element.  */
379
380   /* Check whether the ARGV-element is a long option.
381
382      If long_only and the ARGV-element has the form "-f", where f is
383      a valid short option, don't consider it an abbreviated form of
384      a long option that starts with f.  Otherwise there would be no
385      way to give the -f short option.
386
387      On the other hand, if there's a long option "fubar" and
388      the ARGV-element is "-fu", do consider that an abbreviation of
389      the long option, just like "--fu", and not "-f" with arg "u".
390
391      This distinction seems to be the most useful approach.  */
392
393   if (longopts != NULL
394       && (argv[woptind][1] == L'-'
395           || (long_only && (argv[woptind][2] || !wcschr (optstring, argv[woptind][1])))))
396     {
397       wchar_t *nameend;
398       const struct woption *p;
399       const struct woption *pfound = NULL;
400       int exact = 0;
401       int ambig = 0;
402       int indfound = -1;
403       int option_index;
404
405       for (nameend = nextchar; *nameend && *nameend != L'='; nameend++)
406         /* Do nothing.  */ ;
407
408       /* Test all long options for either exact match
409          or abbreviated matches.  */
410       for (p = longopts, option_index = 0; p->name; p++, option_index++)
411         if (!wcsncmp (p->name, nextchar, nameend - nextchar))
412           {
413             if ((unsigned int) (nameend - nextchar)
414                 == (unsigned int) wcslen (p->name))
415               {
416                 /* Exact match found.  */
417                 pfound = p;
418                 indfound = option_index;
419                 exact = 1;
420                 break;
421               }
422             else if (pfound == NULL)
423               {
424                 /* First nonexact match found.  */
425                 pfound = p;
426                 indfound = option_index;
427               }
428             else
429               /* Second or later nonexact match found.  */
430               ambig = 1;
431           }
432
433       if (ambig && !exact)
434         {
435           if (wopterr)
436             fwprintf (stderr, L"%ls: option `%ls' is ambiguous\n",
437                       argv[0], argv[woptind]);
438           nextchar += wcslen (nextchar);
439           woptind++;
440           woptopt = 0;
441           return L'?';
442         }
443
444       if (pfound != NULL)
445         {
446           option_index = indfound;
447           woptind++;
448           if (*nameend)
449             {
450               /* Don't test has_arg with >, because some C compilers don't
451                  allow it to be used on enums.  */
452               if (pfound->has_arg)
453                 woptarg = nameend + 1;
454               else
455                 {
456                   if (wopterr) {
457                    if (argv[woptind - 1][1] == L'-')
458                     /* --option */
459                     fwprintf (stderr,
460                      L"%ls: option `--%ls' doesn't allow an argument\n",
461                      argv[0], pfound->name);
462                    else
463                     /* +option or -option */
464                     fwprintf (stderr,
465                      L"%ls: option `%lc%ls' doesn't allow an argument\n",
466                      argv[0], argv[woptind - 1][0], pfound->name);
467                   }
468
469                   nextchar += wcslen (nextchar);
470
471                   woptopt = pfound->val;
472                   return L'?';
473                 }
474             }
475           else if (pfound->has_arg == 1)
476             {
477               if (woptind < argc)
478                 woptarg = argv[woptind++];
479               else
480                 {
481                   if (wopterr)
482                     fwprintf (stderr,
483                            L"%ls: option `%ls' requires an argument\n",
484                            argv[0], argv[woptind - 1]);
485                   nextchar += wcslen (nextchar);
486                   woptopt = pfound->val;
487                   return optstring[0] == L':' ? L':' : L'?';
488                 }
489             }
490           nextchar += wcslen (nextchar);
491           if (longind != NULL)
492             *longind = option_index;
493           if (pfound->flag)
494             {
495               *(pfound->flag) = pfound->val;
496               return 0;
497             }
498           return pfound->val;
499         }
500
501       /* Can't find it as a long option.  If this is not getopt_long_only,
502          or the option starts with '--' or is not a valid short
503          option, then it's an error.
504          Otherwise interpret it as a short option.  */
505       if (!long_only || argv[woptind][1] == L'-'
506           || wcschr (optstring, *nextchar) == NULL)
507         {
508           if (wopterr)
509             {
510               if (argv[woptind][1] == '-')
511                 /* --option */
512                 fwprintf (stderr, L"%ls: unrecognized option `--%ls'\n",
513                          argv[0], nextchar);
514               else
515                 /* +option or -option */
516                 fwprintf (stderr, L"%ls: unrecognized option `%lc%ls'\n",
517                          argv[0], argv[woptind][0], nextchar);
518             }
519           nextchar = (wchar_t *) L"";
520           woptind++;
521           woptopt = 0;
522           return L'?';
523         }
524     }
525
526   /* Look at and handle the next short option-character.  */
527
528   {
529     wchar_t c = *nextchar++;
530     wchar_t *temp = wcschr (optstring, c);
531
532     /* Increment `woptind' when we start to process its last character.  */
533     if (*nextchar == L'\0')
534       ++woptind;
535
536     if (temp == NULL || c == L':')
537       {
538         if (wopterr)
539           {
540             if (posixly_correct)
541               /* 1003.2 specifies the format of this message.  */
542               fwprintf (stderr, L"%ls: illegal option -- %lc\n",
543                        argv[0], c);
544             else
545               fwprintf (stderr, L"%ls: invalid option -- %lc\n",
546                        argv[0], c);
547           }
548         woptopt = c;
549         return L'?';
550       }
551     /* Convenience. Treat POSIX -W foo same as long option --foo */
552     if (temp[0] == L'W' && temp[1] == L';')
553       {
554         wchar_t *nameend;
555         const struct woption *p;
556         const struct woption *pfound = NULL;
557         int exact = 0;
558         int ambig = 0;
559         int indfound = 0;
560         int option_index;
561
562         /* This is an option that requires an argument.  */
563         if (*nextchar != L'\0')
564           {
565             woptarg = nextchar;
566             /* If we end this ARGV-element by taking the rest as an arg,
567                we must advance to the next element now.  */
568             woptind++;
569           }
570         else if (woptind == argc)
571           {
572             if (wopterr)
573               {
574                 /* 1003.2 specifies the format of this message.  */
575                 fwprintf (stderr, L"%ls: option requires an argument -- %lc\n",
576                          argv[0], c);
577               }
578             woptopt = c;
579             if (optstring[0] == L':')
580               c = L':';
581             else
582               c = L'?';
583             return c;
584           }
585         else
586           /* We already incremented `woptind' once;
587              increment it again when taking next ARGV-elt as argument.  */
588           woptarg = argv[woptind++];
589
590         /* woptarg is now the argument, see if it's in the
591            table of longopts.  */
592
593         for (nextchar = nameend = woptarg; *nameend && *nameend != L'='; nameend++)
594           /* Do nothing.  */ ;
595
596         /* Test all long options for either exact match
597            or abbreviated matches.  */
598         for (p = longopts, option_index = 0; p->name; p++, option_index++)
599           if (!wcsncmp (p->name, nextchar, nameend - nextchar))
600             {
601               if ((unsigned int) (nameend - nextchar) == wcslen (p->name))
602                 {
603                   /* Exact match found.  */
604                   pfound = p;
605                   indfound = option_index;
606                   exact = 1;
607                   break;
608                 }
609               else if (pfound == NULL)
610                 {
611                   /* First nonexact match found.  */
612                   pfound = p;
613                   indfound = option_index;
614                 }
615               else
616                 /* Second or later nonexact match found.  */
617                 ambig = 1;
618             }
619         if (ambig && !exact)
620           {
621             if (wopterr)
622               fwprintf (stderr, L"%ls: option `-W %ls' is ambiguous\n",
623                        argv[0], argv[woptind]);
624             nextchar += wcslen (nextchar);
625             woptind++;
626             return L'?';
627           }
628         if (pfound != NULL)
629           {
630             option_index = indfound;
631             if (*nameend)
632               {
633                 /* Don't test has_arg with >, because some C compilers don't
634                    allow it to be used on enums.  */
635                 if (pfound->has_arg)
636                   woptarg = nameend + 1;
637                 else
638                   {
639                     if (wopterr)
640                       fwprintf (stderr, L"\
641 %ls: option `-W %ls' doesn't allow an argument\n",
642                                argv[0], pfound->name);
643
644                     nextchar += wcslen (nextchar);
645                     return L'?';
646                   }
647               }
648             else if (pfound->has_arg == 1)
649               {
650                 if (woptind < argc)
651                   woptarg = argv[woptind++];
652                 else
653                   {
654                     if (wopterr)
655                       fwprintf (stderr,
656                                L"%ls: option `%ls' requires an argument\n",
657                                argv[0], argv[woptind - 1]);
658                     nextchar += wcslen (nextchar);
659                     return optstring[0] == L':' ? L':' : L'?';
660                   }
661               }
662             nextchar += wcslen (nextchar);
663             if (longind != NULL)
664               *longind = option_index;
665             if (pfound->flag)
666               {
667                 *(pfound->flag) = pfound->val;
668                 return 0;
669               }
670             return pfound->val;
671           }
672           nextchar = NULL;
673           return L'W';  /* Let the application handle it.   */
674       }
675     if (temp[1] == L':')
676       {
677         if (temp[2] == L':')
678           {
679             /* This is an option that accepts an argument optionally.  */
680             if (*nextchar != L'\0')
681               {
682                 woptarg = nextchar;
683                 woptind++;
684               }
685             else
686               woptarg = NULL;
687             nextchar = NULL;
688           }
689         else
690           {
691             /* This is an option that requires an argument.  */
692             if (*nextchar != L'\0')
693               {
694                 woptarg = nextchar;
695                 /* If we end this ARGV-element by taking the rest as an arg,
696                    we must advance to the next element now.  */
697                 woptind++;
698               }
699             else if (woptind == argc)
700               {
701                 if (wopterr)
702                   {
703                     /* 1003.2 specifies the format of this message.  */
704                     fwprintf (stderr,
705                              L"%ls: option requires an argument -- %lc\n",
706                              argv[0], c);
707                   }
708                 woptopt = c;
709                 if (optstring[0] == L':')
710                   c = L':';
711                 else
712                   c = L'?';
713               }
714             else
715               /* We already incremented `woptind' once;
716                  increment it again when taking next ARGV-elt as argument.  */
717               woptarg = argv[woptind++];
718             nextchar = NULL;
719           }
720       }
721     return c;
722   }
723 }
724
725 int
726 wgetopt (int argc, wchar_t *const *argv, const wchar_t *optstring)
727 {
728   return _wgetopt_internal (argc, argv, optstring,
729                             (const struct woption *) 0,
730                             (int *) 0,
731                             0);
732 }
733
734 int
735 wgetopt_long (int argc, wchar_t * const *argv, const wchar_t *options,
736               const struct woption *long_options, int *opt_index)
737 {
738   return _wgetopt_internal (argc, argv, options, long_options, opt_index, 0);
739 }
740
741 /* Like getopt_long, but '-' as well as '--' can indicate a long option.
742    If an option that starts with '-' (not '--') doesn't match a long option,
743    but does match a short option, it is parsed as a short option
744    instead.  */
745 int
746 wgetopt_long_only (int argc, wchar_t * const *argv, const wchar_t *options,
747                   const struct woption *long_options, int *opt_index)
748 {
749   return _wgetopt_internal (argc, argv, options, long_options, opt_index, 1);
750 }