]> git.sesse.net Git - vlc/blob - src/config/getopt.c
8c5b89099a74aaea068ad8d51b5c5c4b3a8357df
[vlc] / src / config / getopt.c
1 /*****************************************************************************
2  * getopt_long()
3  *****************************************************************************
4  * Copyright (C) 1987-1997 Free Software Foundation, Inc.
5  * Copyright (C) 2005-2010 the VideoLAN team
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <vlc_common.h>
26
27 #include <stdio.h>
28 #include <string.h>
29
30 /* This version of `getopt' appears to the caller like standard Unix `getopt'
31    but it behaves differently for the user, since it allows the user
32    to intersperse the options with the other arguments.
33
34    As `getopt' works, it permutes the elements of ARGV so that,
35    when it is done, all the options precede everything else.  Thus
36    all application programs are extended to handle flexible argument order.
37
38    Setting the environment variable POSIXLY_CORRECT disables permutation.
39    Then the behavior is completely standard.
40
41    GNU application programs can use a third alternative mode in which
42    they can distinguish the relative order of options and other arguments.  */
43
44 #include "vlc_getopt.h"
45
46 /* For communication from `getopt' to the caller.
47    When `getopt' finds an option that takes an argument,
48    the argument value is returned here.
49    Also, when `ordering' is RETURN_IN_ORDER,
50    each non-option ARGV-element is returned here.  */
51
52 char *vlc_optarg = NULL;
53
54 /* Index in ARGV of the next element to be scanned.
55    This is used for communication to and from the caller
56    and for communication between successive calls to `getopt'.
57
58    On entry to `getopt', zero means this is the first call; initialize.
59
60    When `getopt' returns -1, this is the index of the first of the
61    non-option elements that the caller should itself scan.
62
63    Otherwise, `optind' communicates from one call to the next
64    how much of ARGV has been scanned so far.  */
65
66 /* 1003.2 says this must be 1 before any call.  */
67 int vlc_optind = 1;
68
69 /* The next char to be scanned in the option-element
70    in which the last option character we returned was found.
71    This allows us to pick up the scan where we left off.
72
73    If this is zero, or a null string, it means resume the scan
74    by advancing to the next ARGV-element.  */
75
76 static char *nextchar;
77
78 /* Set to an option character which was unrecognized.
79    This must be initialized on some systems to avoid linking in the
80    system's own getopt implementation.  */
81
82 int vlc_optopt = '?';
83
84 /* Describe how to deal with options that follow non-option ARGV-elements.
85
86    If the caller did not specify anything,
87    the default is REQUIRE_ORDER if the environment variable
88    POSIXLY_CORRECT is defined, PERMUTE otherwise.
89
90    REQUIRE_ORDER means don't recognize them as options;
91    stop option processing when the first non-option is seen.
92    This is what Unix does.
93    This mode of operation is selected by either setting the environment
94    variable POSIXLY_CORRECT, or using `+' as the first character
95    of the list of option characters.
96
97    PERMUTE is the default.  We permute the contents of ARGV as we scan,
98    so that eventually all the non-options are at the end.  This allows options
99    to be given in any order, even with programs that were not written to
100    expect this.
101
102    RETURN_IN_ORDER is an option available to programs that were written
103    to expect options and other ARGV-elements in any order and that care about
104    the ordering of the two.  We describe each non-option ARGV-element
105    as if it were the argument of an option with character code 1.
106    Using `-' as the first character of the list of option characters
107    selects this mode of operation.
108
109    The special argument `--' forces an end of option-scanning regardless
110    of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
111    `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
112
113 static enum
114 {
115     REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
116 }
117 ordering;
118
119 /* Value of POSIXLY_CORRECT environment variable.  */
120 static char *posixly_correct;
121
122 /* Handle permutation of arguments.  */
123
124 /* Describe the part of ARGV that contains non-options that have
125    been skipped.  `first_nonopt' is the index in ARGV of the first of them;
126    `last_nonopt' is the index after the last of them.  */
127
128 static int first_nonopt;
129 static int last_nonopt;
130
131 /* Exchange two adjacent subsequences of ARGV.
132    One subsequence is elements [first_nonopt,last_nonopt)
133    which contains all the non-options that have been skipped so far.
134    The other is elements [last_nonopt,optind), which contains all
135    the options processed since those non-options were skipped.
136
137    `first_nonopt' and `last_nonopt' are relocated so that they describe
138    the new indices of the non-options in ARGV after they are moved.  */
139
140 static void exchange(char **);
141
142 static void
143      exchange(argv)
144      char **argv;
145 {
146     int bottom = first_nonopt;
147     int middle = last_nonopt;
148     int top = vlc_optind;
149     char *tem;
150
151     /* Exchange the shorter segment with the far end of the longer segment.
152        That puts the shorter segment into the right place.
153        It leaves the longer segment in the right place overall,
154        but it consists of two parts that need to be swapped next.  */
155
156     while (top > middle && middle > bottom)
157     {
158         if (top - middle > middle - bottom)
159         {
160             /* Bottom segment is the short one.  */
161             int len = middle - bottom;
162             register int i;
163
164             /* Swap it with the top part of the top segment.  */
165             for (i = 0; i < len; i++)
166             {
167                 tem = argv[bottom + i];
168                 argv[bottom + i] = argv[top - (middle - bottom) + i];
169                 argv[top - (middle - bottom) + i] = tem;
170             }
171             /* Exclude the moved bottom segment from further swapping.  */
172             top -= len;
173         }
174         else
175         {
176             /* Top segment is the short one.  */
177             int len = top - middle;
178             register int i;
179
180             /* Swap it with the bottom part of the bottom segment.  */
181             for (i = 0; i < len; i++)
182             {
183                 tem = argv[bottom + i];
184                 argv[bottom + i] = argv[middle + i];
185                 argv[middle + i] = tem;
186             }
187             /* Exclude the moved top segment from further swapping.  */
188             bottom += len;
189         }
190     }
191
192     /* Update records for the slots the non-options now occupy.  */
193
194     first_nonopt += (vlc_optind - last_nonopt);
195     last_nonopt = vlc_optind;
196 }
197
198 /* Initialize the internal data when the first call is made.  */
199
200 static const char *vlc_getopt_initialize(int, char *const *, const char *);
201
202 static const char *
203      vlc_getopt_initialize(argc, argv, optstring)
204      int argc;
205      char *const *argv;
206      const char *optstring;
207 {
208     (void)argc;
209     (void)argv;
210     /* Start processing options with ARGV-element 1 (since ARGV-element 0
211        is the program name); the sequence of previously skipped
212        non-option ARGV-elements is empty.  */
213
214     first_nonopt = last_nonopt = vlc_optind = 1;
215
216     nextchar = NULL;
217
218     posixly_correct = getenv("POSIXLY_CORRECT");
219
220     /* Determine how to handle the ordering of options and nonoptions.  */
221
222     if (optstring[0] == '-')
223     {
224         ordering = RETURN_IN_ORDER;
225         ++optstring;
226     }
227     else if (optstring[0] == '+')
228     {
229         ordering = REQUIRE_ORDER;
230         ++optstring;
231     }
232     else if (posixly_correct != NULL)
233         ordering = REQUIRE_ORDER;
234     else
235         ordering = PERMUTE;
236
237     return optstring;
238 }
239 \f
240 /* Scan elements of ARGV (whose length is ARGC) for option characters
241    given in OPTSTRING.
242
243    If an element of ARGV starts with '-', and is not exactly "-" or "--",
244    then it is an option element.  The characters of this element
245    (aside from the initial '-') are option characters.  If `getopt'
246    is called repeatedly, it returns successively each of the option characters
247    from each of the option elements.
248
249    If `getopt' finds another option character, it returns that character,
250    updating `optind' and `nextchar' so that the next call to `getopt' can
251    resume the scan with the following option character or ARGV-element.
252
253    If there are no more option characters, `getopt' returns -1.
254    Then `optind' is the index in ARGV of the first ARGV-element
255    that is not an option.  (The ARGV-elements have been permuted
256    so that those that are not options now come last.)
257
258    OPTSTRING is a string containing the legitimate option characters.
259    If an option character is seen that is not listed in OPTSTRING,
260    return '?'.
261
262    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
263    so the following text in the same ARGV-element, or the text of the following
264    ARGV-element, is returned in `optarg'.
265
266    If OPTSTRING starts with `-' or `+', it requests different methods of
267    handling the non-option ARGV-elements.
268    See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
269
270    Long-named options begin with `--' instead of `-'.
271    Their names may be abbreviated as long as the abbreviation is unique
272    or is an exact match for some defined option.  If they have an
273    argument, it follows the option name in the same ARGV-element, separated
274    from the option name by a `=', or else the in next ARGV-element.
275    When `getopt' finds a long-named option, it returns 0 if that option's
276    `flag' field is nonzero, the value of the option's `val' field
277    if the `flag' field is zero.
278
279    The elements of ARGV aren't really const, because we permute them.
280    But we pretend they're const in the prototype to be compatible
281    with other systems.
282
283    LONGOPTS is a vector of `struct option' terminated by an
284    element containing a name which is zero.
285
286    LONGIND returns the index in LONGOPT of the long-named option found.
287    It is only valid when a long-named option has been found by the most
288    recent call.  */
289
290 int
291     vlc_getopt_long(argc, argv, optstring, longopts, longind)
292      int argc;
293      char *const *argv;
294      const char *optstring;
295      const struct vlc_option *restrict longopts;
296      int *longind;
297 {
298     vlc_optarg = NULL;
299
300     if (vlc_optind == 0)
301     {
302         optstring = vlc_getopt_initialize(argc, argv, optstring);
303         vlc_optind = 1;    /* Don't scan ARGV[0], the program name.  */
304     }
305
306 #define NONOPTION_P (argv[vlc_optind][0] != '-' || argv[vlc_optind][1] == '\0')
307
308     if (nextchar == NULL || *nextchar == '\0')
309     {
310         /* Advance to the next ARGV-element.  */
311
312         /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
313            moved back by the user (who may also have changed the arguments).  */
314         if (last_nonopt > vlc_optind)
315             last_nonopt = vlc_optind;
316         if (first_nonopt > vlc_optind)
317             first_nonopt = vlc_optind;
318
319         if (ordering == PERMUTE)
320         {
321             /* If we have just processed some options following some non-options,
322                exchange them so that the options come first.  */
323
324             if (first_nonopt != last_nonopt && last_nonopt != vlc_optind)
325                 exchange((char **) argv);
326             else if (last_nonopt != vlc_optind)
327                 first_nonopt = vlc_optind;
328
329             /* Skip any additional non-options
330                and extend the range of non-options previously skipped.  */
331
332             while (vlc_optind < argc && NONOPTION_P)
333                 vlc_optind++;
334             last_nonopt = vlc_optind;
335         }
336
337         /* The special ARGV-element `--' means premature end of options.
338            Skip it like a null option,
339            then exchange with previous non-options as if it were an option,
340            then skip everything else like a non-option.  */
341
342         if (vlc_optind != argc && !strcmp(argv[vlc_optind], "--"))
343         {
344             vlc_optind++;
345
346             if (first_nonopt != last_nonopt && last_nonopt != vlc_optind)
347                 exchange((char **) argv);
348             else if (first_nonopt == last_nonopt)
349                 first_nonopt = vlc_optind;
350             last_nonopt = argc;
351
352             vlc_optind = argc;
353         }
354
355         /* If we have done all the ARGV-elements, stop the scan
356            and back over any non-options that we skipped and permuted.  */
357
358         if (vlc_optind == argc)
359         {
360             /* Set the next-arg-index to point at the non-options
361                that we previously skipped, so the caller will digest them.  */
362             if (first_nonopt != last_nonopt)
363                 vlc_optind = first_nonopt;
364             return -1;
365         }
366
367         /* If we have come to a non-option and did not permute it,
368            either stop the scan or describe it to the caller and pass it by.  */
369
370         if (NONOPTION_P)
371         {
372             if (ordering == REQUIRE_ORDER)
373                 return -1;
374             vlc_optarg = argv[vlc_optind++];
375             return 1;
376         }
377
378         /* We have found another option-ARGV-element.
379            Skip the initial punctuation.  */
380
381         nextchar = (argv[vlc_optind] + 1
382                 + (argv[vlc_optind][1] == '-'));
383     }
384
385     /* Decode the current option-ARGV-element.  */
386
387     /* Check whether the ARGV-element is a long option.  */
388
389     if (argv[vlc_optind][1] == '-')
390     {
391         char *nameend;
392         const struct vlc_option *p;
393         const struct vlc_option *pfound = NULL;
394         int exact = 0;
395         int ambig = 0;
396         int indfound = -1;
397         int option_index;
398
399         for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
400             /* Do nothing.  */ ;
401
402         /* Test all long options for either exact match
403            or abbreviated matches.  */
404         for (p = longopts, option_index = 0; p->name; p++, option_index++)
405             if (!strncmp(p->name, nextchar, nameend - nextchar))
406             {
407                 if ((unsigned int) (nameend - nextchar)
408                     == (unsigned int) strlen(p->name))
409                 {
410                     /* Exact match found.  */
411                     pfound = p;
412                     indfound = option_index;
413                     exact = 1;
414                     break;
415                 }
416                 else if (pfound == NULL)
417                 {
418                     /* First nonexact match found.  */
419                     pfound = p;
420                     indfound = option_index;
421                 }
422                 else
423                     /* Second or later nonexact match found.  */
424                     ambig = 1;
425             }
426
427         if (ambig && !exact)
428         {
429             nextchar += strlen(nextchar);
430             vlc_optind++;
431             vlc_optopt = 0;
432             return '?';
433         }
434
435         if (pfound != NULL)
436         {
437             option_index = indfound;
438             vlc_optind++;
439             if (*nameend)
440             {
441                 if (pfound->has_arg)
442                     vlc_optarg = nameend + 1;
443                 else
444                 {
445                     nextchar += strlen(nextchar);
446
447                     vlc_optopt = pfound->val;
448                     return '?';
449                 }
450             }
451             else if (pfound->has_arg)
452             {
453                 if (vlc_optind < argc)
454                     vlc_optarg = argv[vlc_optind++];
455                 else
456                 {
457                     nextchar += strlen(nextchar);
458                     vlc_optopt = pfound->val;
459                     return optstring[0] == ':' ? ':' : '?';
460                 }
461             }
462             nextchar += strlen(nextchar);
463             if (longind != NULL)
464                 *longind = option_index;
465             if (pfound->flag)
466             {
467                 *(pfound->flag) = pfound->val;
468                 return 0;
469             }
470             return pfound->val;
471         }
472
473         nextchar = (char *) "";
474         vlc_optind++;
475         vlc_optopt = 0;
476         return '?';
477     }
478
479     /* Look at and handle the next short option-character.  */
480
481     {
482         char c = *nextchar++;
483         char *temp = strchr(optstring, c);
484
485         /* Increment `optind' when we start to process its last character.  */
486         if (*nextchar == '\0')
487             ++vlc_optind;
488
489         if (temp == NULL || c == ':')
490         {
491             vlc_optopt = c;
492             return '?';
493         }
494         /* Convenience. Treat POSIX -W foo same as long option --foo */
495         if (temp[0] == 'W' && temp[1] == ';')
496         {
497             char *nameend;
498             const struct vlc_option *p;
499             const struct vlc_option *pfound = NULL;
500             int exact = 0;
501             int ambig = 0;
502             int indfound = 0;
503             int option_index;
504
505             /* This is an option that requires an argument.  */
506             if (*nextchar != '\0')
507             {
508                 vlc_optarg = nextchar;
509                 /* If we end this ARGV-element by taking the rest as an arg,
510                    we must advance to the next element now.  */
511                 vlc_optind++;
512             }
513             else if (vlc_optind == argc)
514             {
515                 vlc_optopt = c;
516                 if (optstring[0] == ':')
517                     c = ':';
518                 else
519                     c = '?';
520                 return c;
521             }
522             else
523                 /* We already incremented `optind' once;
524                    increment it again when taking next ARGV-elt as argument.  */
525                 vlc_optarg = argv[vlc_optind++];
526
527             /* optarg is now the argument, see if it's in the
528                table of longopts.  */
529
530             for (nextchar = nameend = vlc_optarg; *nameend && *nameend != '='; nameend++)
531                 /* Do nothing.  */ ;
532
533             /* Test all long options for either exact match
534                or abbreviated matches.  */
535             for (p = longopts, option_index = 0; p->name; p++, option_index++)
536                 if (!strncmp(p->name, nextchar, nameend - nextchar))
537                 {
538                     if ((unsigned int) (nameend - nextchar) == strlen(p->name))
539                     {
540                         /* Exact match found.  */
541                         pfound = p;
542                         indfound = option_index;
543                         exact = 1;
544                         break;
545                     }
546                     else if (pfound == NULL)
547                     {
548                         /* First nonexact match found.  */
549                         pfound = p;
550                         indfound = option_index;
551                     }
552                     else
553                         /* Second or later nonexact match found.  */
554                         ambig = 1;
555                 }
556             if (ambig && !exact)
557             {
558                 nextchar += strlen(nextchar);
559                 vlc_optind++;
560                 return '?';
561             }
562             if (pfound != NULL)
563             {
564                 option_index = indfound;
565                 if (*nameend)
566                 {
567                     if (pfound->has_arg)
568                         vlc_optarg = nameend + 1;
569                     else
570                     {
571                         nextchar += strlen(nextchar);
572                         return '?';
573                     }
574                 }
575                 else if (pfound->has_arg)
576                 {
577                     if (vlc_optind < argc)
578                         vlc_optarg = argv[vlc_optind++];
579                     else
580                     {
581                         nextchar += strlen(nextchar);
582                         return optstring[0] == ':' ? ':' : '?';
583                     }
584                 }
585                 nextchar += strlen(nextchar);
586                 if (longind != NULL)
587                     *longind = option_index;
588                 if (pfound->flag)
589                 {
590                     *(pfound->flag) = pfound->val;
591                     return 0;
592                 }
593                 return pfound->val;
594             }
595             nextchar = NULL;
596             return 'W';    /* Let the application handle it.   */
597         }
598         if (temp[1] == ':')
599         {
600             /* This is an option that requires an argument.  */
601             if (*nextchar != '\0')
602             {
603                 vlc_optarg = nextchar;
604                 /* If we end this ARGV-element by taking the rest as an arg,
605                    we must advance to the next element now.  */
606                 vlc_optind++;
607             }
608             else if (vlc_optind == argc)
609             {
610                 vlc_optopt = c;
611                 if (optstring[0] == ':')
612                     c = ':';
613                 else
614                     c = '?';
615             }
616             else
617                 /* We already incremented `optind' once;
618                    increment it again when taking next ARGV-elt as argument.  */
619                 vlc_optarg = argv[vlc_optind++];
620             nextchar = NULL;
621         }
622         return c;
623     }
624 }