remove "program_invocation_short_name" dependency for Linux
[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 <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #endif
60
61 #ifndef HAVE_GETOPT_LONG
62 #define HAVE_GETOPT_LONG 1
63 /*
64 * structs are in their own namespace, so this should be OK
65 * the worst it can do is make compiler messages worse
66 */
67 #define option i_option_
68
69 struct i_option_ {
70 const char *name;
71 /*
72 * one of no_argument, required_argument, and optional_argument:
73 * whether option takes an argument
74 */
75 int has_arg;
76 /* if not NULL, set *flag to val when option found */
77 int *flag;
78 /* if flag not NULL, value to set *flag to; else return value */
79 int val;
80 };
81
82 int i_getopt_(int argc, char *argv[], const char *optstring);
83 int i_getsubopt_(char *restrict optionp[], const char *restrict tokens[],
84 char *restrict valuep[]);
85 int i_getopt_long_(int argc, char *argv[], const char *optstring,
86 const struct option *longopts, int *longindex);
87 int i_getopt_long_only_(int argc, char *argv[], const char *optstring,
88 const struct option *longopts, int *longindex);
89
90 #define no_argument 0
91 #define required_argument 1
92 #define optional_argument 2
93
94 #define getopt(c,v,o) i_getopt_(c,v,o)
95 #define getsubopt(o,t,v) i_getsubopt_(o,t,v)
96 #define getopt_long(c,v,o,l,i) i_getopt_long_(c,v,o,l,i)
97 #define getopt_long_only(c,v,o,l,i) i_getopt_long_only_(c,v,o,l,i)
98
99 #endif /* !HAVE_GETOPT_LONG */
100
101 #ifdef GETOPT_LONG_IMPLEMENTATION
102 #define I_PRINT_ERROR_ ((i_opterr_) && (*options != ':'))
103
104 #define I_FLAG_PERMUTE_ 0x01 /* permute non-options to the end of argv */
105 #define I_FLAG_ALLARGS_ 0x02 /* treat non-options as args to option "-1" */
106 #define I_FLAG_LONGONLY_ 0x04 /* operate as i_getopt_long_only */
107
108 /* return values */
109 #define I_BADCH_ (int)'?'
110 #define I_BADARG_ ((*options == ':') ? (int)':' : (int)'?')
111 #define I_INORDER_ (int)1
112
113 #define I_EMSG_ ""
114
115 static void i_warnx_(const char *, ...);
116 static int i_getopt_internal_(int, char **, const char *,
117 const struct option *, int *, int);
118 static int i_parse_long_options__(char * const *, const char *,
119 const struct option *, int *, int);
120 static int i_gcd_(int, int);
121 static void i_permute_args_(int, int, int, char * const *);
122
123
124 /* XXX: set i_optreeset_ to 1 rather than these two */
125 static int i_nonopt_start_ = -1; /* first non option argument (for permute) */
126 static int i_nonopt_end_ = -1; /* first option after non options (for permute) */
127 static int i_opterr_ = 1; /* if error message should be printed */
128 static int i_optind_ = 1; /* index into parent argv vector */
129 static int i_optopt_ = '?'; /* character checked for validity */
130 static int i_optreeset_; /* reset getopt */
131 static char *i_optarg_; /* argument associated with option */
132 static char *i_place_ = I_EMSG_; /* option letter processing */
133
134 /* Error messages */
135 static const char i_recargchar_[] = "option requires an argument -- %c";
136 static const char i_recargstring_[] = "option requires an argument -- %s";
137 static const char i_ambig_[] = "i_ambig_uous option -- %.*s";
138 static const char i_noarg_[] = "option doesn't take an argument -- %.*s";
139 static const char i_illoptchar_[] = "unknown option -- %c";
140 static const char i_illoptstring_[] = "unknown option -- %s";
141
142 /*
143 * Own warnx() for portability
144 */
145 void
146 i_warnx_(const char *fmt, ...)
147 {
148 va_list args;
149
150 /* Linux "program_invocation_short_name" is a GNU extension, so we don't use it */
151 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
152 #define I_GET_PROG_NAME_() getprogname()
153 #else
154 #define I_GET_PROG_NAME_() "warnx"
155 #endif
156
157 va_start(args, fmt);
158 fprintf(stderr, "%s: ", I_GET_PROG_NAME_());
159 vfprintf(stderr, fmt, args);
160 fputc('\n', stderr);
161 va_end(args);
162 }
163
164 /*
165 * Compute the greatest common divisor of a and b.
166 */
167 static int
168 i_gcd_(int a, int b)
169 {
170 int c;
171
172 c = a % b;
173 while (c != 0) {
174 a = b;
175 b = c;
176 c = a % b;
177 }
178
179 return (b);
180 }
181
182 /*
183 * Exchange the block from i_nonopt_start_ to i_nonopt_end_ with the block
184 * from i_nonopt_end_ to opt_end (keeping the same order of arguments
185 * in each block).
186 */
187 static void
188 i_permute_args_(int pai_nonopt_start_, int pai_nonopt_end_, int opt_end,
189 char * const *nargv)
190 {
191 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
192 char *swap;
193
194 /*
195 * compute lengths of blocks and number and size of cycles
196 */
197 nnonopts = pai_nonopt_end_ - pai_nonopt_start_;
198 nopts = opt_end - pai_nonopt_end_;
199 ncycle = i_gcd_(nnonopts, nopts);
200 cyclelen = (opt_end - pai_nonopt_start_) / ncycle;
201
202 for (i = 0; i < ncycle; i++) {
203 cstart = pai_nonopt_end_+i;
204 pos = cstart;
205 for (j = 0; j < cyclelen; j++) {
206 if (pos >= pai_nonopt_end_)
207 pos -= nnonopts;
208 else
209 pos += nopts;
210 swap = nargv[pos];
211 /* LINTED const cast */
212 ((char **) nargv)[pos] = nargv[cstart];
213 /* LINTED const cast */
214 ((char **)nargv)[cstart] = swap;
215 }
216 }
217 }
218
219 /*
220 * i_parse_long_options__ --
221 * Parse long options in argc/argv argument vector.
222 * Returns -1 if short_too is set and the option does not match long_options.
223 */
224 static int
225 i_parse_long_options__(char * const *nargv, const char *options,
226 const struct option *long_options, int *idx, int short_too)
227 {
228 char *current_argv, *has_equal;
229 size_t current_argv_len;
230 int i, match;
231
232 current_argv = i_place_;
233 match = -1;
234
235 i_optind_++;
236
237 if ((has_equal = strchr(current_argv, '=')) != NULL) {
238 /* argument found (--option=arg) */
239 current_argv_len = has_equal - current_argv;
240 has_equal++;
241 } else {
242 current_argv_len = strlen(current_argv);
243 }
244 for (i = 0; long_options[i].name; i++) {
245 /* find matching long option */
246 if (strncmp(current_argv, long_options[i].name,
247 current_argv_len))
248 continue;
249
250 if (strlen(long_options[i].name) == current_argv_len) {
251 /* exact match */
252 match = i;
253 break;
254 }
255 /*
256 * If this is a known short option, don't allow
257 * a partial match of a single character.
258 */
259 if (short_too && current_argv_len == 1)
260 continue;
261
262 if (match == -1) { /* partial match */
263 match = i;
264 } else {
265 /* i_ambig_uous abbreviation */
266 if (I_PRINT_ERROR_)
267 i_warnx_(i_ambig_, (int)current_argv_len,
268 current_argv);
269 i_optopt_ = 0;
270 return (I_BADCH_);
271 }
272 }
273 if (match != -1) { /* option found */
274 if (long_options[match].has_arg == no_argument
275 && has_equal) {
276 if (I_PRINT_ERROR_)
277 i_warnx_(i_noarg_, (int)current_argv_len,
278 current_argv);
279 /*
280 * XXX: GNU sets i_optopt_ to val regardless of flag
281 */
282 if (long_options[match].flag == NULL)
283 i_optopt_ = long_options[match].val;
284 else
285 i_optopt_ = 0;
286 return (I_BADARG_);
287 }
288 if (long_options[match].has_arg == required_argument ||
289 long_options[match].has_arg == optional_argument) {
290 if (has_equal)
291 i_optarg_ = has_equal;
292 else if (long_options[match].has_arg ==
293 required_argument) {
294 /*
295 * optional argument doesn't use next nargv
296 */
297 i_optarg_ = nargv[i_optind_++];
298 }
299 }
300 if ((long_options[match].has_arg == required_argument)
301 && (i_optarg_ == NULL)) {
302 /*
303 * Missing argument; leading ':' indicates no error
304 * should be generated.
305 */
306 if (I_PRINT_ERROR_)
307 i_warnx_(i_recargstring_,
308 current_argv);
309 /*
310 * XXX: GNU sets i_optopt_ to val regardless of flag
311 */
312 if (long_options[match].flag == NULL)
313 i_optopt_ = long_options[match].val;
314 else
315 i_optopt_ = 0;
316 --i_optind_;
317 return (I_BADARG_);
318 }
319 } else { /* unknown option */
320 if (short_too) {
321 --i_optind_;
322 return (-1);
323 }
324 if (I_PRINT_ERROR_)
325 i_warnx_(i_illoptstring_, current_argv);
326 i_optopt_ = 0;
327 return (I_BADCH_);
328 }
329 if (idx)
330 *idx = match;
331 if (long_options[match].flag) {
332 *long_options[match].flag = long_options[match].val;
333 return (0);
334 } else
335 return (long_options[match].val);
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 **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 /*
354 * XXX Some GNU programs (like cvs) set optind to 0 instead of
355 * XXX using optreeset. Work around this braindamage.
356 */
357 if (i_optind_ == 0)
358 i_optind_ = i_optreeset_ = 1;
359
360 /*
361 * Disable GNU extensions if POSIXLY_CORRECT is set or options
362 * string begins with a '+'.
363 */
364 if (posix_me_harder == -1 || i_optreeset_)
365 posix_me_harder = (getenv("POSIXLY_CORRECT") != NULL);
366 if (*options == '-')
367 flags |= I_FLAG_ALLARGS_;
368 else if (posix_me_harder || *options == '+')
369 flags &= ~I_FLAG_PERMUTE_;
370 if (*options == '+' || *options == '-')
371 options++;
372
373 i_optarg_ = NULL;
374 if (i_optreeset_)
375 i_nonopt_start_ = i_nonopt_end_ = -1;
376 start:
377 if (i_optreeset_ || !*i_place_) { /* update scanning pointer */
378 i_optreeset_ = 0;
379 if (i_optind_ >= nargc) { /* end of argument vector */
380 i_place_ = I_EMSG_;
381 if (i_nonopt_end_ != -1) {
382 /* do permutation, if we have to */
383 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
384 i_optind_, nargv);
385 i_optind_ -= i_nonopt_end_ - i_nonopt_start_;
386 } else if (i_nonopt_start_ != -1) {
387 /*
388 * If we skipped non-options, set i_optind_
389 * to the first of them.
390 */
391 i_optind_ = i_nonopt_start_;
392 }
393 i_nonopt_start_ = i_nonopt_end_ = -1;
394 return (-1);
395 }
396 if (*(i_place_ = nargv[i_optind_]) != '-' ||
397 (i_place_[1] == '\0' && strchr(options, '-') == NULL)) {
398 i_place_ = I_EMSG_; /* found non-option */
399 if (flags & I_FLAG_ALLARGS_) {
400 /*
401 * GNU extension:
402 * return non-option as argument to option 1
403 */
404 i_optarg_ = nargv[i_optind_++];
405 return (I_INORDER_);
406 }
407 if (!(flags & I_FLAG_PERMUTE_)) {
408 /*
409 * If no permutation wanted, stop parsing
410 * at first non-option.
411 */
412 return (-1);
413 }
414 /* do permutation */
415 if (i_nonopt_start_ == -1) {
416 i_nonopt_start_ = i_optind_;
417 } else if (i_nonopt_end_ != -1) {
418 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
419 i_optind_, nargv);
420 i_nonopt_start_ = i_optind_ -
421 (i_nonopt_end_ - i_nonopt_start_);
422 i_nonopt_end_ = -1;
423 }
424 i_optind_++;
425 /* process next argument */
426 goto start;
427 }
428 if (i_nonopt_start_ != -1 && i_nonopt_end_ == -1)
429 i_nonopt_end_ = i_optind_;
430
431 /*
432 * If we have "-" do nothing, if "--" we are done.
433 */
434 if (i_place_[1] != '\0' && *++i_place_ == '-' && i_place_[1] == '\0') {
435 i_optind_++;
436 i_place_ = I_EMSG_;
437 /*
438 * We found an option (--), so if we skipped
439 * non-options, we have to permute.
440 */
441 if (i_nonopt_end_ != -1) {
442 i_permute_args_(i_nonopt_start_, i_nonopt_end_,
443 i_optind_, nargv);
444 i_optind_ -= i_nonopt_end_ - i_nonopt_start_;
445 }
446 i_nonopt_start_ = i_nonopt_end_ = -1;
447 return (-1);
448 }
449 }
450
451 /*
452 * Check long options if:
453 * 1) we were passed some
454 * 2) the arg is not just "-"
455 * 3) either the arg starts with -- we are i_getopt_long_only()
456 */
457 if (long_options != NULL && i_place_ != nargv[i_optind_] &&
458 (*i_place_ == '-' || (flags & I_FLAG_LONGONLY_))) {
459 short_too = 0;
460 if (*i_place_ == '-')
461 i_place_++; /* --foo long option */
462 else if (*i_place_ != ':'
463 && strchr(options, *i_place_) != NULL)
464 short_too = 1; /* could be short option too */
465
466 optchar = i_parse_long_options__(nargv, options, long_options,
467 idx, short_too);
468 if (optchar != -1) {
469 i_place_ = I_EMSG_;
470 return (optchar);
471 }
472 }
473
474 if ((optchar = (int)*i_place_++) == (int)':' ||
475 (optchar == (int)'-' && *i_place_ != '\0') ||
476 (oli = strchr(options, optchar)) == NULL) {
477 /*
478 * If the user specified "-" and '-' isn't listed in
479 * options, return -1 (non-option) as per POSIX.
480 * Otherwise, it is an unknown option character (or ':').
481 */
482 if (optchar == (int)'-' && *i_place_ == '\0')
483 return (-1);
484 if (!*i_place_)
485 ++i_optind_;
486 if (I_PRINT_ERROR_)
487 i_warnx_(i_illoptchar_, optchar);
488 i_optopt_ = optchar;
489 return (I_BADCH_);
490 }
491 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
492 /* -W long-option */
493 if (*i_place_) { /* no space */
494 /* NOTHING */;
495 } else if (++i_optind_ >= nargc) { /* no arg */
496 i_place_ = I_EMSG_;
497 if (I_PRINT_ERROR_)
498 i_warnx_(i_recargchar_, optchar);
499 i_optopt_ = optchar;
500 return (I_BADARG_);
501 } else { /* white space */
502 i_place_ = nargv[i_optind_];
503 }
504 optchar = i_parse_long_options__(nargv, options, long_options,
505 idx, 0);
506 i_place_ = I_EMSG_;
507 return (optchar);
508 }
509 if (*++oli != ':') { /* doesn't take argument */
510 if (!*i_place_)
511 ++i_optind_;
512 } else { /* takes (optional) argument */
513 i_optarg_ = NULL;
514 if (*i_place_) /* no white space */
515 i_optarg_ = i_place_;
516 else if (oli[1] != ':') { /* arg not optional */
517 if (++i_optind_ >= nargc) { /* no arg */
518 i_place_ = I_EMSG_;
519 if (I_PRINT_ERROR_)
520 i_warnx_(i_recargchar_, optchar);
521 i_optopt_ = optchar;
522 return (I_BADARG_);
523 } else {
524 i_optarg_ = nargv[i_optind_];
525 }
526 }
527 i_place_ = I_EMSG_;
528 ++i_optind_;
529 }
530 /* dump back option letter */
531 return (optchar);
532 }
533
534 int
535 i_getopt_(int argc, char *argv[], const char *optstring)
536 {
537 #ifdef GETOPT_PERMUTE_ARGS
538 return (i_getopt_internal_(argc, argv, optstring, NULL, NULL,
539 I_FLAG_PERMUTE_));
540 #else
541 return (i_getopt_internal_(argc, argv, optstring, NULL, NULL, 0));
542 #endif
543 }
544
545 int
546 i_getopt_long_(int argc, char *argv[], const char *optstring,
547 const struct option *longopts, int *longindex)
548 {
549 #ifdef GETOPT_LONG_PERMUTE_ARGS
550 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
551 I_FLAG_PERMUTE_));
552 #else
553 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex, 0));
554 #endif
555
556 }
557
558 int
559 i_getopt_long_only_(int argc, char *argv[], const char *optstring,
560 const struct option *longopts, int *longindex)
561 {
562 #ifdef GETOPT_LONG_ONLY_PERMUTE_ARGS
563 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
564 I_FLAG_PERMUTE_ | I_FLAG_LONGONLY_));
565 #else
566 return (i_getopt_internal_(argc, argv, optstring, longopts, longindex,
567 I_FLAG_LONGONLY_));
568 #endif
569 }
570 #endif /* GETOPT_LONG_IMPLEMENTATION */
571 #endif /* !GETOPT_LONG_H_ */