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