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 } else if (nonopt_start != -1) { 423 /* 424 * If we skipped non-options, set optind 425 * to the first of them. 426 */ 427 optind = nonopt_start; 428 } 429 nonopt_start = nonopt_end = -1; 430 return (-1); 431 } 432 if (*(place = nargv[optind]) != '-' || 433 #ifdef GNU_COMPATIBLE 434 place[1] == '\0') { 435 #else 436 (place[1] == '\0' && strchr(options, '-') == NULL)) { 437 #endif 438 place = EMSG; /* found non-option */ 439 if (flags & FLAG_ALLARGS) { 440 /* 441 * GNU extension: 442 * return non-option as argument to option 1 443 */ 444 optarg = nargv[optind++]; 445 return (INORDER); 446 } 447 if (!(flags & FLAG_PERMUTE)) { 448 /* 449 * If no permutation wanted, stop parsing 450 * at first non-option. 451 */ 452 return (-1); 453 } 454 /* do permutation */ 455 if (nonopt_start == -1) 456 nonopt_start = optind; 457 else if (nonopt_end != -1) { 458 permute_args(nonopt_start, nonopt_end, 459 optind, nargv); 460 nonopt_start = optind - 461 (nonopt_end - nonopt_start); 462 nonopt_end = -1; 463 } 464 optind++; 465 /* process next argument */ 466 goto start; 467 } 468 if (nonopt_start != -1 && nonopt_end == -1) 469 nonopt_end = optind; 470 471 /* 472 * If we have "-" do nothing, if "--" we are done. 473 */ 474 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 475 optind++; 476 place = EMSG; 477 /* 478 * We found an option (--), so if we skipped 479 * non-options, we have to permute. 480 */ 481 if (nonopt_end != -1) { 482 permute_args(nonopt_start, nonopt_end, 483 optind, nargv); 484 optind -= nonopt_end - nonopt_start; 485 } 486 nonopt_start = nonopt_end = -1; 487 return (-1); 488 } 489 } 490 491 /* 492 * Check long options if: 493 * 1) we were passed some 494 * 2) the arg is not just "-" 495 * 3) either the arg starts with -- we are getopt_long_only() 496 */ 497 if (long_options != NULL && place != nargv[optind] && 498 (*place == '-' || (flags & FLAG_LONGONLY))) { 499 short_too = 0; 500 #ifdef GNU_COMPATIBLE 501 dash_prefix = D_PREFIX; 502 #endif 503 if (*place == '-') { 504 place++; /* --foo long option */ 505 #ifdef GNU_COMPATIBLE 506 dash_prefix = DD_PREFIX; 507 #endif 508 } else if (*place != ':' && strchr(options, *place) != NULL) 509 short_too = 1; /* could be short option too */ 510 511 optchar = parse_long_options(nargv, options, long_options, 512 idx, short_too, flags); 513 if (optchar != -1) { 514 place = EMSG; 515 return (optchar); 516 } 517 } 518 519 if ((optchar = (int)*place++) == (int)':' || 520 (optchar == (int)'-' && *place != '\0') || 521 (oli = strchr(options, optchar)) == NULL) { 522 /* 523 * If the user specified "-" and '-' isn't listed in 524 * options, return -1 (non-option) as per POSIX. 525 * Otherwise, it is an unknown option character (or ':'). 526 */ 527 if (optchar == (int)'-' && *place == '\0') 528 return (-1); 529 if (!*place) 530 ++optind; 531 #ifdef GNU_COMPATIBLE 532 if (PRINT_ERROR) 533 warnx(posixly_correct ? illoptchar : gnuoptchar, 534 optchar); 535 #else 536 if (PRINT_ERROR) 537 warnx(illoptchar, optchar); 538 #endif 539 optopt = optchar; 540 return (BADCH); 541 } 542 if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 543 /* -W long-option */ 544 if (*place) /* no space */ 545 /* NOTHING */; 546 else if (++optind >= nargc) { /* no arg */ 547 place = EMSG; 548 if (PRINT_ERROR) 549 warnx(recargchar, optchar); 550 optopt = optchar; 551 return (BADARG); 552 } else /* white space */ 553 place = nargv[optind]; 554 #ifdef GNU_COMPATIBLE 555 dash_prefix = W_PREFIX; 556 #endif 557 optchar = parse_long_options(nargv, options, long_options, 558 idx, 0, flags); 559 place = EMSG; 560 return (optchar); 561 } 562 if (*++oli != ':') { /* doesn't take argument */ 563 if (!*place) 564 ++optind; 565 } else { /* takes (optional) argument */ 566 optarg = NULL; 567 if (*place) { /* no white space */ 568 DIAG_OFF_CAST_QUAL 569 optarg = (char *)place; 570 DIAG_ON_CAST_QUAL 571 } else if (oli[1] != ':') { /* arg not optional */ 572 if (++optind >= nargc) { /* no arg */ 573 place = EMSG; 574 if (PRINT_ERROR) 575 warnx(recargchar, optchar); 576 optopt = optchar; 577 return (BADARG); 578 } else 579 optarg = nargv[optind]; 580 } 581 place = EMSG; 582 ++optind; 583 } 584 /* dump back option letter */ 585 return (optchar); 586 } 587 588 #ifdef REPLACE_GETOPT 589 /* 590 * getopt -- 591 * Parse argc/argv argument vector. 592 * 593 * [eventually this will replace the BSD getopt] 594 */ 595 int 596 getopt(int nargc, char * const *nargv, const char *options) 597 { 598 599 /* 600 * We don't pass FLAG_PERMUTE to getopt_internal() since 601 * the BSD getopt(3) (unlike GNU) has never done this. 602 * 603 * Furthermore, since many privileged programs call getopt() 604 * before dropping privileges it makes sense to keep things 605 * as simple (and bug-free) as possible. 606 */ 607 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); 608 } 609 #endif /* REPLACE_GETOPT */ 610 611 /* 612 * getopt_long -- 613 * Parse argc/argv argument vector. 614 */ 615 int 616 getopt_long(int nargc, char * const *nargv, const char *options, 617 const struct option *long_options, int *idx) 618 { 619 620 return (getopt_internal(nargc, nargv, options, long_options, idx, 621 FLAG_PERMUTE)); 622 } 623 624 /* 625 * getopt_long_only -- 626 * Parse argc/argv argument vector. 627 */ 628 int 629 getopt_long_only(int nargc, char * const *nargv, const char *options, 630 const struct option *long_options, int *idx) 631 { 632 633 return (getopt_internal(nargc, nargv, options, long_options, idx, 634 FLAG_PERMUTE|FLAG_LONGONLY)); 635 } 636