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