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