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