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