]> git.proxmox.com Git - mirror_ovs.git/blame - lib/getopt_long.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / getopt_long.c
CommitLineData
9d0581fd
GS
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"
e6211adc 36#include "openvswitch/vlog.h"
9d0581fd
GS
37
38VLOG_DEFINE_THIS_MODULE(getopt_long);
39
40int opterr = 1; /* if error message should be printed */
41int optind = 1; /* index into parent argv vector */
42int optopt = '?'; /* character checked for validity */
43int optreset; /* reset getopt */
44char *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
9d0581fd
GS
62#define _DIAGASSERT(q) ovs_assert(q)
63
64#define warnx VLOG_WARN
65
66static int getopt_internal(int, char **, const char *);
67static int gcd(int, int);
68static void permute_args(int, int, int, char **);
69
70static const char *place = EMSG; /* option letter processing */
71
72/* XXX: set optreset to 1 rather than these two */
73static int nonopt_start = -1; /* first non option argument (for permute) */
74static int nonopt_end = -1; /* first option after non options (for permute) */
75
76/* Error messages */
77static const char recargchar[] = "option requires an argument -- %c";
78static const char recargstring[] = "option requires an argument -- %s";
79static const char ambig[] = "ambiguous option -- %.*s";
80static const char noarg[] = "option doesn't take an argument -- %.*s";
81static const char illoptchar[] = "unknown option -- %c";
82static const char illoptstring[] = "unknown option -- %s";
83
84
85/*
86 * Compute the greatest common divisor of a and b.
87 */
88static int
89gcd(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 */
108static void
109permute_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 */
144static int
145getopt_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;
165start:
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 */
b1bf47f2 260 optarg = CONST_CAST(char *, place);
9d0581fd
GS
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 */
286int
287getopt(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
b1bf47f2 297 retval = getopt_internal(nargc, CONST_CAST(char **, nargv), options);
9d0581fd
GS
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 */
320int
321getopt_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
b1bf47f2 336 retval = getopt_internal(nargc, CONST_CAST(char **, nargv), options);
9d0581fd
GS
337 if (retval == -2) {
338 char *current_argv, *has_equal;
339 size_t current_argv_len;
340 int i, ambiguous, match;
341
b1bf47f2 342 current_argv = CONST_CAST(char *, place);
9d0581fd
GS
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,
b1bf47f2 356 optind, CONST_CAST(char **, nargv));
9d0581fd
GS
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}