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