xref: /dpdk/lib/eal/windows/getopt.c (revision a06fb0fae6ab6a79fa3e8288dbb0c5166be843c2)
1 /* SPDX-License-Identifier: ISC AND BSD-2-Clause
2  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Sponsored in part by the Defense Advanced Research Projects
5  * Agency (DARPA) and Air Force Research Laboratory, Air Force
6  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
7  */
8 /*
9  * Copyright (c) 2000 The NetBSD Foundation, Inc.
10  * All rights reserved.
11  *
12  * This code is derived from software contributed to The NetBSD Foundation
13  * by Dieter Baron and Thomas Klausner.
14  */
15 
16 #include <getopt.h>
17 
18 #ifdef NEED_USUAL_GETOPT
19 
20 #include <string.h>
21 #include <stdlib.h>
22 
23 char    *optarg;		/* argument associated with option */
24 int	opterr = 1;		/* if error message should be printed */
25 int	optind = 1;		/* index into parent argv vector */
26 int	optopt = '?';		/* character checked for validity */
27 
28 static void pass(const char *a) {(void) a; }
29 #define warnx(a, ...) pass(a)
30 
31 #define PRINT_ERROR	((opterr) && (*options != ':'))
32 
33 #define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
34 #define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
35 #define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */
36 
37 /* return values */
38 #define	BADCH		((int)'?')
39 #define	BADARG		((*options == ':') ? (int)':' : (int)'?')
40 #define	INORDER		1
41 
42 static char EMSG[] = "";
43 
44 static char *place = EMSG; /* option letter processing */
45 
46 /* XXX: set optreset to 1 rather than these two */
47 static int nonopt_start = -1; /* first non option argument (for permute) */
48 static int nonopt_end = -1;   /* first option after non options (for permute) */
49 
50 /* Error messages */
51 static const char recargchar[] = "option requires an argument -- %c";
52 static const char recargstring[] = "option requires an argument -- %s";
53 static const char ambig[] = "ambiguous option -- %.*s";
54 static const char noarg[] = "option doesn't take an argument -- %.*s";
55 static const char illoptchar[] = "unknown option -- %c";
56 static const char illoptstring[] = "unknown option -- %s";
57 
58 /*
59  * Compute the greatest common divisor of a and b.
60  */
61 static int
62 gcd(int a, int b)
63 {
64 	int c;
65 
66 	c = a % b;
67 	while (c != 0) {
68 		a = b;
69 		b = c;
70 		c = a % b;
71 	}
72 
73 	return (b);
74 }
75 
76 /*
77  * Exchange the block from nonopt_start to nonopt_end with the block
78  * from nonopt_end to opt_end (keeping the same order of arguments
79  * in each block).
80  */
81 static void
82 permute_args(int panonopt_start, int panonopt_end, int opt_end,
83 	char * const *nargv)
84 {
85 	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
86 	char *swap;
87 
88 	/*
89 	 * compute lengths of blocks and number and size of cycles
90 	 */
91 	nnonopts = panonopt_end - panonopt_start;
92 	nopts = opt_end - panonopt_end;
93 	ncycle = gcd(nnonopts, nopts);
94 	cyclelen = (opt_end - panonopt_start) / ncycle;
95 
96 	for (i = 0; i < ncycle; i++) {
97 		cstart = panonopt_end+i;
98 		pos = cstart;
99 		for (j = 0; j < cyclelen; j++) {
100 			if (pos >= panonopt_end)
101 				pos -= nnonopts;
102 			else
103 				pos += nopts;
104 
105 			swap = nargv[pos];
106 			/* LINTED const cast */
107 			((char **)(uintptr_t)nargv)[pos] = nargv[cstart];
108 			/* LINTED const cast */
109 			((char **)(uintptr_t)nargv)[cstart] = swap;
110 		}
111 	}
112 }
113 
114 /*
115  * parse_long_options --
116  *	Parse long options in argc/argv argument vector.
117  * Returns -1 if short_too is set and the option does not match long_options.
118  */
119 static int
120 parse_long_options(char * const *nargv, const char *options,
121 	const struct option *long_options, int *idx, int short_too)
122 {
123 	const char *current_argv;
124 	char *has_equal;
125 	size_t current_argv_len;
126 	int i, match;
127 
128 	current_argv = place;
129 	match = -1;
130 
131 	optind++;
132 
133 	has_equal = strchr(current_argv, '=');
134 	if (has_equal != NULL) {
135 		/* argument found (--option=arg) */
136 		current_argv_len = has_equal - current_argv;
137 		has_equal++;
138 	} else
139 		current_argv_len = strlen(current_argv);
140 
141 	for (i = 0; long_options[i].name; i++) {
142 		/* find matching long option */
143 		if (strncmp(current_argv, long_options[i].name,
144 		    current_argv_len))
145 			continue;
146 
147 		if (strlen(long_options[i].name) == current_argv_len) {
148 			/* exact match */
149 			match = i;
150 			break;
151 		}
152 		/*
153 		 * If this is a known short option, don't allow
154 		 * a partial match of a single character.
155 		 */
156 		if (short_too && current_argv_len == 1)
157 			continue;
158 
159 		if (match == -1)	/* partial match */
160 			match = i;
161 		else {
162 			/* ambiguous abbreviation */
163 			if (PRINT_ERROR)
164 				warnx(ambig, (int)current_argv_len,
165 				     current_argv);
166 			optopt = 0;
167 			return BADCH;
168 		}
169 	}
170 	if (match != -1) {		/* option found */
171 		if (long_options[match].has_arg == no_argument
172 		    && has_equal) {
173 			if (PRINT_ERROR)
174 				warnx(noarg, (int)current_argv_len,
175 				     current_argv);
176 			/*
177 			 * XXX: GNU sets optopt to val regardless of flag
178 			 */
179 			if (long_options[match].flag == NULL)
180 				optopt = long_options[match].val;
181 			else
182 				optopt = 0;
183 			return BADARG;
184 		}
185 		if (long_options[match].has_arg == required_argument ||
186 		    long_options[match].has_arg == optional_argument) {
187 			if (has_equal)
188 				optarg = has_equal;
189 			else if (long_options[match].has_arg ==
190 			    required_argument) {
191 				/*
192 				 * optional argument doesn't use next nargv
193 				 */
194 				optarg = nargv[optind++];
195 			}
196 		}
197 		if ((long_options[match].has_arg == required_argument)
198 		    && (optarg == NULL)) {
199 			/*
200 			 * Missing argument; leading ':' indicates no error
201 			 * should be generated.
202 			 */
203 			if (PRINT_ERROR)
204 				warnx(recargstring,
205 				    current_argv);
206 			/*
207 			 * XXX: GNU sets optopt to val regardless of flag
208 			 */
209 			if (long_options[match].flag == NULL)
210 				optopt = long_options[match].val;
211 			else
212 				optopt = 0;
213 			--optind;
214 			return BADARG;
215 		}
216 	} else {			/* unknown option */
217 		if (short_too) {
218 			--optind;
219 			return (-1);
220 		}
221 		if (PRINT_ERROR)
222 			warnx(illoptstring, current_argv);
223 		optopt = 0;
224 		return BADCH;
225 	}
226 	if (idx)
227 		*idx = match;
228 	if (long_options[match].flag) {
229 		*long_options[match].flag = long_options[match].val;
230 		return 0;
231 	} else
232 		return (long_options[match].val);
233 }
234 
235 /*
236  * getopt_internal --
237  *	Parse argc/argv argument vector.  Called by user level routines.
238  */
239 static int
240 getopt_internal(int nargc, char *const nargv[], const char *options,
241 	const struct option *long_options, int *idx, int flags)
242 {
243 	char *oli;				/* option letter list index */
244 	int optchar, short_too;
245 	static int posixly_correct = -1;
246 	size_t len;
247 	int optreset = 0;
248 
249 	if (options == NULL)
250 		return (-1);
251 
252 	/*
253 	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
254 	 * string begins with a '+'.
255 	 */
256 	if (posixly_correct == -1) {
257 		errno_t err = _wgetenv_s(&len, NULL, 0, L"POSIXLY_CORRECT");
258 		posixly_correct = (err == 0) && (len > 0);
259 	}
260 	if (!posixly_correct || *options == '+')
261 		flags &= ~FLAG_PERMUTE;
262 	else if (*options == '-')
263 		flags |= FLAG_ALLARGS;
264 	if (*options == '+' || *options == '-')
265 		options++;
266 	/*
267 	 * reset if requested
268 	 */
269 	if (optind == 0)
270 		optind = optreset = 1;
271 
272 	optarg = NULL;
273 	if (optreset)
274 		nonopt_start = nonopt_end = -1;
275 start:
276 	if (optreset || !*place) {		/* update scanning pointer */
277 		optreset = 0;
278 		if (optind >= nargc) {          /* end of argument vector */
279 			place = EMSG;
280 			if (nonopt_end != -1) {
281 				/* do permutation, if we have to */
282 				permute_args(nonopt_start, nonopt_end,
283 				    optind, nargv);
284 				optind -= nonopt_end - nonopt_start;
285 			} else if (nonopt_start != -1) {
286 				/*
287 				 * If we skipped non-options, set optind
288 				 * to the first of them.
289 				 */
290 				optind = nonopt_start;
291 			}
292 			nonopt_start = nonopt_end = -1;
293 			return (-1);
294 		}
295 		place = nargv[optind];
296 		if (*place != '-' ||
297 		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
298 			place = EMSG;		/* found non-option */
299 			if (flags & FLAG_ALLARGS) {
300 				/*
301 				 * GNU extension:
302 				 * return non-option as argument to option 1
303 				 */
304 				optarg = nargv[optind++];
305 				return INORDER;
306 			}
307 			if (!(flags & FLAG_PERMUTE)) {
308 				/*
309 				 * If no permutation wanted, stop parsing
310 				 * at first non-option.
311 				 */
312 				return (-1);
313 			}
314 			/* do permutation */
315 			if (nonopt_start == -1)
316 				nonopt_start = optind;
317 			else if (nonopt_end != -1) {
318 				permute_args(nonopt_start, nonopt_end,
319 				    optind, nargv);
320 				nonopt_start = optind -
321 				    (nonopt_end - nonopt_start);
322 				nonopt_end = -1;
323 			}
324 			optind++;
325 			/* process next argument */
326 			goto start;
327 		}
328 		if (nonopt_start != -1 && nonopt_end == -1)
329 			nonopt_end = optind;
330 
331 		/*
332 		 * If we have "-" do nothing, if "--" we are done.
333 		 */
334 		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
335 			optind++;
336 			place = EMSG;
337 			/*
338 			 * We found an option (--), so if we skipped
339 			 * non-options, we have to permute.
340 			 */
341 			if (nonopt_end != -1) {
342 				permute_args(nonopt_start, nonopt_end,
343 				    optind, nargv);
344 				optind -= nonopt_end - nonopt_start;
345 			}
346 			nonopt_start = nonopt_end = -1;
347 			return (-1);
348 		}
349 	}
350 
351 	/*
352 	 * Check long options if:
353 	 *  1) we were passed some
354 	 *  2) the arg is not just "-"
355 	 *  3) either the arg starts with -- we are getopt_long_only()
356 	 */
357 	if (long_options != NULL && place != nargv[optind] &&
358 	    (*place == '-' || (flags & FLAG_LONGONLY))) {
359 		short_too = 0;
360 		if (*place == '-')
361 			place++;		/* --foo long option */
362 		else if (*place != ':' && strchr(options, *place) != NULL)
363 			short_too = 1;		/* could be short option too */
364 
365 		optchar = parse_long_options(nargv, options, long_options,
366 		    idx, short_too);
367 		if (optchar != -1) {
368 			place = EMSG;
369 			return optchar;
370 		}
371 	}
372 
373 	optchar = (int)*place++;
374 	oli = strchr(options, optchar);
375 	if (optchar == (int)':' ||
376 	    (optchar == (int)'-' && *place != '\0') ||
377 	    oli == NULL) {
378 		/*
379 		 * If the user specified "-" and  '-' isn't listed in
380 		 * options, return -1 (non-option) as per POSIX.
381 		 * Otherwise, it is an unknown option character (or ':').
382 		 */
383 		if (optchar == (int)'-' && *place == '\0')
384 			return (-1);
385 		if (!*place)
386 			++optind;
387 		if (PRINT_ERROR)
388 			warnx(illoptchar, optchar);
389 		optopt = optchar;
390 		return BADCH;
391 	}
392 	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
393 		/* -W long-option */
394 		if (*place)
395 			;
396 		else if (++optind >= nargc) {	/* no arg */
397 			place = EMSG;
398 			if (PRINT_ERROR)
399 				warnx(recargchar, optchar);
400 			optopt = optchar;
401 			return BADARG;
402 		}				/* white space */
403 		place = nargv[optind];
404 		optchar = parse_long_options(nargv, options, long_options,
405 		    idx, 0);
406 		place = EMSG;
407 		return optchar;
408 	}
409 	if (*++oli != ':') {			/* doesn't take argument */
410 		if (!*place)
411 			++optind;
412 	} else {				/* takes (optional) argument */
413 		optarg = NULL;
414 		if (*place)			/* no white space */
415 			optarg = place;
416 		else if (oli[1] != ':') {	/* arg not optional */
417 			if (++optind >= nargc) {	/* no arg */
418 				place = EMSG;
419 				if (PRINT_ERROR)
420 					warnx(recargchar, optchar);
421 				optopt = optchar;
422 				return BADARG;
423 			}
424 			optarg = nargv[optind];
425 		}
426 		place = EMSG;
427 		++optind;
428 	}
429 	/* dump back option letter */
430 	return optchar;
431 }
432 
433 /*
434  * getopt --
435  *	Parse argc/argv argument vector.
436  */
437 int
438 getopt(int nargc, char *const nargv[], const char *options)
439 {
440 	return getopt_internal(nargc, nargv, options, NULL, NULL,
441 			       FLAG_PERMUTE);
442 }
443 
444 /*
445  * getopt_long --
446  *	Parse argc/argv argument vector.
447  */
448 int
449 getopt_long(int nargc, char *const nargv[], const char *options,
450 	const struct option *long_options, int *idx)
451 {
452 
453 	return (getopt_internal(nargc, nargv, options, long_options, idx,
454 	    FLAG_PERMUTE));
455 }
456 
457 /*
458  * getopt_long_only --
459  *	Parse argc/argv argument vector.
460  */
461 int
462 getopt_long_only(int nargc, char *const nargv[], const char *options,
463 	const struct option *long_options, int *idx)
464 {
465 
466 	return (getopt_internal(nargc, nargv, options, long_options, idx,
467 	    FLAG_PERMUTE|FLAG_LONGONLY));
468 }
469 
470 #endif /* NEED_USUAL_GETOPT */
471