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