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