1 /* $NetBSD: getopt_long.c,v 1.10 2001/01/04 03:35:29 lukem 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.10 2001/01/04 03:35:29 lukem Exp $"); 42 #endif /* LIBC_SCCS and not lint */ 43 44 #include "namespace.h" 45 46 #include <assert.h> 47 #include <errno.h> 48 #include <err.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <getopt.h> 52 53 #ifdef REPLACE_GETOPT 54 #ifdef __weak_alias 55 __weak_alias(getopt,_getopt) 56 #endif 57 int opterr = 1; /* if error message should be printed */ 58 int optind = 1; /* index into parent argv vector */ 59 int optopt = '?'; /* character checked for validity */ 60 int optreset; /* reset getopt */ 61 char *optarg; /* argument associated with option */ 62 #endif 63 64 #ifdef __weak_alias 65 __weak_alias(getopt_long,_getopt_long) 66 #endif 67 68 69 #define IGNORE_FIRST (*options == '-' || *options == '+') 70 #define PRINT_ERROR ((opterr) && ((*options != ':') \ 71 || (IGNORE_FIRST && options[1] != ':'))) 72 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) 73 #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) 74 /* XXX: GNU ignores PC if *options == '-' */ 75 #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') 76 77 /* return values */ 78 #define BADCH (int)'?' 79 #define BADARG ((IGNORE_FIRST && options[1] == ':') \ 80 || (*options == ':') ? (int)':' : (int)'?') 81 #define INORDER (int)1 82 83 #define EMSG "" 84 85 static int getopt_internal __P((int, char * const *, const char *)); 86 static int gcd __P((int, int)); 87 static void permute_args __P((int, int, int, char * const *)); 88 89 static char *place = EMSG; /* option letter processing */ 90 91 /* XXX: set optreset to 1 rather than these two */ 92 static int nonopt_start = -1; /* first non option argument (for permute) */ 93 static int nonopt_end = -1; /* first option after non options (for permute) */ 94 95 /* Error messages */ 96 static const char recargchar[] = "option requires an argument -- %c"; 97 static const char recargstring[] = "option requires an argument -- %s"; 98 static const char ambig[] = "ambiguous option -- %.*s"; 99 static const char noarg[] = "option doesn't take an argument -- %.*s"; 100 static const char illoptchar[] = "illegal option -- %c"; 101 static const char illoptstring[] = "illegal option -- %s"; 102 103 104 extern char *__progname; 105 106 /* 107 * Compute the greatest common divisor of a and b. 108 */ 109 static int 110 gcd(a, b) 111 int a; 112 int b; 113 { 114 int c; 115 116 c = a % b; 117 while (c != 0) { 118 a = b; 119 b = c; 120 c = a % b; 121 } 122 123 return b; 124 } 125 126 /* 127 * Exchange the block from nonopt_start to nonopt_end with the block 128 * from nonopt_end to opt_end (keeping the same order of arguments 129 * in each block). 130 */ 131 static void 132 permute_args(nonopt_start, nonopt_end, opt_end, nargv) 133 int nonopt_start; 134 int nonopt_end; 135 int opt_end; 136 char * const *nargv; 137 { 138 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 139 char *swap; 140 141 _DIAGASSERT(nargv != NULL); 142 143 /* 144 * compute lengths of blocks and number and size of cycles 145 */ 146 nnonopts = nonopt_end - nonopt_start; 147 nopts = opt_end - nonopt_end; 148 ncycle = gcd(nnonopts, nopts); 149 cyclelen = (opt_end - nonopt_start) / ncycle; 150 151 for (i = 0; i < ncycle; i++) { 152 cstart = nonopt_end+i; 153 pos = cstart; 154 for (j = 0; j < cyclelen; j++) { 155 if (pos >= nonopt_end) 156 pos -= nnonopts; 157 else 158 pos += nopts; 159 swap = nargv[pos]; 160 /* LINTED const cast */ 161 ((char **) nargv)[pos] = nargv[cstart]; 162 /* LINTED const cast */ 163 ((char **)nargv)[cstart] = swap; 164 } 165 } 166 } 167 168 /* 169 * getopt_internal -- 170 * Parse argc/argv argument vector. Called by user level routines. 171 * Returns -2 if -- is found (can be long option or end of options marker). 172 */ 173 static int 174 getopt_internal(nargc, nargv, options) 175 int nargc; 176 char * const *nargv; 177 const char *options; 178 { 179 char *oli; /* option letter list index */ 180 int optchar; 181 182 _DIAGASSERT(nargv != NULL); 183 _DIAGASSERT(options != NULL); 184 185 optarg = NULL; 186 187 /* 188 * XXX Some programs (like rsyncd) expect to be able to 189 * XXX re-initialize optind to 0 and have getopt_long(3) 190 * XXX properly function again. Work around this braindamage. 191 */ 192 if (optind == 0) 193 optind = 1; 194 195 if (optreset) 196 nonopt_start = nonopt_end = -1; 197 start: 198 if (optreset || !*place) { /* update scanning pointer */ 199 optreset = 0; 200 if (optind >= nargc) { /* end of argument vector */ 201 place = EMSG; 202 if (nonopt_end != -1) { 203 /* do permutation, if we have to */ 204 permute_args(nonopt_start, nonopt_end, 205 optind, nargv); 206 optind -= nonopt_end - nonopt_start; 207 } 208 else if (nonopt_start != -1) { 209 /* 210 * If we skipped non-options, set optind 211 * to the first of them. 212 */ 213 optind = nonopt_start; 214 } 215 nonopt_start = nonopt_end = -1; 216 return -1; 217 } 218 if ((*(place = nargv[optind]) != '-') 219 || (place[1] == '\0')) { /* found non-option */ 220 place = EMSG; 221 if (IN_ORDER) { 222 /* 223 * GNU extension: 224 * return non-option as argument to option 1 225 */ 226 optarg = nargv[optind++]; 227 return INORDER; 228 } 229 if (!PERMUTE) { 230 /* 231 * if no permutation wanted, stop parsing 232 * at first non-option 233 */ 234 return -1; 235 } 236 /* do permutation */ 237 if (nonopt_start == -1) 238 nonopt_start = optind; 239 else if (nonopt_end != -1) { 240 permute_args(nonopt_start, nonopt_end, 241 optind, nargv); 242 nonopt_start = optind - 243 (nonopt_end - nonopt_start); 244 nonopt_end = -1; 245 } 246 optind++; 247 /* process next argument */ 248 goto start; 249 } 250 if (nonopt_start != -1 && nonopt_end == -1) 251 nonopt_end = optind; 252 if (place[1] && *++place == '-') { /* found "--" */ 253 place++; 254 return -2; 255 } 256 } 257 if ((optchar = (int)*place++) == (int)':' || 258 (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { 259 /* option letter unknown or ':' */ 260 if (!*place) 261 ++optind; 262 if (PRINT_ERROR) 263 warnx(illoptchar, optchar); 264 optopt = optchar; 265 return BADCH; 266 } 267 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ 268 /* XXX: what if no long options provided (called by getopt)? */ 269 if (*place) 270 return -2; 271 272 if (++optind >= nargc) { /* no arg */ 273 place = EMSG; 274 if (PRINT_ERROR) 275 warnx(recargchar, optchar); 276 optopt = optchar; 277 return BADARG; 278 } else /* white space */ 279 place = nargv[optind]; 280 /* 281 * Handle -W arg the same as --arg (which causes getopt to 282 * stop parsing). 283 */ 284 return -2; 285 } 286 if (*++oli != ':') { /* doesn't take argument */ 287 if (!*place) 288 ++optind; 289 } else { /* takes (optional) argument */ 290 optarg = NULL; 291 if (*place) /* no white space */ 292 optarg = place; 293 /* XXX: disable test for :: if PC? (GNU doesn't) */ 294 else if (oli[1] != ':') { /* arg not optional */ 295 if (++optind >= nargc) { /* no arg */ 296 place = EMSG; 297 if (PRINT_ERROR) 298 warnx(recargchar, optchar); 299 optopt = optchar; 300 return BADARG; 301 } else 302 optarg = nargv[optind]; 303 } 304 place = EMSG; 305 ++optind; 306 } 307 /* dump back option letter */ 308 return optchar; 309 } 310 311 #ifdef REPLACE_GETOPT 312 /* 313 * getopt -- 314 * Parse argc/argv argument vector. 315 * 316 * [eventually this will replace the real getopt] 317 */ 318 int 319 getopt(nargc, nargv, options) 320 int nargc; 321 char * const *nargv; 322 const char *options; 323 { 324 int retval; 325 326 _DIAGASSERT(nargv != NULL); 327 _DIAGASSERT(options != NULL); 328 329 if ((retval = getopt_internal(nargc, nargv, options)) == -2) { 330 ++optind; 331 /* 332 * We found an option (--), so if we skipped non-options, 333 * we have to permute. 334 */ 335 if (nonopt_end != -1) { 336 permute_args(nonopt_start, nonopt_end, optind, 337 nargv); 338 optind -= nonopt_end - nonopt_start; 339 } 340 nonopt_start = nonopt_end = -1; 341 retval = -1; 342 } 343 return retval; 344 } 345 #endif 346 347 /* 348 * getopt_long -- 349 * Parse argc/argv argument vector. 350 */ 351 int 352 getopt_long(nargc, nargv, options, long_options, idx) 353 int nargc; 354 char * const *nargv; 355 const char *options; 356 const struct option *long_options; 357 int *idx; 358 { 359 int retval; 360 361 _DIAGASSERT(nargv != NULL); 362 _DIAGASSERT(options != NULL); 363 _DIAGASSERT(long_options != NULL); 364 /* idx may be NULL */ 365 366 if ((retval = getopt_internal(nargc, nargv, options)) == -2) { 367 char *current_argv, *has_equal; 368 size_t current_argv_len; 369 int i, match; 370 371 current_argv = place; 372 match = -1; 373 374 optind++; 375 place = EMSG; 376 377 if (*current_argv == '\0') { /* found "--" */ 378 /* 379 * We found an option (--), so if we skipped 380 * non-options, we have to permute. 381 */ 382 if (nonopt_end != -1) { 383 permute_args(nonopt_start, nonopt_end, 384 optind, nargv); 385 optind -= nonopt_end - nonopt_start; 386 } 387 nonopt_start = nonopt_end = -1; 388 return -1; 389 } 390 if ((has_equal = strchr(current_argv, '=')) != NULL) { 391 /* argument found (--option=arg) */ 392 current_argv_len = has_equal - current_argv; 393 has_equal++; 394 } else 395 current_argv_len = strlen(current_argv); 396 397 for (i = 0; long_options[i].name; i++) { 398 /* find matching long option */ 399 if (strncmp(current_argv, long_options[i].name, 400 current_argv_len)) 401 continue; 402 403 if (strlen(long_options[i].name) == 404 (unsigned)current_argv_len) { 405 /* exact match */ 406 match = i; 407 break; 408 } 409 if (match == -1) /* partial match */ 410 match = i; 411 else { 412 /* ambiguous abbreviation */ 413 if (PRINT_ERROR) 414 warnx(ambig, (int)current_argv_len, 415 current_argv); 416 optopt = 0; 417 return BADCH; 418 } 419 } 420 if (match != -1) { /* option found */ 421 if (long_options[match].has_arg == no_argument 422 && has_equal) { 423 if (PRINT_ERROR) 424 warnx(noarg, (int)current_argv_len, 425 current_argv); 426 /* 427 * XXX: GNU sets optopt to val regardless of 428 * flag 429 */ 430 if (long_options[match].flag == NULL) 431 optopt = long_options[match].val; 432 else 433 optopt = 0; 434 return BADARG; 435 } 436 if (long_options[match].has_arg == required_argument || 437 long_options[match].has_arg == optional_argument) { 438 if (has_equal) 439 optarg = has_equal; 440 else if (long_options[match].has_arg == 441 required_argument) { 442 /* 443 * optional argument doesn't use 444 * next nargv 445 */ 446 optarg = nargv[optind++]; 447 } 448 } 449 if ((long_options[match].has_arg == required_argument) 450 && (optarg == NULL)) { 451 /* 452 * Missing argument; leading ':' 453 * indicates no error should be generated 454 */ 455 if (PRINT_ERROR) 456 warnx(recargstring, current_argv); 457 /* 458 * XXX: GNU sets optopt to val regardless 459 * of flag 460 */ 461 if (long_options[match].flag == NULL) 462 optopt = long_options[match].val; 463 else 464 optopt = 0; 465 --optind; 466 return BADARG; 467 } 468 } else { /* unknown option */ 469 if (PRINT_ERROR) 470 warnx(illoptstring, current_argv); 471 optopt = 0; 472 return BADCH; 473 } 474 if (long_options[match].flag) { 475 *long_options[match].flag = long_options[match].val; 476 retval = 0; 477 } else 478 retval = long_options[match].val; 479 if (idx) 480 *idx = match; 481 } 482 return retval; 483 } 484