1 /* $OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $ */ 2 /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ 3 4 /*- 5 * Copyright (c) 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Dieter Baron and Thomas Klausner. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #if defined(LIBC_SCCS) && !defined(lint) 41 static char *rcsid = "$OpenBSD: getopt_long.c,v 1.2 2002/12/03 20:28:12 millert Exp $"; 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include <err.h> 45 #include <errno.h> 46 #include <getopt.h> 47 #include <stdlib.h> 48 #include <string.h> 49 50 #ifdef REPLACE_GETOPT 51 int opterr = 1; /* if error message should be printed */ 52 int optind = 1; /* index into parent argv vector */ 53 int optopt = '?'; /* character checked for validity */ 54 int optreset; /* reset getopt */ 55 char *optarg; /* argument associated with option */ 56 #endif 57 58 #define PRINT_ERROR ((opterr) && (*options != ':')) 59 60 #define FLAG_PERMUTE 0x01 61 #define FLAG_ALLARGS 0x02 62 #define FLAG_LONGONLY 0x04 63 64 /* return values */ 65 #define BADCH (int)'?' 66 #define BADARG ((*options == ':') ? (int)':' : (int)'?') 67 #define INORDER (int)1 68 69 #define EMSG "" 70 71 static int getopt_internal(int, char * const *, const char *, int); 72 static int getopt_long_internal(int, char * const *, const char *, 73 const struct option *, int *, int); 74 static int gcd(int, int); 75 static void permute_args(int, int, int, char * const *); 76 77 static char *place = EMSG; /* option letter processing */ 78 79 /* XXX: set optreset to 1 rather than these two */ 80 static int nonopt_start = -1; /* first non option argument (for permute) */ 81 static int nonopt_end = -1; /* first option after non options (for permute) */ 82 83 /* Error messages */ 84 static const char recargchar[] = "option requires an argument -- %c"; 85 static const char recargstring[] = "option requires an argument -- %s"; 86 static const char ambig[] = "ambiguous option -- %.*s"; 87 static const char noarg[] = "option doesn't take an argument -- %.*s"; 88 static const char illoptchar[] = "unknown option -- %c"; 89 static const char illoptstring[] = "unknown option -- %s"; 90 91 /* 92 * Compute the greatest common divisor of a and b. 93 */ 94 static int 95 gcd(a, b) 96 int a; 97 int b; 98 { 99 int c; 100 101 c = a % b; 102 while (c != 0) { 103 a = b; 104 b = c; 105 c = a % b; 106 } 107 108 return b; 109 } 110 111 /* 112 * Exchange the block from nonopt_start to nonopt_end with the block 113 * from nonopt_end to opt_end (keeping the same order of arguments 114 * in each block). 115 */ 116 static void 117 permute_args(panonopt_start, panonopt_end, opt_end, nargv) 118 int panonopt_start; 119 int panonopt_end; 120 int opt_end; 121 char * const *nargv; 122 { 123 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 124 char *swap; 125 126 /* 127 * compute lengths of blocks and number and size of cycles 128 */ 129 nnonopts = panonopt_end - panonopt_start; 130 nopts = opt_end - panonopt_end; 131 ncycle = gcd(nnonopts, nopts); 132 cyclelen = (opt_end - panonopt_start) / ncycle; 133 134 for (i = 0; i < ncycle; i++) { 135 cstart = panonopt_end+i; 136 pos = cstart; 137 for (j = 0; j < cyclelen; j++) { 138 if (pos >= panonopt_end) 139 pos -= nnonopts; 140 else 141 pos += nopts; 142 swap = nargv[pos]; 143 /* LINTED const cast */ 144 ((char **) nargv)[pos] = nargv[cstart]; 145 /* LINTED const cast */ 146 ((char **)nargv)[cstart] = swap; 147 } 148 } 149 } 150 151 /* 152 * getopt_internal -- 153 * Parse argc/argv argument vector. Called by user level routines. 154 * Returns -2 if -- is found (can be long option or end of options marker). 155 */ 156 static int 157 getopt_internal(nargc, nargv, options, flags) 158 int nargc; 159 char * const *nargv; 160 const char *options; 161 int flags; 162 { 163 char *oli; /* option letter list index */ 164 int optchar; 165 166 optarg = NULL; 167 168 /* 169 * XXX Some programs (like rsyncd) expect to be able to 170 * XXX re-initialize optind to 0 and have getopt_long(3) 171 * XXX properly function again. Work around this braindamage. 172 */ 173 if (optind == 0) 174 optind = 1; 175 176 if (optreset) 177 nonopt_start = nonopt_end = -1; 178 start: 179 if (optreset || !*place) { /* update scanning pointer */ 180 optreset = 0; 181 if (optind >= nargc) { /* end of argument vector */ 182 place = EMSG; 183 if (nonopt_end != -1) { 184 /* do permutation, if we have to */ 185 permute_args(nonopt_start, nonopt_end, 186 optind, nargv); 187 optind -= nonopt_end - nonopt_start; 188 } 189 else if (nonopt_start != -1) { 190 /* 191 * If we skipped non-options, set optind 192 * to the first of them. 193 */ 194 optind = nonopt_start; 195 } 196 nonopt_start = nonopt_end = -1; 197 return -1; 198 } 199 if ((*(place = nargv[optind]) != '-') 200 || (place[1] == '\0')) { /* found non-option */ 201 place = EMSG; 202 if (flags & FLAG_ALLARGS) { 203 /* 204 * GNU extension: 205 * return non-option as argument to option 1 206 */ 207 optarg = nargv[optind++]; 208 return INORDER; 209 } 210 if (!(flags & FLAG_PERMUTE)) { 211 /* 212 * If no permutation wanted, stop parsing 213 * at first non-option. 214 */ 215 return -1; 216 } 217 /* do permutation */ 218 if (nonopt_start == -1) 219 nonopt_start = optind; 220 else if (nonopt_end != -1) { 221 permute_args(nonopt_start, nonopt_end, 222 optind, nargv); 223 nonopt_start = optind - 224 (nonopt_end - nonopt_start); 225 nonopt_end = -1; 226 } 227 optind++; 228 /* process next argument */ 229 goto start; 230 } 231 if (nonopt_start != -1 && nonopt_end == -1) 232 nonopt_end = optind; 233 if (place[1] && *++place == '-') { /* found "--" */ 234 place++; 235 return -2; 236 } 237 } 238 if ((optchar = (int)*place++) == (int)':' || 239 (oli = strchr(options, optchar)) == NULL) { 240 /* could it be a long option with a single '-'? */ 241 if (flags & FLAG_LONGONLY) 242 return -2; 243 /* option letter unknown or ':' */ 244 if (!*place) 245 ++optind; 246 if (PRINT_ERROR) 247 warnx(illoptchar, optchar); 248 optopt = optchar; 249 return BADCH; 250 } 251 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ 252 /* XXX: what if no long options provided (called by getopt)? */ 253 if (*place) 254 return -2; 255 256 if (++optind >= nargc) { /* no arg */ 257 place = EMSG; 258 if (PRINT_ERROR) 259 warnx(recargchar, optchar); 260 optopt = optchar; 261 return BADARG; 262 } else /* white space */ 263 place = nargv[optind]; 264 /* 265 * Handle -W arg the same as --arg (which causes getopt to 266 * stop parsing). 267 */ 268 return -2; 269 } 270 if (*++oli != ':') { /* doesn't take argument */ 271 if (!*place) 272 ++optind; 273 } else { /* takes (optional) argument */ 274 optarg = NULL; 275 if (*place) /* no white space */ 276 optarg = place; 277 /* XXX: disable test for :: if PC? (GNU doesn't) */ 278 else if (oli[1] != ':') { /* arg not optional */ 279 if (++optind >= nargc) { /* no arg */ 280 place = EMSG; 281 if (PRINT_ERROR) 282 warnx(recargchar, optchar); 283 optopt = optchar; 284 return BADARG; 285 } else 286 optarg = nargv[optind]; 287 } 288 place = EMSG; 289 ++optind; 290 } 291 /* dump back option letter */ 292 return optchar; 293 } 294 295 #ifdef REPLACE_GETOPT 296 /* 297 * getopt -- 298 * Parse argc/argv argument vector. 299 * 300 * [eventually this will replace the BSD getopt] 301 */ 302 int 303 getopt(nargc, nargv, options) 304 int nargc; 305 char * const *nargv; 306 const char *options; 307 { 308 int retval; 309 310 if ((retval = getopt_internal(nargc, nargv, options, 0)) == -2) { 311 ++optind; 312 /* 313 * We found an option (--), so if we skipped non-options, 314 * we have to permute. 315 */ 316 if (nonopt_end != -1) { 317 permute_args(nonopt_start, nonopt_end, optind, 318 nargv); 319 optind -= nonopt_end - nonopt_start; 320 } 321 nonopt_start = nonopt_end = -1; 322 retval = -1; 323 } 324 return retval; 325 } 326 #endif /* REPLACE_GETOPT */ 327 328 /* 329 * getopt_long_internal -- 330 * Parse argc/argv argument vector. 331 */ 332 static int 333 getopt_long_internal(nargc, nargv, options, long_options, idx, flags) 334 int nargc; 335 char * const *nargv; 336 const char *options; 337 const struct option *long_options; 338 int *idx; 339 int flags; 340 { 341 int retval; 342 343 /* 344 * Disable GNU extensions if POSIXLY_CORRECT is set or options 345 * string begins with a '+'. 346 */ 347 if (getenv("POSIXLY_CORRECT")) { 348 if (*options == '+' || *options == '-') 349 options++; 350 } else { 351 if (*options == '+') { 352 options++; 353 } else { 354 flags |= FLAG_PERMUTE; 355 if (*options == '-') { 356 flags |= FLAG_ALLARGS; 357 options++; 358 } 359 } 360 } 361 362 if ((retval = getopt_internal(nargc, nargv, options, flags)) == -2) { 363 char *current_argv, *has_equal; 364 size_t current_argv_len; 365 int i, match; 366 367 current_argv = place; 368 match = -1; 369 370 optind++; 371 place = EMSG; 372 373 if (*current_argv == '\0') { /* found "--" */ 374 /* 375 * We found an option (--), so if we skipped 376 * non-options, we have to permute. 377 */ 378 if (nonopt_end != -1) { 379 permute_args(nonopt_start, nonopt_end, 380 optind, nargv); 381 optind -= nonopt_end - nonopt_start; 382 } 383 nonopt_start = nonopt_end = -1; 384 return -1; 385 } 386 if ((has_equal = strchr(current_argv, '=')) != NULL) { 387 /* argument found (--option=arg) */ 388 current_argv_len = has_equal - current_argv; 389 has_equal++; 390 } else 391 current_argv_len = strlen(current_argv); 392 393 for (i = 0; long_options[i].name; i++) { 394 /* find matching long option */ 395 if (strncmp(current_argv, long_options[i].name, 396 current_argv_len)) 397 continue; 398 399 if (strlen(long_options[i].name) == 400 (unsigned)current_argv_len) { 401 /* exact match */ 402 match = i; 403 break; 404 } 405 if (match == -1) /* partial match */ 406 match = i; 407 else { 408 /* ambiguous abbreviation */ 409 if (PRINT_ERROR) 410 warnx(ambig, (int)current_argv_len, 411 current_argv); 412 optopt = 0; 413 return BADCH; 414 } 415 } 416 if (match != -1) { /* option found */ 417 if (long_options[match].has_arg == no_argument 418 && has_equal) { 419 if (PRINT_ERROR) 420 warnx(noarg, (int)current_argv_len, 421 current_argv); 422 /* 423 * XXX: GNU sets optopt to val regardless of 424 * flag 425 */ 426 if (long_options[match].flag == NULL) 427 optopt = long_options[match].val; 428 else 429 optopt = 0; 430 return BADARG; 431 } 432 if (long_options[match].has_arg == required_argument || 433 long_options[match].has_arg == optional_argument) { 434 if (has_equal) 435 optarg = has_equal; 436 else if (long_options[match].has_arg == 437 required_argument) { 438 /* 439 * optional argument doesn't use 440 * next nargv 441 */ 442 optarg = nargv[optind++]; 443 } 444 } 445 if ((long_options[match].has_arg == required_argument) 446 && (optarg == NULL)) { 447 /* 448 * Missing argument; leading ':' 449 * indicates no error should be generated 450 */ 451 if (PRINT_ERROR) 452 warnx(recargstring, current_argv); 453 /* 454 * XXX: GNU sets optopt to val regardless 455 * of flag 456 */ 457 if (long_options[match].flag == NULL) 458 optopt = long_options[match].val; 459 else 460 optopt = 0; 461 --optind; 462 return BADARG; 463 } 464 } else { /* unknown option */ 465 if (PRINT_ERROR) 466 warnx(illoptstring, current_argv); 467 optopt = 0; 468 return BADCH; 469 } 470 if (long_options[match].flag) { 471 *long_options[match].flag = long_options[match].val; 472 retval = 0; 473 } else 474 retval = long_options[match].val; 475 if (idx) 476 *idx = match; 477 } 478 return retval; 479 } 480 481 /* 482 * getopt_long -- 483 * Parse argc/argv argument vector. 484 */ 485 int 486 getopt_long(nargc, nargv, options, long_options, idx) 487 int nargc; 488 char * const *nargv; 489 const char *options; 490 const struct option *long_options; 491 int *idx; 492 { 493 494 return getopt_long_internal(nargc, nargv, options, long_options, idx, 0); 495 } 496 497 /* 498 * getopt_long_only -- 499 * Parse argc/argv argument vector. 500 */ 501 int 502 getopt_long_only(nargc, nargv, options, long_options, idx) 503 int nargc; 504 char * const *nargv; 505 const char *options; 506 const struct option *long_options; 507 int *idx; 508 { 509 510 return getopt_long_internal(nargc, nargv, options, long_options, idx, 511 FLAG_LONGONLY); 512 } 513