1 /* $OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc 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 53 #include <errno.h> 54 #include "getopt_long.h" 55 #include <stdlib.h> 56 #include <stdio.h> 57 #include <string.h> 58 #include <stdarg.h> 59 60 #include "diag-control.h" 61 62 #define GNU_COMPATIBLE /* Be more compatible, configure's use us! */ 63 64 #define PRINT_ERROR ((opterr) && (*options != ':')) 65 66 #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ 67 #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ 68 #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ 69 70 /* return values */ 71 #define BADCH (int)'?' 72 #define BADARG ((*options == ':') ? (int)':' : (int)'?') 73 #define INORDER (int)1 74 75 #define EMSG "" 76 77 #ifdef GNU_COMPATIBLE 78 #define NO_PREFIX (-1) 79 #define D_PREFIX 0 80 #define DD_PREFIX 1 81 #define W_PREFIX 2 82 #endif 83 84 char *optarg; 85 int optind, opterr = 1, optopt; 86 87 static int getopt_internal(int, char * const *, const char *, 88 const struct option *, int *, int); 89 static int parse_long_options(char * const *, const char *, 90 const struct option *, int *, int, int); 91 static int gcd(int, int); 92 static void permute_args(int, int, int, char * const *); 93 94 static const char *place = EMSG; /* option letter processing */ 95 96 static int nonopt_start = -1; /* first non option argument (for permute) */ 97 static int nonopt_end = -1; /* first option after non options (for permute) */ 98 99 /* Error messages */ 100 static const char recargchar[] = "option requires an argument -- %c"; 101 static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ 102 #ifdef GNU_COMPATIBLE 103 static int dash_prefix = NO_PREFIX; 104 static const char gnuoptchar[] = "invalid option -- %c"; 105 106 static const char recargstring[] = "option `%s%s' requires an argument"; 107 static const char ambig[] = "option `%s%.*s' is ambiguous"; 108 static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; 109 static const char illoptstring[] = "unrecognized option `%s%s'"; 110 #else 111 static const char recargstring[] = "option requires an argument -- %s"; 112 static const char ambig[] = "ambiguous option -- %.*s"; 113 static const char noarg[] = "option doesn't take an argument -- %.*s"; 114 static const char illoptstring[] = "unknown option -- %s"; 115 #endif 116 117 /* 118 * Compute the greatest common divisor of a and b. 119 */ 120 static int 121 gcd(int a, int b) 122 { 123 int c; 124 125 c = a % b; 126 while (c != 0) { 127 a = b; 128 b = c; 129 c = a % b; 130 } 131 132 return (b); 133 } 134 135 /* 136 * Exchange the block from nonopt_start to nonopt_end with the block 137 * from nonopt_end to opt_end (keeping the same order of arguments 138 * in each block). 139 */ 140 static void 141 permute_args(int panonopt_start, int panonopt_end, int opt_end, 142 char * const *nargv) 143 { 144 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 145 char *swap; 146 147 /* 148 * compute lengths of blocks and number and size of cycles 149 */ 150 nnonopts = panonopt_end - panonopt_start; 151 nopts = opt_end - panonopt_end; 152 ncycle = gcd(nnonopts, nopts); 153 cyclelen = (opt_end - panonopt_start) / ncycle; 154 155 for (i = 0; i < ncycle; i++) { 156 cstart = panonopt_end+i; 157 pos = cstart; 158 for (j = 0; j < cyclelen; j++) { 159 if (pos >= panonopt_end) 160 pos -= nnonopts; 161 else 162 pos += nopts; 163 /* 164 * This is annoying - I guess the 165 * "char * const argv[]" in the declaration 166 * of getopt() - and thus getopt_long() - 167 * means that it makes a promise not to 168 * shuffle the arguments, but here we are, 169 * shuffling the arguments. 170 * 171 * (No, it's not a promise that it won't 172 * modify any of the argument strings. 173 * It's a promise that it won't modify 174 * the array of pointers to the argument 175 * strings.) 176 * 177 * So squelch the cast warnings. 178 */ 179 swap = nargv[pos]; 180 DIAG_OFF_CAST_QUAL 181 /* LINTED const cast */ 182 ((char **) nargv)[pos] = nargv[cstart]; 183 /* LINTED const cast */ 184 ((char **)nargv)[cstart] = swap; 185 DIAG_ON_CAST_QUAL 186 } 187 } 188 } 189 190 static void 191 warnx(const char *fmt, ...) 192 { 193 extern char *program_name; 194 va_list ap; 195 196 va_start(ap, fmt); 197 fprintf(stderr, "%s: ", program_name); 198 vfprintf(stderr, fmt, ap); 199 fprintf(stderr, "\n"); 200 va_end(ap); 201 } 202 203 /* 204 * parse_long_options -- 205 * Parse long options in argc/argv argument vector. 206 * Returns -1 if short_too is set and the option does not match long_options. 207 */ 208 static int 209 parse_long_options(char * const *nargv, const char *options, 210 const struct option *long_options, int *idx, int short_too, int flags) 211 { 212 const char *current_argv, *has_equal; 213 #ifdef GNU_COMPATIBLE 214 const char *current_dash; 215 #endif 216 size_t current_argv_len; 217 int i, match, exact_match, second_partial_match; 218 219 current_argv = place; 220 #ifdef GNU_COMPATIBLE 221 switch (dash_prefix) { 222 case D_PREFIX: 223 current_dash = "-"; 224 break; 225 case DD_PREFIX: 226 current_dash = "--"; 227 break; 228 case W_PREFIX: 229 current_dash = "-W "; 230 break; 231 default: 232 current_dash = ""; 233 break; 234 } 235 #endif 236 match = -1; 237 exact_match = 0; 238 second_partial_match = 0; 239 240 optind++; 241 242 if ((has_equal = strchr(current_argv, '=')) != NULL) { 243 /* argument found (--option=arg) */ 244 current_argv_len = has_equal - current_argv; 245 has_equal++; 246 } else 247 current_argv_len = strlen(current_argv); 248 249 for (i = 0; long_options[i].name; i++) { 250 /* find matching long option */ 251 if (strncmp(current_argv, long_options[i].name, 252 current_argv_len)) 253 continue; 254 255 if (strlen(long_options[i].name) == current_argv_len) { 256 /* exact match */ 257 match = i; 258 exact_match = 1; 259 break; 260 } 261 /* 262 * If this is a known short option, don't allow 263 * a partial match of a single character. 264 */ 265 if (short_too && current_argv_len == 1) 266 continue; 267 268 if (match == -1) /* first partial match */ 269 match = i; 270 else if ((flags & FLAG_LONGONLY) || 271 long_options[i].has_arg != 272 long_options[match].has_arg || 273 long_options[i].flag != long_options[match].flag || 274 long_options[i].val != long_options[match].val) 275 second_partial_match = 1; 276 } 277 if (!exact_match && second_partial_match) { 278 /* ambiguous abbreviation */ 279 if (PRINT_ERROR) 280 warnx(ambig, 281 #ifdef GNU_COMPATIBLE 282 current_dash, 283 #endif 284 (int)current_argv_len, 285 current_argv); 286 optopt = 0; 287 return (BADCH); 288 } 289 if (match != -1) { /* option found */ 290 if (long_options[match].has_arg == no_argument 291 && has_equal) { 292 if (PRINT_ERROR) 293 warnx(noarg, 294 #ifdef GNU_COMPATIBLE 295 current_dash, 296 #endif 297 (int)current_argv_len, 298 current_argv); 299 /* 300 * XXX: GNU sets optopt to val regardless of flag 301 */ 302 if (long_options[match].flag == NULL) 303 optopt = long_options[match].val; 304 else 305 optopt = 0; 306 #ifdef GNU_COMPATIBLE 307 return (BADCH); 308 #else 309 return (BADARG); 310 #endif 311 } 312 if (long_options[match].has_arg == required_argument || 313 long_options[match].has_arg == optional_argument) { 314 DIAG_OFF_CAST_QUAL 315 if (has_equal) 316 optarg = (char *)has_equal; 317 else if (long_options[match].has_arg == 318 required_argument) { 319 /* 320 * optional argument doesn't use next nargv 321 */ 322 optarg = nargv[optind++]; 323 } 324 DIAG_ON_CAST_QUAL 325 } 326 if ((long_options[match].has_arg == required_argument) 327 && (optarg == NULL)) { 328 /* 329 * Missing argument; leading ':' indicates no error 330 * should be generated. 331 */ 332 if (PRINT_ERROR) 333 warnx(recargstring, 334 #ifdef GNU_COMPATIBLE 335 current_dash, 336 #endif 337 current_argv); 338 /* 339 * XXX: GNU sets optopt to val regardless of flag 340 */ 341 if (long_options[match].flag == NULL) 342 optopt = long_options[match].val; 343 else 344 optopt = 0; 345 --optind; 346 return (BADARG); 347 } 348 } else { /* unknown option */ 349 if (short_too) { 350 --optind; 351 return (-1); 352 } 353 if (PRINT_ERROR) 354 warnx(illoptstring, 355 #ifdef GNU_COMPATIBLE 356 current_dash, 357 #endif 358 current_argv); 359 optopt = 0; 360 return (BADCH); 361 } 362 if (idx) 363 *idx = match; 364 if (long_options[match].flag) { 365 *long_options[match].flag = long_options[match].val; 366 return (0); 367 } else 368 return (long_options[match].val); 369 } 370 371 /* 372 * getopt_internal -- 373 * Parse argc/argv argument vector. Called by user level routines. 374 */ 375 static int 376 getopt_internal(int nargc, char * const *nargv, const char *options, 377 const struct option *long_options, int *idx, int flags) 378 { 379 char *oli; /* option letter list index */ 380 int optchar, short_too; 381 int posixly_correct; /* no static, can be changed on the fly */ 382 383 if (options == NULL) 384 return (-1); 385 386 /* 387 * Disable GNU extensions if POSIXLY_CORRECT is set or options 388 * string begins with a '+'. 389 */ 390 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); 391 #ifdef GNU_COMPATIBLE 392 if (*options == '-') 393 flags |= FLAG_ALLARGS; 394 else if (posixly_correct || *options == '+') 395 flags &= ~FLAG_PERMUTE; 396 #else 397 if (posixly_correct || *options == '+') 398 flags &= ~FLAG_PERMUTE; 399 else if (*options == '-') 400 flags |= FLAG_ALLARGS; 401 #endif 402 if (*options == '+' || *options == '-') 403 options++; 404 405 /* 406 * XXX Some GNU programs (like cvs) set optind to 0 instead of 407 * XXX using optreset. Work around this braindamage. 408 */ 409 if (optind == 0) 410 optind = 1; 411 412 optarg = NULL; 413 start: 414 if (!*place) { /* update scanning pointer */ 415 if (optind >= nargc) { /* end of argument vector */ 416 place = EMSG; 417 if (nonopt_end != -1) { 418 /* do permutation, if we have to */ 419 permute_args(nonopt_start, nonopt_end, 420 optind, nargv); 421 optind -= nonopt_end - nonopt_start; 422 } 423 else if (nonopt_start != -1) { 424 /* 425 * If we skipped non-options, set optind 426 * to the first of them. 427 */ 428 optind = nonopt_start; 429 } 430 nonopt_start = nonopt_end = -1; 431 return (-1); 432 } 433 if (*(place = nargv[optind]) != '-' || 434 #ifdef GNU_COMPATIBLE 435 place[1] == '\0') { 436 #else 437 (place[1] == '\0' && strchr(options, '-') == NULL)) { 438 #endif 439 place = EMSG; /* found non-option */ 440 if (flags & FLAG_ALLARGS) { 441 /* 442 * GNU extension: 443 * return non-option as argument to option 1 444 */ 445 optarg = nargv[optind++]; 446 return (INORDER); 447 } 448 if (!(flags & FLAG_PERMUTE)) { 449 /* 450 * If no permutation wanted, stop parsing 451 * at first non-option. 452 */ 453 return (-1); 454 } 455 /* do permutation */ 456 if (nonopt_start == -1) 457 nonopt_start = optind; 458 else if (nonopt_end != -1) { 459 permute_args(nonopt_start, nonopt_end, 460 optind, nargv); 461 nonopt_start = optind - 462 (nonopt_end - nonopt_start); 463 nonopt_end = -1; 464 } 465 optind++; 466 /* process next argument */ 467 goto start; 468 } 469 if (nonopt_start != -1 && nonopt_end == -1) 470 nonopt_end = optind; 471 472 /* 473 * If we have "-" do nothing, if "--" we are done. 474 */ 475 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 476 optind++; 477 place = EMSG; 478 /* 479 * We found an option (--), so if we skipped 480 * non-options, we have to permute. 481 */ 482 if (nonopt_end != -1) { 483 permute_args(nonopt_start, nonopt_end, 484 optind, nargv); 485 optind -= nonopt_end - nonopt_start; 486 } 487 nonopt_start = nonopt_end = -1; 488 return (-1); 489 } 490 } 491 492 /* 493 * Check long options if: 494 * 1) we were passed some 495 * 2) the arg is not just "-" 496 * 3) either the arg starts with -- we are getopt_long_only() 497 */ 498 if (long_options != NULL && place != nargv[optind] && 499 (*place == '-' || (flags & FLAG_LONGONLY))) { 500 short_too = 0; 501 #ifdef GNU_COMPATIBLE 502 dash_prefix = D_PREFIX; 503 #endif 504 if (*place == '-') { 505 place++; /* --foo long option */ 506 #ifdef GNU_COMPATIBLE 507 dash_prefix = DD_PREFIX; 508 #endif 509 } else if (*place != ':' && strchr(options, *place) != NULL) 510 short_too = 1; /* could be short option too */ 511 512 optchar = parse_long_options(nargv, options, long_options, 513 idx, short_too, flags); 514 if (optchar != -1) { 515 place = EMSG; 516 return (optchar); 517 } 518 } 519 520 if ((optchar = (int)*place++) == (int)':' || 521 (optchar == (int)'-' && *place != '\0') || 522 (oli = strchr(options, optchar)) == NULL) { 523 /* 524 * If the user specified "-" and '-' isn't listed in 525 * options, return -1 (non-option) as per POSIX. 526 * Otherwise, it is an unknown option character (or ':'). 527 */ 528 if (optchar == (int)'-' && *place == '\0') 529 return (-1); 530 if (!*place) 531 ++optind; 532 #ifdef GNU_COMPATIBLE 533 if (PRINT_ERROR) 534 warnx(posixly_correct ? illoptchar : gnuoptchar, 535 optchar); 536 #else 537 if (PRINT_ERROR) 538 warnx(illoptchar, optchar); 539 #endif 540 optopt = optchar; 541 return (BADCH); 542 } 543 if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 544 /* -W long-option */ 545 if (*place) /* no space */ 546 /* NOTHING */; 547 else if (++optind >= nargc) { /* no arg */ 548 place = EMSG; 549 if (PRINT_ERROR) 550 warnx(recargchar, optchar); 551 optopt = optchar; 552 return (BADARG); 553 } else /* white space */ 554 place = nargv[optind]; 555 #ifdef GNU_COMPATIBLE 556 dash_prefix = W_PREFIX; 557 #endif 558 optchar = parse_long_options(nargv, options, long_options, 559 idx, 0, flags); 560 place = EMSG; 561 return (optchar); 562 } 563 if (*++oli != ':') { /* doesn't take argument */ 564 if (!*place) 565 ++optind; 566 } else { /* takes (optional) argument */ 567 optarg = NULL; 568 if (*place) { /* no white space */ 569 DIAG_OFF_CAST_QUAL 570 optarg = (char *)place; 571 DIAG_ON_CAST_QUAL 572 } else if (oli[1] != ':') { /* arg not optional */ 573 if (++optind >= nargc) { /* no arg */ 574 place = EMSG; 575 if (PRINT_ERROR) 576 warnx(recargchar, optchar); 577 optopt = optchar; 578 return (BADARG); 579 } else 580 optarg = nargv[optind]; 581 } 582 place = EMSG; 583 ++optind; 584 } 585 /* dump back option letter */ 586 return (optchar); 587 } 588 589 #ifdef REPLACE_GETOPT 590 /* 591 * getopt -- 592 * Parse argc/argv argument vector. 593 * 594 * [eventually this will replace the BSD getopt] 595 */ 596 int 597 getopt(int nargc, char * const *nargv, const char *options) 598 { 599 600 /* 601 * We don't pass FLAG_PERMUTE to getopt_internal() since 602 * the BSD getopt(3) (unlike GNU) has never done this. 603 * 604 * Furthermore, since many privileged programs call getopt() 605 * before dropping privileges it makes sense to keep things 606 * as simple (and bug-free) as possible. 607 */ 608 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); 609 } 610 #endif /* REPLACE_GETOPT */ 611 612 /* 613 * getopt_long -- 614 * Parse argc/argv argument vector. 615 */ 616 int 617 getopt_long(int nargc, char * const *nargv, const char *options, 618 const struct option *long_options, int *idx) 619 { 620 621 return (getopt_internal(nargc, nargv, options, long_options, idx, 622 FLAG_PERMUTE)); 623 } 624 625 /* 626 * getopt_long_only -- 627 * Parse argc/argv argument vector. 628 */ 629 int 630 getopt_long_only(int nargc, char * const *nargv, const char *options, 631 const struct option *long_options, int *idx) 632 { 633 634 return (getopt_internal(nargc, nargv, options, long_options, idx, 635 FLAG_PERMUTE|FLAG_LONGONLY)); 636 } 637