]> git.sesse.net Git - x264/blob - extras/getopt.c
Deblock B-frames. (Not yet used, since B-frames aren't kept as references.)
[x264] / extras / getopt.c
1 /*      $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $      */
2
3 /*-
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Dieter Baron and Thomas Klausner.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <getopt.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46
47 #define REPLACE_GETOPT
48
49 #define _DIAGASSERT(x) do {} while (0)
50
51 #ifdef REPLACE_GETOPT
52 #ifdef __weak_alias
53 __weak_alias(getopt,_getopt)
54 #endif
55 int opterr = 1; /* if error message should be printed */
56 int optind = 1; /* index into parent argv vector */
57 int optopt = '?';       /* character checked for validity */
58 int optreset;   /* reset getopt */
59 char *optarg;   /* argument associated with option */
60 #endif
61
62 #ifdef __weak_alias
63 __weak_alias(getopt_long,_getopt_long)
64 #endif
65
66 #ifndef __CYGWIN__
67 #define __progname __argv[0]
68 #else
69 extern char *__progname;
70 #endif
71
72 #define IGNORE_FIRST    (*options == '-' || *options == '+')
73 #define PRINT_ERROR     ((opterr) && ((*options != ':') \
74                                       || (IGNORE_FIRST && options[1] != ':')))
75
76 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_INCORRECT_GETOPT") == NULL)
77
78 #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
79 /* XXX: GNU ignores PC if *options == '-' */
80 #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
81
82 /* return values */
83 #define BADCH   (int)'?'
84 #define BADARG          ((IGNORE_FIRST && options[1] == ':') \
85                          || (*options == ':') ? (int)':' : (int)'?')
86 #define INORDER (int)1
87
88 static char EMSG[1];
89
90 static int getopt_internal (int, char * const *, const char *);
91 static int gcd (int, int);
92 static void permute_args (int, int, int, char * const *);
93
94 static char *place = EMSG; /* option letter processing */
95
96 /* XXX: set optreset to 1 rather than these two */
97 static int nonopt_start = -1; /* first non option argument (for permute) */
98 static int nonopt_end = -1;   /* first option after non options (for permute) */
99
100 /* Error messages */
101 static const char recargchar[] = "option requires an argument -- %c";
102 static const char recargstring[] = "option requires an argument -- %s";
103 static const char ambig[] = "ambiguous option -- %.*s";
104 static const char noarg[] = "option doesn't take an argument -- %.*s";
105 static const char illoptchar[] = "unknown option -- %c";
106 static const char illoptstring[] = "unknown option -- %s";
107
108 static void
109 _vwarnx(const char *fmt, va_list ap)
110 {
111   (void)fprintf(stderr, "%s: ", __progname);
112   if (fmt != NULL)
113     (void)vfprintf(stderr, fmt, ap);
114   (void)fprintf(stderr, "\n");
115 }
116
117 static void
118 warnx(const char *fmt, ...)
119 {
120   va_list ap;
121   va_start(ap, fmt);
122   _vwarnx(fmt, ap);
123   va_end(ap);
124 }
125
126 /*
127  * Compute the greatest common divisor of a and b.
128  */
129 static int
130 gcd(a, b)
131         int a;
132         int b;
133 {
134         int c;
135
136         c = a % b;
137         while (c != 0) {
138                 a = b;
139                 b = c;
140                 c = a % b;
141         }
142
143         return b;
144 }
145
146 /*
147  * Exchange the block from nonopt_start to nonopt_end with the block
148  * from nonopt_end to opt_end (keeping the same order of arguments
149  * in each block).
150  */
151 static void
152 permute_args(panonopt_start, panonopt_end, opt_end, nargv)
153         int panonopt_start;
154         int panonopt_end;
155         int opt_end;
156         char * const *nargv;
157 {
158         int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
159         char *swap;
160
161         _DIAGASSERT(nargv != NULL);
162
163         /*
164          * compute lengths of blocks and number and size of cycles
165          */
166         nnonopts = panonopt_end - panonopt_start;
167         nopts = opt_end - panonopt_end;
168         ncycle = gcd(nnonopts, nopts);
169         cyclelen = (opt_end - panonopt_start) / ncycle;
170
171         for (i = 0; i < ncycle; i++) {
172                 cstart = panonopt_end+i;
173                 pos = cstart;
174                 for (j = 0; j < cyclelen; j++) {
175                         if (pos >= panonopt_end)
176                                 pos -= nnonopts;
177                         else
178                                 pos += nopts;
179                         swap = nargv[pos];
180                         /* LINTED const cast */
181                         ((char **) nargv)[pos] = nargv[cstart];
182                         /* LINTED const cast */
183                         ((char **)nargv)[cstart] = swap;
184                 }
185         }
186 }
187
188 /*
189  * getopt_internal --
190  *      Parse argc/argv argument vector.  Called by user level routines.
191  *  Returns -2 if -- is found (can be long option or end of options marker).
192  */
193 static int
194 getopt_internal(nargc, nargv, options)
195         int nargc;
196         char * const *nargv;
197         const char *options;
198 {
199         char *oli;                              /* option letter list index */
200         int optchar;
201
202         _DIAGASSERT(nargv != NULL);
203         _DIAGASSERT(options != NULL);
204
205         optarg = NULL;
206
207         /*
208          * XXX Some programs (like rsyncd) expect to be able to
209          * XXX re-initialize optind to 0 and have getopt_long(3)
210          * XXX properly function again.  Work around this braindamage.
211          */
212         if (optind == 0)
213                 optind = 1;
214
215         if (optreset)
216                 nonopt_start = nonopt_end = -1;
217 start:
218         if (optreset || !*place) {              /* update scanning pointer */
219                 optreset = 0;
220                 if (optind >= nargc) {          /* end of argument vector */
221                         place = EMSG;
222                         if (nonopt_end != -1) {
223                                 /* do permutation, if we have to */
224                                 permute_args(nonopt_start, nonopt_end,
225                                     optind, nargv);
226                                 optind -= nonopt_end - nonopt_start;
227                         }
228                         else if (nonopt_start != -1) {
229                                 /*
230                                  * If we skipped non-options, set optind
231                                  * to the first of them.
232                                  */
233                                 optind = nonopt_start;
234                         }
235                         nonopt_start = nonopt_end = -1;
236                         return -1;
237                 }
238                 if ((*(place = nargv[optind]) != '-')
239                     || (place[1] == '\0')) {    /* found non-option */
240                         place = EMSG;
241                         if (IN_ORDER) {
242                                 /*
243                                  * GNU extension:
244                                  * return non-option as argument to option 1
245                                  */
246                                 optarg = nargv[optind++];
247                                 return INORDER;
248                         }
249                         if (!PERMUTE) {
250                                 /*
251                                  * if no permutation wanted, stop parsing
252                                  * at first non-option
253                                  */
254                                 return -1;
255                         }
256                         /* do permutation */
257                         if (nonopt_start == -1)
258                                 nonopt_start = optind;
259                         else if (nonopt_end != -1) {
260                                 permute_args(nonopt_start, nonopt_end,
261                                     optind, nargv);
262                                 nonopt_start = optind -
263                                     (nonopt_end - nonopt_start);
264                                 nonopt_end = -1;
265                         }
266                         optind++;
267                         /* process next argument */
268                         goto start;
269                 }
270                 if (nonopt_start != -1 && nonopt_end == -1)
271                         nonopt_end = optind;
272                 if (place[1] && *++place == '-') {      /* found "--" */
273                         place++;
274                         return -2;
275                 }
276         }
277         if ((optchar = (int)*place++) == (int)':' ||
278             (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
279                 /* option letter unknown or ':' */
280                 if (!*place)
281                         ++optind;
282                 if (PRINT_ERROR)
283                         warnx(illoptchar, optchar);
284                 optopt = optchar;
285                 return BADCH;
286         }
287         if (optchar == 'W' && oli[1] == ';') {          /* -W long-option */
288                 /* XXX: what if no long options provided (called by getopt)? */
289                 if (*place)
290                         return -2;
291
292                 if (++optind >= nargc) {        /* no arg */
293                         place = EMSG;
294                         if (PRINT_ERROR)
295                                 warnx(recargchar, optchar);
296                         optopt = optchar;
297                         return BADARG;
298                 } else                          /* white space */
299                         place = nargv[optind];
300                 /*
301                  * Handle -W arg the same as --arg (which causes getopt to
302                  * stop parsing).
303                  */
304                 return -2;
305         }
306         if (*++oli != ':') {                    /* doesn't take argument */
307                 if (!*place)
308                         ++optind;
309         } else {                                /* takes (optional) argument */
310                 optarg = NULL;
311                 if (*place)                     /* no white space */
312                         optarg = place;
313                 /* XXX: disable test for :: if PC? (GNU doesn't) */
314                 else if (oli[1] != ':') {       /* arg not optional */
315                         if (++optind >= nargc) {        /* no arg */
316                                 place = EMSG;
317                                 if (PRINT_ERROR)
318                                         warnx(recargchar, optchar);
319                                 optopt = optchar;
320                                 return BADARG;
321                         } else
322                                 optarg = nargv[optind];
323                 }
324                 place = EMSG;
325                 ++optind;
326         }
327         /* dump back option letter */
328         return optchar;
329 }
330
331 #ifdef REPLACE_GETOPT
332 /*
333  * getopt --
334  *      Parse argc/argv argument vector.
335  *
336  * [eventually this will replace the real getopt]
337  */
338 int
339 getopt(nargc, nargv, options)
340         int nargc;
341         char * const *nargv;
342         const char *options;
343 {
344         int retval;
345
346         _DIAGASSERT(nargv != NULL);
347         _DIAGASSERT(options != NULL);
348
349         if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
350                 ++optind;
351                 /*
352                  * We found an option (--), so if we skipped non-options,
353                  * we have to permute.
354                  */
355                 if (nonopt_end != -1) {
356                         permute_args(nonopt_start, nonopt_end, optind,
357                                        nargv);
358                         optind -= nonopt_end - nonopt_start;
359                 }
360                 nonopt_start = nonopt_end = -1;
361                 retval = -1;
362         }
363         return retval;
364 }
365 #endif
366
367 /*
368  * getopt_long --
369  *      Parse argc/argv argument vector.
370  */
371 int
372 getopt_long(nargc, nargv, options, long_options, idx)
373         int nargc;
374         char * const *nargv;
375         const char *options;
376         const struct option *long_options;
377         int *idx;
378 {
379         int retval;
380
381         _DIAGASSERT(nargv != NULL);
382         _DIAGASSERT(options != NULL);
383         _DIAGASSERT(long_options != NULL);
384         /* idx may be NULL */
385
386         if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
387                 char *current_argv, *has_equal;
388                 size_t current_argv_len;
389                 int i, match;
390
391                 current_argv = place;
392                 match = -1;
393
394                 optind++;
395                 place = EMSG;
396
397                 if (*current_argv == '\0') {            /* found "--" */
398                         /*
399                          * We found an option (--), so if we skipped
400                          * non-options, we have to permute.
401                          */
402                         if (nonopt_end != -1) {
403                                 permute_args(nonopt_start, nonopt_end,
404                                     optind, nargv);
405                                 optind -= nonopt_end - nonopt_start;
406                         }
407                         nonopt_start = nonopt_end = -1;
408                         return -1;
409                 }
410                 if ((has_equal = strchr(current_argv, '=')) != NULL) {
411                         /* argument found (--option=arg) */
412                         current_argv_len = has_equal - current_argv;
413                         has_equal++;
414                 } else
415                         current_argv_len = strlen(current_argv);
416
417                 for (i = 0; long_options[i].name; i++) {
418                         /* find matching long option */
419                         if (strncmp(current_argv, long_options[i].name,
420                             current_argv_len))
421                                 continue;
422
423                         if (strlen(long_options[i].name) ==
424                             (unsigned)current_argv_len) {
425                                 /* exact match */
426                                 match = i;
427                                 break;
428                         }
429                         if (match == -1)                /* partial match */
430                                 match = i;
431                         else {
432                                 /* ambiguous abbreviation */
433                                 if (PRINT_ERROR)
434                                         warnx(ambig, (int)current_argv_len,
435                                              current_argv);
436                                 optopt = 0;
437                                 return BADCH;
438                         }
439                 }
440                 if (match != -1) {                      /* option found */
441                         if (long_options[match].has_arg == no_argument
442                             && has_equal) {
443                                 if (PRINT_ERROR)
444                                         warnx(noarg, (int)current_argv_len,
445                                              current_argv);
446                                 /*
447                                  * XXX: GNU sets optopt to val regardless of
448                                  * flag
449                                  */
450                                 if (long_options[match].flag == NULL)
451                                         optopt = long_options[match].val;
452                                 else
453                                         optopt = 0;
454                                 return BADARG;
455                         }
456                         if (long_options[match].has_arg == required_argument ||
457                             long_options[match].has_arg == optional_argument) {
458                                 if (has_equal)
459                                         optarg = has_equal;
460                                 else if (long_options[match].has_arg ==
461                                     required_argument) {
462                                         /*
463                                          * optional argument doesn't use
464                                          * next nargv
465                                          */
466                                         optarg = nargv[optind++];
467                                 }
468                         }
469                         if ((long_options[match].has_arg == required_argument)
470                             && (optarg == NULL)) {
471                                 /*
472                                  * Missing argument; leading ':'
473                                  * indicates no error should be generated
474                                  */
475                                 if (PRINT_ERROR)
476                                         warnx(recargstring, current_argv);
477                                 /*
478                                  * XXX: GNU sets optopt to val regardless
479                                  * of flag
480                                  */
481                                 if (long_options[match].flag == NULL)
482                                         optopt = long_options[match].val;
483                                 else
484                                         optopt = 0;
485                                 --optind;
486                                 return BADARG;
487                         }
488                 } else {                        /* unknown option */
489                         if (PRINT_ERROR)
490                                 warnx(illoptstring, current_argv);
491                         optopt = 0;
492                         return BADCH;
493                 }
494                 if (long_options[match].flag) {
495                         *long_options[match].flag = long_options[match].val;
496                         retval = 0;
497                 } else
498                         retval = long_options[match].val;
499                 if (idx)
500                         *idx = match;
501         }
502         return retval;
503 }