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