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