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