Reactos
at master 486 lines 16 kB view raw
1//////////////////////////////////////////////////////////////////// 2// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine 3// All rights reserved 4// This file was released under the GPLv2 on June 2015. 5//////////////////////////////////////////////////////////////////// 6 7#include "getopt.h" 8 9 10#ifdef __cplusplus 11extern "C" { 12#endif 13 14/* Describe how to deal with options that follow non-option ARGV-elements. 15 16 If the caller did not specify anything, 17 the default is REQUIRE_ORDER if the environment variable 18 POSIXLY_CORRECT is defined, PERMUTE otherwise. 19 20 REQUIRE_ORDER means don't recognize them as options; 21 stop option processing when the first non-option is seen. 22 This is what Unix does. 23 This mode of operation is selected by either setting the environment 24 variable POSIXLY_CORRECT, or using `+' as the first character 25 of the list of option characters. 26 27 PERMUTE is the default. We permute the contents of ARGV as we scan, 28 so that eventually all the non-options are at the end. This allows options 29 to be given in any order, even with programs that were not written to 30 expect this. 31 32 RETURN_IN_ORDER is an option available to programs that were written 33 to expect options and other ARGV-elements in any order and that care about 34 the ordering of the two. We describe each non-option ARGV-element 35 as if it were the argument of an option with character code 1. 36 Using `-' as the first character of the list of option characters 37 selects this mode of operation. 38 39 The special argument `--' forces an end of option-scanning regardless 40 of the value of `ordering'. In the case of RETURN_IN_ORDER, only 41 `--' can cause `getopt' to return EOF with `optind' != ARGC. */ 42 43static enum 44{ 45 REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER 46} ordering; 47 48#define my_index wcschr 49 50#define my_strtoul wcstoul 51#define my_strlen wcslen 52#define my_strncmp wcsncmp 53#define my_strcpy wcscpy 54#define my_strcat wcscat 55#define my_strcmp wcscmp 56 57/* Handle permutation of arguments. */ 58 59 60void 61getopt_init(optarg_ctx* o) { 62 63 o->optarg = NULL; 64 o->optind = 0; 65 o->optopt = BAD_OPTION; 66 o->opterr = 1; 67} 68 69static void 70exchange ( 71 optarg_ctx* o, 72 WCHAR **argv 73 ) 74{ 75 WCHAR *temp, **first, **last; 76 77 /* Reverse all the elements [first_nonopt, optind) */ 78 first = &argv[o->first_nonopt]; 79 last = &argv[o->optind-1]; 80 while (first < last) { 81 temp = *first; *first = *last; *last = temp; first++; last--; 82 } 83 /* Put back the options in order */ 84 first = &argv[o->first_nonopt]; 85 o->first_nonopt += (o->optind - o->last_nonopt); 86 last = &argv[o->first_nonopt - 1]; 87 while (first < last) { 88 temp = *first; *first = *last; *last = temp; first++; last--; 89 } 90 91 /* Put back the non options in order */ 92 first = &argv[o->first_nonopt]; 93 o->last_nonopt = o->optind; 94 last = &argv[o->last_nonopt-1]; 95 while (first < last) { 96 temp = *first; *first = *last; *last = temp; first++; last--; 97 } 98} 99 100/* Scan elements of ARGV (whose length is ARGC) for option characters 101 given in OPTSTRING. 102 103 If an element of ARGV starts with '-', and is not exactly "-" or "--", 104 then it is an option element. The characters of this element 105 (aside from the initial '-') are option characters. If `getopt' 106 is called repeatedly, it returns successively each of the option characters 107 from each of the option elements. 108 109 If `getopt' finds another option character, it returns that character, 110 updating `optind' and `nextchar' so that the next call to `getopt' can 111 resume the scan with the following option character or ARGV-element. 112 113 If there are no more option characters, `getopt' returns `EOF'. 114 Then `optind' is the index in ARGV of the first ARGV-element 115 that is not an option. (The ARGV-elements have been permuted 116 so that those that are not options now come last.) 117 118 OPTSTRING is a string containing the legitimate option characters. 119 If an option character is seen that is not listed in OPTSTRING, 120 return BAD_OPTION after printing an error message. If you set `opterr' to 121 zero, the error message is suppressed but we still return BAD_OPTION. 122 123 If a char in OPTSTRING is followed by a colon, that means it wants an arg, 124 so the following text in the same ARGV-element, or the text of the following 125 ARGV-element, is returned in `optarg'. Two colons mean an option that 126 wants an optional arg; if there is text in the current ARGV-element, 127 it is returned in `optarg', otherwise `optarg' is set to zero. 128 129 If OPTSTRING starts with `-' or `+', it requests different methods of 130 handling the non-option ARGV-elements. 131 See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. 132 133 Long-named options begin with `--' instead of `-'. 134 Their names may be abbreviated as long as the abbreviation is unique 135 or is an exact match for some defined option. If they have an 136 argument, it follows the option name in the same ARGV-element, separated 137 from the option name by a `=', or else the in next ARGV-element. 138 When `getopt' finds a long-named option, it returns 0 if that option's 139 `flag' field is nonzero, the value of the option's `val' field 140 if the `flag' field is zero. 141 142 The elements of ARGV aren't really const, because we permute them. 143 But we pretend they're const in the prototype to be compatible 144 with other systems. 145 146 LONGOPTS is a vector of `struct option' terminated by an 147 element containing a name which is zero. 148 149 LONGIND returns the index in LONGOPT of the long-named option found. 150 It is only valid when a long-named option has been found by the most 151 recent call. 152 153 If LONG_ONLY is nonzero, '-' as well as '--' can introduce 154 long-named options. */ 155 156int 157_getopt_internal( 158 optarg_ctx* o, 159 int argc, 160 WCHAR *const *argv, 161 const WCHAR *optstring, 162 const struct option *longopts, 163 int *longind, 164 int long_only) 165{ 166 int option_index; 167 168 o->optarg = 0; 169 170 /* Initialize the internal data when the first call is made. 171 Start processing options with ARGV-element 1 (since ARGV-element 0 172 is the program name); the sequence of previously skipped 173 non-option ARGV-elements is empty. */ 174 175 if (o->optind == 0) 176 { 177 o->first_nonopt = o->last_nonopt = o->optind = 1; 178 179 o->nextchar = NULL; 180 181 /* Determine how to handle the ordering of options and nonoptions. */ 182 183 if (optstring[0] == '-') { 184 ordering = RETURN_IN_ORDER; 185 ++optstring; 186 } else if (optstring[0] == '+') { 187 ordering = REQUIRE_ORDER; 188 ++optstring; 189/* } else if (getenv ("POSIXLY_CORRECT") != NULL) { 190 ordering = REQUIRE_ORDER;*/ 191 } else { 192 ordering = PERMUTE; 193 } 194 } 195 196 if (o->nextchar == NULL || *(o->nextchar) == '\0') 197 { 198 if (ordering == PERMUTE) 199 { 200 /* If we have just processed some options following some non-options, 201 exchange them so that the options come first. */ 202 203 if (o->first_nonopt != o->last_nonopt && o->last_nonopt != o->optind) { 204 exchange (o, (WCHAR **) argv); 205 } else if (o->last_nonopt != o->optind) { 206 o->first_nonopt = o->optind; 207 } 208 209 /* Now skip any additional non-options 210 and extend the range of non-options previously skipped. */ 211 212 while (o->optind < argc 213 && (argv[o->optind][0] != '-' || argv[o->optind][1] == '\0') 214 ) { 215 o->optind++; 216 } 217 o->last_nonopt = o->optind; 218 } 219 220 /* Special ARGV-element `--' means premature end of options. 221 Skip it like a null option, 222 then exchange with previous non-options as if it were an option, 223 then skip everything else like a non-option. */ 224 225 if (o->optind != argc && !my_strcmp (argv[o->optind], L"--")) 226 { 227 o->optind++; 228 229 if (o->first_nonopt != o->last_nonopt && o->last_nonopt != o->optind) { 230 exchange (o, (WCHAR **) argv); 231 } else if (o->first_nonopt == o->last_nonopt) { 232 o->first_nonopt = o->optind; 233 } 234 o->last_nonopt = argc; 235 236 o->optind = argc; 237 } 238 239 /* If we have done all the ARGV-elements, stop the scan 240 and back over any non-options that we skipped and permuted. */ 241 242 if (o->optind == argc) 243 { 244 /* Set the next-arg-index to point at the non-options 245 that we previously skipped, so the caller will digest them. */ 246 if (o->first_nonopt != o->last_nonopt) 247 o->optind = o->first_nonopt; 248 return EOF; 249 } 250 251 /* If we have come to a non-option and did not permute it, 252 either stop the scan or describe it to the caller and pass it by. */ 253 254 if ((argv[o->optind][0] != '-' || argv[o->optind][1] == '\0')) 255 { 256 if (ordering == REQUIRE_ORDER) 257 return EOF; 258 o->optarg = argv[o->optind++]; 259 return 1; 260 } 261 262 /* We have found another option-ARGV-element. 263 Start decoding its characters. */ 264 o->nextchar = (argv[o->optind] + 1 265 + (longopts != NULL && argv[o->optind][1] == '-')); 266 } 267 268 if (longopts != NULL 269 && ((argv[o->optind][0] == '-' 270 && (argv[o->optind][1] == '-' || long_only)) 271 )) 272 { 273 const struct option *p; 274 WCHAR *s = o->nextchar; 275 int exact = 0; 276 int ambig = 0; 277 const struct option *pfound = NULL; 278 int indfound = 0; 279 280 while (*s && *s != '=') 281 s++; 282 283 /* Test all options for either exact match or abbreviated matches. */ 284 for (p = longopts, option_index = 0; 285 p->name; 286 p++, option_index++) 287 if ( (p->val) && (!my_strncmp (p->name, o->nextchar, s - o->nextchar)) ) 288 { 289 if (s - o->nextchar == (int)my_strlen (p->name)) 290 { 291 /* Exact match found. */ 292 pfound = p; 293 indfound = option_index; 294 exact = 1; 295 break; 296 } else if (pfound == NULL) { 297 /* First nonexact match found. */ 298 pfound = p; 299 indfound = option_index; 300 } else { 301 /* Second nonexact match found. */ 302 ambig = 1; 303 } 304 } 305 306 if (ambig && !exact) { 307 if (o->opterr) { 308 UDFPrint(("%ws: option `%s' is ambiguous\n", 309 argv[0], argv[o->optind])); 310 } 311 o->nextchar += my_strlen (o->nextchar); 312 o->optind++; 313 return BAD_OPTION; 314 } 315 316 if (pfound != NULL) 317 { 318 option_index = indfound; 319 o->optind++; 320 if (*s) { 321 /* Don't test has_arg with >, because some C compilers don't 322 allow it to be used on enums. */ 323 if (pfound->has_arg) { 324 o->optarg = s + 1; 325 } else { 326 if (o->opterr) { 327 if (argv[o->optind - 1][1] == '-') { 328 /* --option */ 329 UDFPrint(( 330 "%ws: option `--%ws' doesn't allow an argument\n", 331 argv[0], pfound->name)); 332 } else { 333 /* +option or -option */ 334 UDFPrint(( 335 "%ws: option `%c%ws' doesn't allow an argument\n", 336 argv[0], argv[o->optind - 1][0], pfound->name)); 337 } 338 } 339 o->nextchar += my_strlen (o->nextchar); 340 return BAD_OPTION; 341 } 342 } 343 else if (pfound->has_arg == 1) 344 { 345 if (o->optind < argc) { 346 o->optarg = argv[(o->optind)++]; 347 } else { 348 if (o->opterr) 349 UDFPrint(("%ws: option `%ws' requires an argument\n", 350 argv[0], argv[o->optind - 1])); 351 o->nextchar += my_strlen (o->nextchar); 352 return optstring[0] == ':' ? ':' : BAD_OPTION; 353 } 354 } 355 o->nextchar += my_strlen (o->nextchar); 356 if (longind != NULL) 357 *longind = option_index; 358 if (pfound->flag) { 359 *(pfound->flag) = pfound->val; 360 return 0; 361 } 362 return pfound->val; 363 } 364 /* Can't find it as a long option. If this is not getopt_long_only, 365 or the option starts with '--' or is not a valid short 366 option, then it's an error. 367 Otherwise interpret it as a short option. */ 368 if (!long_only || argv[o->optind][1] == '-' 369 || my_index (optstring, *(o->nextchar)) == NULL) 370 { 371 if (o->opterr) 372 { 373 if (argv[o->optind][1] == '-') { 374 /* --option */ 375 UDFPrint(("%ws: unrecognized option `--%ws'\n", 376 argv[0], o->nextchar)); 377 } else { 378 /* +option or -option */ 379 UDFPrint(("%ws: unrecognized option `%c%ws'\n", 380 argv[0], argv[o->optind][0], o->nextchar)); 381 } 382 } 383 o->nextchar = (WCHAR *) L""; 384 o->optind++; 385 return BAD_OPTION; 386 } 387 } 388 389 /* Look at and handle the next option-character. */ 390 391 { 392 WCHAR c = *(o->nextchar)++; 393 WCHAR *temp = my_index (optstring, c); 394 395 /* Increment `optind' when we start to process its last character. */ 396 if (*(o->nextchar) == '\0') 397 ++(o->optind); 398 399 if (temp == NULL || c == ':') 400 { 401 if (o->opterr) 402 { 403 UDFPrint(("%ws: illegal option -- %c\n", argv[0], c)); 404 } 405 o->optopt = c; 406 return BAD_OPTION; 407 } 408 if (temp[1] == ':') 409 { 410 if (temp[2] == ':') 411 { 412 /* This is an option that accepts an argument optionally. */ 413 if (*(o->nextchar) != '\0') { 414 o->optarg = o->nextchar; 415 o->optind++; 416 } else { 417 o->optarg = 0; 418 } 419 o->nextchar = NULL; 420 } 421 else 422 { 423 /* This is an option that requires an argument. */ 424 if (*(o->nextchar) != '\0') 425 { 426 o->optarg = o->nextchar; 427 /* If we end this ARGV-element by taking the rest as an arg, 428 we must advance to the next element now. */ 429 o->optind++; 430 } 431 else if (o->optind == argc) 432 { 433 if (o->opterr) 434 { 435 UDFPrint(("%ws: option requires an argument -- %c\n", 436 argv[0], c)); 437 } 438 o->optopt = c; 439 if (optstring[0] == ':') { 440 c = ':'; 441 } else { 442 c = BAD_OPTION; 443 } 444 } 445 else 446 { 447 /* We already incremented `optind' once; 448 increment it again when taking next ARGV-elt as argument. */ 449 o->optarg = argv[o->optind++]; 450 } 451 o->nextchar = NULL; 452 } 453 } 454 return c; 455 } 456} 457 458int 459getopt ( 460 optarg_ctx* o, 461 int argc, 462 WCHAR *const *argv, 463 const WCHAR *optstring) 464{ 465 return _getopt_internal (o, argc, argv, optstring, 466 (const struct option *) 0, 467 (int *) 0, 468 0); 469} 470 471int 472getopt_long ( 473 optarg_ctx* o, 474 int argc, 475 WCHAR *const *argv, 476 const WCHAR *options, 477 const struct option *long_options, 478 int *opt_index) 479{ 480 return _getopt_internal (o, argc, argv, options, long_options, opt_index, 0); 481} 482 483 484#ifdef __cplusplus 485} 486#endif