strtonum: fix `TOOLARGE` macro name
[single-header-libcext.git] / getopt_long.h
1 /*
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Dieter Baron and Thomas Klausner.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 * Sponsored in part by the Defense Advanced Research Projects
36 * Agency (DARPA) and Air Force Research Laboratory, Air Force
37 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
38 */
39
40 /* https://git.datadissipation.net */
41 #ifndef GETOPT_LONG_H_
42 #define GETOPT_LONG_H_ 1
43
44 #ifdef GETOPT_LONG_INCLUDE_LIBC
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #endif /* GETOPT_LONG_INCLUDE_LIBC */
50
51 #ifndef HAVE_GETOPT_LONG
52 #define HAVE_GETOPT_LONG 1
53
54 #define no_argument 0
55 #define required_argument 1
56 #define optional_argument 2
57
58 /*
59 * structs are in their own namespace, so this should be OK
60 * the worst it can do is make compiler messages worse
61 */
62 #define option i_option_
63
64 #define optarg i_optarg_
65 #define suboptarg i_suboptarg_
66 #define optind i_optind_
67 #define opterr i_opterr_
68 #define optopt i_optopt_
69 #define optreset i_optreset_
70
71 #define getopt i_getopt_
72 #define getsubopt i_getsubopt_
73 #define getopt_long i_getopt_long_
74 #define getopt_long_only i_getopt_long_only_
75
76 #ifndef GETOPT_LONG_IMPLEMENTATION
77 extern char *i_optarg_;
78 extern char *i_suboptarg_;
79 extern int i_optind_, i_opterr_, i_optopt_, i_optreset_;
80 #endif /* !GETOPT_LONG_IMPLEMENTATION */
81
82 struct i_option_ {
83 const char *name;
84 /*
85 * one of no_argument, required_argument, and optional_argument:
86 * whether option takes an argument
87 */
88 int has_arg;
89 /* if not NULL, set *flag to val when option found */
90 int *flag;
91 /* if flag not NULL, value to set *flag to; else return value */
92 int val;
93 };
94
95 int i_getopt_(int argc, char * const *argv, const char *optstring);
96 int i_getsubopt_(char **optionp, char * const *tokens, char **valuep);
97 int i_getopt_long_(int argc, char * const *argv, const char *optstring,
98 const struct option *longopts, int *longindex);
99 int i_getopt_long_only_(int argc, char * const *argv, const char *optstring,
100 const struct option *longopts, int *longindex);
101
102 #endif /* !HAVE_GETOPT_LONG */
103
104 #ifdef GETOPT_LONG_IMPLEMENTATION
105 #define I_PRINT_ERROR_ ((i_opterr_) && (*options != ':'))
106
107 #define I_FLAG_PERMUTE_ 0x01 /* permute non-options to the end of argv */
108 #define I_FLAG_ALLARGS_ 0x02 /* treat non-options as args to option "-1" */
109 #define I_FLAG_LONGONLY_ 0x04 /* operate as getopt_long_only */
110
111 /* return values */
112 #define I_BADCH_ (int) '?'
113 #define I_BADARG_ ((*options == ':') ? (int) ':' : (int) '?')
114 #define I_INORDER_ (int) 1
115
116 #define I_EMSG_ ""
117
118 int i_opterr_ = 1; /* if error message should be printed */
119 int i_optind_ = 1; /* index into parent argv vector */
120 int i_optopt_ = '?'; /* character checked for validity */
121 int i_optreset_; /* reset getopt */
122 char *i_optarg_; /* argument associated with option */
123 char *i_suboptarg_; /* argument associated with suboption */
124
125 /* XXX: set optreset to 1 rather than these two */
126 static int i_nonopt_start_ = -1; /* first non option argument (for permute) */
127 static int i_nonopt_end_ = -1; /* first option after non options (for permute) */
128 static char *i_place_ = I_EMSG_; /* option letter processing */
129 static char *i_progname_; /* program name == argv[0] */
130
131 /* Error messages */
132 static const char i_recargchar_[] = "option requires an argument -- %c";
133 static const char i_recargstring_[] = "option requires an argument -- %s";
134 static const char i_ambig_[] = "i_ambig_uous option -- %.*s";
135 static const char i_noarg_[] = "option doesn't take an argument -- %.*s";
136 static const char i_illoptchar_[] = "unknown option -- %c";
137 static const char i_illoptstring_[] = "unknown option -- %s";
138
139 static void i_warnx_(const char *, ...);
140 static int i_getopt_internal_(int, char * const *, const char *,
141 const struct option *, int *, int);
142 static int i_parse_long_options__(char * const *, const char *,
143 const struct option *, int *, int);
144 static int i_gcd_(int, int);
145 static void i_permute_args_(int, int, int, char * const *);
146
147 /*
148 * Own warnx() for portability
149 */
150 void
151 i_warnx_(const char *fmt, ...)
152 {
153 va_list args;
154
155 va_start(args, fmt);
156 fprintf(stderr, "%s: ", i_progname_);
157 vfprintf(stderr, fmt, args);
158 fputc('\n', stderr);
159 va_end(args);
160 }
161
162 /*
163 * Compute the greatest common divisor of a and b.
164 */
165 static int
166 i_gcd_(int a, int b)
167 {
168 int c;
169
170 c = a % b;
171 while (c != 0) {
172 a = b;
173 b = c;
174 c = a % b;
175 }
176
177 return (b);
178 }
179
180 /*
181 * Exchange the block from nonopt_start to nonopt_end with the block
182 * from nonopt_end to opt_end (keeping the same order of arguments
183 * in each block).
184 */
185 static void
186 i_permute_args_(int panonopt_start, int panonopt_end, int opt_end,
187 char * const *nargv)
188 {
189 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
190 char *swap;
191
192 /*
193 * compute lengths of blocks and number and size of cycles
194 */
195 nnonopts = panonopt_end - panonopt_start;
196 nopts = opt_end - panonopt_end;
197 ncycle = i_gcd_(nnonopts, nopts);
198 cyclelen = (opt_end - panonopt_start) / ncycle;
199
200 for (i = 0; i < ncycle; i++) {
201 cstart = panonopt_end+i;
202 pos = cstart;
203 for (j = 0; j < cyclelen; j++) {
204 if (pos >= panonopt_end)
205 pos -= nnonopts;
206 else
207 pos += nopts;
208 swap = nargv[pos];
209 /* LINTED const cast */
210 ((char **) nargv)[pos] = nargv[cstart];
211 /* LINTED const cast */
212 ((char **) nargv)[cstart] = swap;
213 }
214 }
215 }
216
217 /*
218 * i_parse_long_options__ --
219 * Parse long options in argc/argv argument vector.
220 * Returns -1 if short_too is set and the option does not match long_options.
221 */
222 static int
223 i_parse_long_options__(char * const *nargv, const char *options,
224 const struct option *long_options, int *idx, int
225 short_too)
226 {
227 char *current_argv, *has_equal;
228 size_t current_argv_len;
229 int i, match;
230
231 current_argv = i_place_;
232 match = -1;
233
234 i_optind_++;
235
236 if ((has_equal = strchr(current_argv, '=')) != NULL) {
237 /* argument found (--option=arg) */
238 current_argv_len = has_equal - current_argv;
239 has_equal++;
240 } else {
241 current_argv_len = strlen(current_argv);
242 }
243 for (i = 0; long_options[i].name; i++) {
244 /* find matching long option */
245 if (strncmp(current_argv, long_options[i].name,
246 current_argv_len))
247 continue;
248
249 if (strlen(long_options[i].name) == current_argv_len) {
250 /* exact match */
251 match = i;
252 break;
253 }
254 /*
255 * If this is a known short option, don't allow
256 * a partial match of a single character.
257 */
258 if (short_too && current_argv_len == 1)
259 continue;
260
261 if (match == -1) { /* partial match */
262 match = i;
263 } else {
264 /* ambiguous abbreviation */
265 if (I_PRINT_ERROR_)
266 i_warnx_(i_ambig_, (int) current_argv_len,
267 current_argv);
268 i_optopt_ = 0;
269 return (I_BADCH_);
270 }
271 }
272 if (match != -1) { /* option found */
273 if (long_options[match].has_arg == no_argument
274 && has_equal) {
275 if (I_PRINT_ERROR_)
276 i_warnx_(i_noarg_, (int) current_argv_len,
277 current_argv);
278 /*
279 * XXX: GNU sets optopt to val regardless of flag
280 */
281 if (long_options[match].flag == NULL)
282 i_optopt_ = long_options[match].val;
283 else
284 i_optopt_ = 0;
285 return (I_BADARG_);
286 }
287 if (long_options[match].has_arg == required_argument ||
288 long_options[match].has_arg == optional_argument) {
289 if (has_equal){
290 i_optarg_ = has_equal;
291 } else if (long_options[match].has_arg ==
292 required_argument) {
293 /*
294 * optional argument doesn't use next nargv
295 */
296 i_optarg_ = nargv[i_optind_++];
297 }
298 }
299 if ((long_options[match].has_arg == required_argument)
300 && (i_optarg_ == NULL)) {
301 /*
302 * Missing argument; leading ':' indicates no error
303 * should be generated.
304 */
305 if (I_PRINT_ERROR_)
306 i_warnx_(i_recargstring_,
307 current_argv);
308 /*
309 * XXX: GNU sets optopt to val regardless of flag
310 */
311 if (long_options[match].flag == NULL)
312 i_optopt_ = long_options[match].val;
313 else
314 i_optopt_ = 0;
315 --i_optind_;
316 return (I_BADARG_);
317 }
318 } else { /* unknown option */
319 if (short_too) {
320 --i_optind_;
321 return (-1);
322 }
323 if (I_PRINT_ERROR_)
324 i_warnx_(i_illoptstring_, current_argv);
325 i_optopt_ = 0;
326 return (I_BADCH_);
327 }
328 if (idx)
329 *idx = match;
330 if (long_options[match].flag) {
331 *long_options[match].flag = long_options[match].val;
332 return (0);
333 } else {
334 return (long_options[match].val);
335 }
336 }
337
338 /*
339 * i_getopt_internal_ --
340 * Parse argc/argv argument vector. Called by user level routines.
341 */
342 static int
343 i_getopt_internal_(int nargc, char *const *nargv, const char *options,
344 const struct option *long_options, int *idx, int flags)
345 {
346 char *oli; /* option letter list index */
347 int optchar, short_too;
348 static int posix_me_harder = -1;
349
350 if (options == NULL)
351 return (-1);
352
353 i_progname_ = nargv[0];
354
355 /*
356 * XXX Some GNU programs (like cvs) set optind to 0 instead of
357 * XXX using optreset. Work around this braindamage.
358 */
359 if (i_optind_ == 0)
360 i_optind_ = i_optreset_ = 1;
361
362 /*
363 * Disable GNU extensions if POSIXLY_CORRECT is set or options
364 * string begins with a '+'.
365 */
366 if (posix_me_harder == -1 || i_optreset_)
367 posix_me_harder = (getenv("POSIXLY_CORRECT") != NULL);
368 if (*options == '-')
369 flags |= I_FLAG_ALLARGS_;
370 else if (posix_me_harder || *options == '+')
371 flags &= ~I_FLAG_PERMUTE_;
372 if (*options == '+' || *options == '-')
373 options++;
374
375 i_optarg_ = NULL;
376 if (i_optreset_)
377 i_nonopt_start_ = i_nonopt_end_ = -1;
378 start:
379 if (i_optreset_ || !*i_place_) { /* update scanning pointer */
380 i_optreset_ = 0;
381 if (i_optind_ >= nargc) { /* end of argument vector */
382 i_place_ = I_EMSG_;
383 if (i_nonopt_end_ != -1) {
384 /* do permutation, if we have to */
385 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
386 i_optind_, nargv);
387 i_optind_ -= i_nonopt_end_ - i_nonopt_start_;
388 } else if (i_nonopt_start_ != -1) {
389 /*
390 * If we skipped non-options, set optind
391 * to the first of them.
392 */
393 i_optind_ = i_nonopt_start_;
394 }
395 i_nonopt_start_ = i_nonopt_end_ = -1;
396 return (-1);
397 }
398 if (*(i_place_ = nargv[i_optind_]) != '-' ||
399 (i_place_[1] == '\0' && strchr(options, '-') == NULL)) {
400 i_place_ = I_EMSG_; /* found non-option */
401 if (flags & I_FLAG_ALLARGS_) {
402 /*
403 * GNU extension:
404 * return non-option as argument to option 1
405 */
406 i_optarg_ = nargv[i_optind_++];
407 return (I_INORDER_);
408 }
409 if (!(flags & I_FLAG_PERMUTE_)) {
410 /*
411 * If no permutation wanted, stop parsing
412 * at first non-option.
413 */
414 return (-1);
415 }
416 /* do permutation */
417 if (i_nonopt_start_ == -1) {
418 i_nonopt_start_ = i_optind_;
419 } else if (i_nonopt_end_ != -1) {
420 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
421 i_optind_, nargv);
422 i_nonopt_start_ = i_optind_ -
423 (i_nonopt_end_ -
424 i_nonopt_start_);
425 i_nonopt_end_ = -1;
426 }
427 i_optind_++;
428 /* process next argument */
429 goto start;
430 }
431 if (i_nonopt_start_ != -1 && i_nonopt_end_ == -1)
432 i_nonopt_end_ = i_optind_;
433
434 /*
435 * If we have "-" do nothing, if "--" we are done.
436 */
437 if (i_place_[1] != '\0' && *++i_place_ == '-' && i_place_[1] ==
438 '\0') {
439 i_optind_++;
440 i_place_ = I_EMSG_;
441 /*
442 * We found an option (--), so if we skipped
443 * non-options, we have to permute.
444 */
445 if (i_nonopt_end_ != -1) {
446 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
447 i_optind_, nargv);
448 i_optind_ -= i_nonopt_end_ - i_nonopt_start_;
449 }
450 i_nonopt_start_ = i_nonopt_end_ = -1;
451 return (-1);
452 }
453 }
454
455 /*
456 * Check long options if:
457 * 1) we were passed some
458 * 2) the arg is not just "-"
459 * 3) either the arg starts with -- we are i_getopt_long_only()
460 */
461 if (long_options != NULL && i_place_ != nargv[i_optind_] &&
462 (*i_place_ == '-' || (flags & I_FLAG_LONGONLY_))) {
463 short_too = 0;
464 if (*i_place_ == '-')
465 i_place_++; /* --foo long option */
466 else if (*i_place_ != ':'
467 && strchr(options, *i_place_) != NULL)
468 short_too = 1; /* could be short option too */
469
470 optchar = i_parse_long_options__(nargv, options, long_options,
471 idx, short_too);
472 if (optchar != -1) {
473 i_place_ = I_EMSG_;
474 return (optchar);
475 }
476 }
477
478 if ((optchar = (int) *i_place_++) == (int) ':' ||
479 (optchar == (int) '-' && *i_place_ != '\0') ||
480 (oli = strchr(options, optchar)) == NULL) {
481 /*
482 * If the user specified "-" and '-' isn't listed in
483 * options, return -1 (non-option) as per POSIX.
484 * Otherwise, it is an unknown option character (or ':').
485 */
486 if (optchar == (int) '-' && *i_place_ == '\0')
487 return (-1);
488 if (!*i_place_)
489 ++i_optind_;
490 if (I_PRINT_ERROR_)
491 i_warnx_(i_illoptchar_, optchar);
492 i_optopt_ = optchar;
493 return (I_BADCH_);
494 }
495 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
496 /* -W long-option */
497 if (*i_place_) { /* no space */
498 /* NOTHING */
499 ;
500 } else if (++i_optind_ >= nargc) { /* no arg */
501 i_place_ = I_EMSG_;
502 if (I_PRINT_ERROR_)
503 i_warnx_(i_recargchar_, optchar);
504 i_optopt_ = optchar;
505 return (I_BADARG_);
506 } else { /* white space */
507 i_place_ = nargv[i_optind_];
508 }
509 optchar = i_parse_long_options__(nargv, options, long_options,
510 idx, 0);
511 i_place_ = I_EMSG_;
512 return (optchar);
513 }
514 if (*++oli != ':') { /* doesn't take argument */
515 if (!*i_place_)
516 ++i_optind_;
517 } else { /* takes (optional) argument */
518 i_optarg_ = NULL;
519 if (*i_place_){ /* no white space */
520 i_optarg_ = i_place_;
521 } else if (oli[1] != ':'){ /* arg not optional */
522 if (++i_optind_ >= nargc) { /* no arg */
523 i_place_ = I_EMSG_;
524 if (I_PRINT_ERROR_)
525 i_warnx_(i_recargchar_, optchar);
526 i_optopt_ = optchar;
527 return (I_BADARG_);
528 } else {
529 i_optarg_ = nargv[i_optind_];
530 }
531 }
532 i_place_ = I_EMSG_;
533 ++i_optind_;
534 }
535 /* dump back option letter */
536 return (optchar);
537 }
538
539 int
540 i_getsubopt_(char **optionp, char * const *tokens, char **valuep)
541 {
542 int cnt;
543 char *p;
544
545 i_suboptarg_ = *valuep = NULL;
546
547 if (!optionp || !*optionp)
548 return (-1);
549
550 /* skip leading white-space, commas */
551 for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p) ;
552
553 if (!*p) {
554 *optionp = p;
555 return (-1);
556 }
557
558 /* save the start of the token, and skip the rest of the token. */
559 for (i_suboptarg_ = p;
560 *++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';) ;
561
562 if (*p) {
563 /*
564 * If there's an equals sign, set the value pointer, and
565 * skip over the value part of the token. Terminate the
566 * token.
567 */
568 if (*p == '=') {
569 *p = '\0';
570 for (*valuep = ++p;
571 *p && *p != ',' && *p != ' ' && *p != '\t'; ++p) ;
572 if (*p)
573 *p++ = '\0';
574 } else {
575 *p++ = '\0';
576 }
577 /* Skip any whitespace or commas after this token. */
578 for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p) ;
579 }
580
581 /* set optionp for next round. */
582 *optionp = p;
583
584 for (cnt = 0; *tokens; ++tokens, ++cnt)
585 if (!strcmp(i_suboptarg_, *tokens))
586 return (cnt);
587 return (-1);
588 }
589
590 int
591 i_getopt_(int argc, char * const *argv, const char *optstring)
592 {
593 #ifdef GETOPT_PERMUTE_ARGS
594 return (i_getopt_internal_(argc, argv, optstring, NULL, NULL,
595 I_FLAG_PERMUTE_));
596 #else
597 return (i_getopt_internal_(argc, argv, optstring, NULL, NULL, 0));
598 #endif
599 }
600
601 int
602 i_getopt_long_(int argc, char * const *argv, const char *optstring,
603 const struct option *longopts, int *longindex)
604 {
605 #ifdef GETOPT_LONG_PERMUTE_ARGS
606 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
607 I_FLAG_PERMUTE_));
608 #else
609 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex, 0
610 ));
611 #endif
612 }
613
614 int
615 i_getopt_long_only_(int argc, char * const *argv, const char *optstring,
616 const struct option *longopts, int *longindex)
617 {
618 #ifdef GETOPT_LONG_ONLY_PERMUTE_ARGS
619 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
620 I_FLAG_PERMUTE_ | I_FLAG_LONGONLY_));
621 #else
622 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
623 I_FLAG_LONGONLY_));
624 #endif
625 }
626 #endif /* GETOPT_LONG_IMPLEMENTATION */
627 #endif /* !GETOPT_LONG_H_ */