1 /* $OpenBSD: scanopt.c,v 1.6 2017/05/31 07:20:26 tedu Exp $ */ 2 3 /* flex - tool to generate fast lexical analyzers */ 4 5 /* Copyright (c) 1990 The Regents of the University of California. */ 6 /* All rights reserved. */ 7 8 /* This code is derived from software contributed to Berkeley by */ 9 /* Vern Paxson. */ 10 11 /* The United States Government has rights in this work pursuant */ 12 /* to contract no. DE-AC03-76SF00098 between the United States */ 13 /* Department of Energy and the University of California. */ 14 15 /* This file is part of flex. */ 16 17 /* Redistribution and use in source and binary forms, with or without */ 18 /* modification, are permitted provided that the following conditions */ 19 /* are met: */ 20 21 /* 1. Redistributions of source code must retain the above copyright */ 22 /* notice, this list of conditions and the following disclaimer. */ 23 /* 2. Redistributions in binary form must reproduce the above copyright */ 24 /* notice, this list of conditions and the following disclaimer in the */ 25 /* documentation and/or other materials provided with the distribution. */ 26 27 /* Neither the name of the University nor the names of its contributors */ 28 /* may be used to endorse or promote products derived from this software */ 29 /* without specific prior written permission. */ 30 31 /* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ 32 /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 33 /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ 34 /* PURPOSE. */ 35 36 #include "flexdef.h" 37 #include "scanopt.h" 38 39 40 /* Internal structures */ 41 42 #ifdef HAVE_STRCASECMP 43 #define STRCASECMP(a,b) strcasecmp(a,b) 44 #else 45 static int STRCASECMP PROTO ((const char *, const char *)); 46 47 static int STRCASECMP (a, b) 48 const char *a; 49 const char *b; 50 { 51 while (tolower ((u_char)*a++) == tolower ((u_char)*b++)) ; 52 return b - a; 53 } 54 #endif 55 56 #define ARG_NONE 0x01 57 #define ARG_REQ 0x02 58 #define ARG_OPT 0x04 59 #define IS_LONG 0x08 60 61 struct _aux { 62 int flags; /* The above hex flags. */ 63 int namelen; /* Length of the actual option word, e.g., "--file[=foo]" is 4 */ 64 int printlen; /* Length of entire string, e.g., "--file[=foo]" is 12 */ 65 }; 66 67 68 struct _scanopt_t { 69 const optspec_t *options; /* List of options. */ 70 struct _aux *aux; /* Auxiliary data about options. */ 71 int optc; /* Number of options. */ 72 int argc; /* Number of args. */ 73 char **argv; /* Array of strings. */ 74 int index; /* Used as: argv[index][subscript]. */ 75 int subscript; 76 char no_err_msg; /* If true, do not print errors. */ 77 char has_long; 78 char has_short; 79 }; 80 81 /* Accessor functions. These WOULD be one-liners, but portability calls. */ 82 static const char *NAME PROTO ((struct _scanopt_t *, int)); 83 static int PRINTLEN PROTO ((struct _scanopt_t *, int)); 84 static int RVAL PROTO ((struct _scanopt_t *, int)); 85 static int FLAGS PROTO ((struct _scanopt_t *, int)); 86 static const char *DESC PROTO ((struct _scanopt_t *, int)); 87 static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int)); 88 static int matchlongopt PROTO ((char *, char **, int *, char **, int *)); 89 static int find_opt 90 PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset)); 91 92 static const char *NAME (s, i) 93 struct _scanopt_t *s; 94 int i; 95 { 96 return s->options[i].opt_fmt + 97 ((s->aux[i].flags & IS_LONG) ? 2 : 1); 98 } 99 100 static int PRINTLEN (s, i) 101 struct _scanopt_t *s; 102 int i; 103 { 104 return s->aux[i].printlen; 105 } 106 107 static int RVAL (s, i) 108 struct _scanopt_t *s; 109 int i; 110 { 111 return s->options[i].r_val; 112 } 113 114 static int FLAGS (s, i) 115 struct _scanopt_t *s; 116 int i; 117 { 118 return s->aux[i].flags; 119 } 120 121 static const char *DESC (s, i) 122 struct _scanopt_t *s; 123 int i; 124 { 125 return s->options[i].desc ? s->options[i].desc : ""; 126 } 127 128 #ifndef NO_SCANOPT_USAGE 129 static int get_cols PROTO ((void)); 130 131 static int get_cols () 132 { 133 char *env; 134 int cols = 80; /* default */ 135 136 #ifdef HAVE_NCURSES_H 137 initscr (); 138 endwin (); 139 if (COLS > 0) 140 return COLS; 141 #endif 142 143 if ((env = getenv ("COLUMNS")) != NULL) 144 cols = atoi (env); 145 146 return cols; 147 } 148 #endif 149 150 /* Macro to check for NULL before assigning a value. */ 151 #define SAFE_ASSIGN(ptr,val) \ 152 do{ \ 153 if((ptr)!=NULL) \ 154 *(ptr) = val; \ 155 }while(0) 156 157 /* Macro to assure we reset subscript whenever we adjust s->index.*/ 158 #define INC_INDEX(s,n) \ 159 do{ \ 160 (s)->index += (n); \ 161 (s)->subscript= 0; \ 162 }while(0) 163 164 scanopt_t *scanopt_init (options, argc, argv, flags) 165 const optspec_t *options; 166 int argc; 167 char **argv; 168 int flags; 169 { 170 int i; 171 struct _scanopt_t *s; 172 s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t)); 173 174 s->options = options; 175 s->optc = 0; 176 s->argc = argc; 177 s->argv = (char **) argv; 178 s->index = 1; 179 s->subscript = 0; 180 s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG); 181 s->has_long = 0; 182 s->has_short = 0; 183 184 /* Determine option count. (Find entry with all zeros). */ 185 s->optc = 0; 186 while (options[s->optc].opt_fmt 187 || options[s->optc].r_val || options[s->optc].desc) 188 s->optc++; 189 190 /* Build auxiliary data */ 191 s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux)); 192 193 for (i = 0; i < s->optc; i++) { 194 const u_char *p, *pname; 195 const struct optspec_t *opt; 196 struct _aux *aux; 197 198 opt = s->options + i; 199 aux = s->aux + i; 200 201 aux->flags = ARG_NONE; 202 203 if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') { 204 aux->flags |= IS_LONG; 205 pname = (const u_char *)(opt->opt_fmt + 2); 206 s->has_long = 1; 207 } 208 else { 209 pname = (const u_char *)(opt->opt_fmt + 1); 210 s->has_short = 1; 211 } 212 aux->printlen = strlen (opt->opt_fmt); 213 214 aux->namelen = 0; 215 for (p = pname + 1; *p; p++) { 216 /* detect required arg */ 217 if (*p == '=' || isspace (*p) 218 || !(aux->flags & IS_LONG)) { 219 if (aux->namelen == 0) 220 aux->namelen = p - pname; 221 aux->flags |= ARG_REQ; 222 aux->flags &= ~ARG_NONE; 223 } 224 /* detect optional arg. This overrides required arg. */ 225 if (*p == '[') { 226 if (aux->namelen == 0) 227 aux->namelen = p - pname; 228 aux->flags &= ~(ARG_REQ | ARG_NONE); 229 aux->flags |= ARG_OPT; 230 break; 231 } 232 } 233 if (aux->namelen == 0) 234 aux->namelen = p - pname; 235 } 236 return (scanopt_t *) s; 237 } 238 239 #ifndef NO_SCANOPT_USAGE 240 /* these structs are for scanopt_usage(). */ 241 struct usg_elem { 242 int idx; 243 struct usg_elem *next; 244 struct usg_elem *alias; 245 }; 246 typedef struct usg_elem usg_elem; 247 248 249 /* Prints a usage message based on contents of optlist. 250 * Parameters: 251 * scanner - The scanner, already initialized with scanopt_init(). 252 * fp - The file stream to write to. 253 * usage - Text to be prepended to option list. 254 * Return: Always returns 0 (zero). 255 * The output looks something like this: 256 257 [indent][option, alias1, alias2...][indent][description line1 258 description line2...] 259 */ 260 int scanopt_usage (scanner, fp, usage) 261 scanopt_t *scanner; 262 FILE *fp; 263 const char *usage; 264 { 265 struct _scanopt_t *s; 266 int i, columns, indent = 2; 267 usg_elem *byr_val = NULL; /* option indices sorted by r_val */ 268 usg_elem *store; /* array of preallocated elements. */ 269 int store_idx = 0; 270 usg_elem *ue; 271 int maxlen[2]; 272 int desccol = 0; 273 int print_run = 0; 274 275 maxlen[0] = 0; 276 maxlen[1] = 0; 277 278 s = (struct _scanopt_t *) scanner; 279 280 if (usage) { 281 fprintf (fp, "%s\n", usage); 282 } 283 else { 284 /* Find the basename of argv[0] */ 285 const char *p; 286 287 p = s->argv[0] + strlen (s->argv[0]); 288 while (p != s->argv[0] && *p != '/') 289 --p; 290 if (*p == '/') 291 p++; 292 293 fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p); 294 } 295 fprintf (fp, "\n"); 296 297 /* Sort by r_val and string. Yes, this is O(n*n), but n is small. */ 298 store = (usg_elem *) malloc (s->optc * sizeof (usg_elem)); 299 for (i = 0; i < s->optc; i++) { 300 301 /* grab the next preallocate node. */ 302 ue = store + store_idx++; 303 ue->idx = i; 304 ue->next = ue->alias = NULL; 305 306 /* insert into list. */ 307 if (!byr_val) 308 byr_val = ue; 309 else { 310 int found_alias = 0; 311 usg_elem **ue_curr, **ptr_if_no_alias = NULL; 312 313 ue_curr = &byr_val; 314 while (*ue_curr) { 315 if (RVAL (s, (*ue_curr)->idx) == 316 RVAL (s, ue->idx)) { 317 /* push onto the alias list. */ 318 ue_curr = &((*ue_curr)->alias); 319 found_alias = 1; 320 break; 321 } 322 if (!ptr_if_no_alias 323 && 324 STRCASECMP (NAME (s, (*ue_curr)->idx), 325 NAME (s, ue->idx)) > 0) { 326 ptr_if_no_alias = ue_curr; 327 } 328 ue_curr = &((*ue_curr)->next); 329 } 330 if (!found_alias && ptr_if_no_alias) 331 ue_curr = ptr_if_no_alias; 332 ue->next = *ue_curr; 333 *ue_curr = ue; 334 } 335 } 336 337 #if 0 338 if (1) { 339 printf ("ORIGINAL:\n"); 340 for (i = 0; i < s->optc; i++) 341 printf ("%2d: %s\n", i, NAME (s, i)); 342 printf ("SORTED:\n"); 343 ue = byr_val; 344 while (ue) { 345 usg_elem *ue2; 346 347 printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx)); 348 for (ue2 = ue->alias; ue2; ue2 = ue2->next) 349 printf (" +---> %2d: %s\n", ue2->idx, 350 NAME (s, ue2->idx)); 351 ue = ue->next; 352 } 353 } 354 #endif 355 356 /* Now build each row of output. */ 357 358 /* first pass calculate how much room we need. */ 359 for (ue = byr_val; ue; ue = ue->next) { 360 usg_elem *ap; 361 int len = 0; 362 int nshort = 0, nlong = 0; 363 364 365 #define CALC_LEN(i) do {\ 366 if(FLAGS(s,i) & IS_LONG) \ 367 len += (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 368 else\ 369 len += (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 370 }while(0) 371 372 if (!(FLAGS (s, ue->idx) & IS_LONG)) 373 CALC_LEN (ue->idx); 374 375 /* do short aliases first. */ 376 for (ap = ue->alias; ap; ap = ap->next) { 377 if (FLAGS (s, ap->idx) & IS_LONG) 378 continue; 379 CALC_LEN (ap->idx); 380 } 381 382 if (FLAGS (s, ue->idx) & IS_LONG) 383 CALC_LEN (ue->idx); 384 385 /* repeat the above loop, this time for long aliases. */ 386 for (ap = ue->alias; ap; ap = ap->next) { 387 if (!(FLAGS (s, ap->idx) & IS_LONG)) 388 continue; 389 CALC_LEN (ap->idx); 390 } 391 392 if (len > maxlen[0]) 393 maxlen[0] = len; 394 395 /* It's much easier to calculate length for description column! */ 396 len = strlen (DESC (s, ue->idx)); 397 if (len > maxlen[1]) 398 maxlen[1] = len; 399 } 400 401 /* Determine how much room we have, and how much we will allocate to each col. 402 * Do not address pathological cases. Output will just be ugly. */ 403 columns = get_cols () - 1; 404 if (maxlen[0] + maxlen[1] + indent * 2 > columns) { 405 /* col 0 gets whatever it wants. we'll wrap the desc col. */ 406 maxlen[1] = columns - (maxlen[0] + indent * 2); 407 if (maxlen[1] < 14) /* 14 is arbitrary lower limit on desc width. */ 408 maxlen[1] = INT_MAX; 409 } 410 desccol = maxlen[0] + indent * 2; 411 412 #define PRINT_SPACES(fp,n)\ 413 do{\ 414 int _n;\ 415 _n=(n);\ 416 while(_n-- > 0)\ 417 fputc(' ',(fp));\ 418 }while(0) 419 420 421 /* Second pass (same as above loop), this time we print. */ 422 /* Sloppy hack: We iterate twice. The first time we print short and long options. 423 The second time we print those lines that have ONLY long options. */ 424 while (print_run++ < 2) { 425 for (ue = byr_val; ue; ue = ue->next) { 426 usg_elem *ap; 427 int nwords = 0, nchars = 0, has_short = 0; 428 429 /* TODO: get has_short schtick to work */ 430 has_short = !(FLAGS (s, ue->idx) & IS_LONG); 431 for (ap = ue->alias; ap; ap = ap->next) { 432 if (!(FLAGS (s, ap->idx) & IS_LONG)) { 433 has_short = 1; 434 break; 435 } 436 } 437 if ((print_run == 1 && !has_short) || 438 (print_run == 2 && has_short)) 439 continue; 440 441 PRINT_SPACES (fp, indent); 442 nchars += indent; 443 444 /* Print, adding a ", " between aliases. */ 445 #define PRINT_IT(i) do{\ 446 if(nwords++)\ 447 nchars+=fprintf(fp,", ");\ 448 nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\ 449 }while(0) 450 451 if (!(FLAGS (s, ue->idx) & IS_LONG)) 452 PRINT_IT (ue->idx); 453 454 /* print short aliases first. */ 455 for (ap = ue->alias; ap; ap = ap->next) { 456 if (!(FLAGS (s, ap->idx) & IS_LONG)) 457 PRINT_IT (ap->idx); 458 } 459 460 461 if (FLAGS (s, ue->idx) & IS_LONG) 462 PRINT_IT (ue->idx); 463 464 /* repeat the above loop, this time for long aliases. */ 465 for (ap = ue->alias; ap; ap = ap->next) { 466 if (FLAGS (s, ap->idx) & IS_LONG) 467 PRINT_IT (ap->idx); 468 } 469 470 /* pad to desccol */ 471 PRINT_SPACES (fp, desccol - nchars); 472 473 /* Print description, wrapped to maxlen[1] columns. */ 474 if (1) { 475 const char *pstart; 476 477 pstart = DESC (s, ue->idx); 478 while (1) { 479 int n = 0; 480 const char *lastws = NULL, *p; 481 482 p = pstart; 483 484 while (*p && n < maxlen[1] 485 && *p != '\n') { 486 if (isspace ((u_char)(*p)) 487 || *p == '-') lastws = 488 p; 489 n++; 490 p++; 491 } 492 493 if (!*p) { /* hit end of desc. done. */ 494 fprintf (fp, "%s\n", 495 pstart); 496 break; 497 } 498 else if (*p == '\n') { /* print everything up to here then wrap. */ 499 fprintf (fp, "%.*s\n", n, 500 pstart); 501 PRINT_SPACES (fp, desccol); 502 pstart = p + 1; 503 continue; 504 } 505 else { /* we hit the edge of the screen. wrap at space if possible. */ 506 if (lastws) { 507 fprintf (fp, 508 "%.*s\n", 509 (int)(lastws - pstart), 510 pstart); 511 pstart = 512 lastws + 1; 513 } 514 else { 515 fprintf (fp, 516 "%.*s\n", 517 n, 518 pstart); 519 pstart = p + 1; 520 } 521 PRINT_SPACES (fp, desccol); 522 continue; 523 } 524 } 525 } 526 } 527 } /* end while */ 528 free (store); 529 return 0; 530 } 531 #endif /* no scanopt_usage */ 532 533 534 static int scanopt_err (s, opt_offset, is_short, err) 535 struct _scanopt_t *s; 536 int opt_offset; 537 int is_short; 538 int err; 539 { 540 const char *optname = ""; 541 char optchar[2]; 542 const optspec_t *opt = NULL; 543 544 if (opt_offset >= 0) 545 opt = s->options + opt_offset; 546 547 if (!s->no_err_msg) { 548 549 if (s->index > 0 && s->index < s->argc) { 550 if (is_short) { 551 optchar[0] = 552 s->argv[s->index][s->subscript]; 553 optchar[1] = '\0'; 554 optname = optchar; 555 } 556 else { 557 optname = s->argv[s->index]; 558 } 559 } 560 561 fprintf (stderr, "%s: ", s->argv[0]); 562 switch (err) { 563 case SCANOPT_ERR_ARG_NOT_ALLOWED: 564 fprintf (stderr, 565 _ 566 ("option `%s' doesn't allow an argument\n"), 567 optname); 568 break; 569 case SCANOPT_ERR_ARG_NOT_FOUND: 570 fprintf (stderr, 571 _("option `%s' requires an argument\n"), 572 optname); 573 break; 574 case SCANOPT_ERR_OPT_AMBIGUOUS: 575 fprintf (stderr, _("option `%s' is ambiguous\n"), 576 optname); 577 break; 578 case SCANOPT_ERR_OPT_UNRECOGNIZED: 579 fprintf (stderr, _("Unrecognized option `%s'\n"), 580 optname); 581 break; 582 default: 583 fprintf (stderr, _("Unknown error=(%d)\n"), err); 584 break; 585 } 586 } 587 return err; 588 } 589 590 591 /* Internal. Match str against the regex ^--([^=]+)(=(.*))? 592 * return 1 if *looks* like a long option. 593 * 'str' is the only input argument, the rest of the arguments are output only. 594 * optname will point to str + 2 595 * 596 */ 597 static int matchlongopt (str, optname, optlen, arg, arglen) 598 char *str; 599 char **optname; 600 int *optlen; 601 char **arg; 602 int *arglen; 603 { 604 char *p; 605 606 *optname = *arg = (char *) 0; 607 *optlen = *arglen = 0; 608 609 /* Match regex /--./ */ 610 p = str; 611 if (p[0] != '-' || p[1] != '-' || !p[2]) 612 return 0; 613 614 p += 2; 615 *optname = (char *) p; 616 617 /* find the end of optname */ 618 while (*p && *p != '=') 619 ++p; 620 621 *optlen = p - *optname; 622 623 if (!*p) 624 /* an option with no '=...' part. */ 625 return 1; 626 627 628 /* We saw an '=' char. The rest of p is the arg. */ 629 p++; 630 *arg = p; 631 while (*p) 632 ++p; 633 *arglen = p - *arg; 634 635 return 1; 636 } 637 638 639 /* Internal. Look up long or short option by name. 640 * Long options must match a non-ambiguous prefix, or exact match. 641 * Short options must be exact. 642 * Return boolean true if found and no error. 643 * Error stored in err_code or zero if no error. */ 644 static int find_opt (s, lookup_long, optstart, len, err_code, opt_offset) 645 struct _scanopt_t *s; 646 int lookup_long; 647 char *optstart; 648 int len; 649 int *err_code; 650 int *opt_offset; 651 { 652 int nmatch = 0, lastr_val = 0, i; 653 654 *err_code = 0; 655 *opt_offset = -1; 656 657 if (!optstart) 658 return 0; 659 660 for (i = 0; i < s->optc; i++) { 661 char *optname; 662 663 optname = 664 (char *) (s->options[i].opt_fmt + 665 (lookup_long ? 2 : 1)); 666 667 if (lookup_long && (s->aux[i].flags & IS_LONG)) { 668 if (len > s->aux[i].namelen) 669 continue; 670 671 if (strncmp (optname, optstart, len) == 0) { 672 nmatch++; 673 *opt_offset = i; 674 675 /* exact match overrides all. */ 676 if (len == s->aux[i].namelen) { 677 nmatch = 1; 678 break; 679 } 680 681 /* ambiguity is ok between aliases. */ 682 if (lastr_val 683 && lastr_val == 684 s->options[i].r_val) nmatch--; 685 lastr_val = s->options[i].r_val; 686 } 687 } 688 else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) { 689 if (optname[0] == optstart[0]) { 690 nmatch++; 691 *opt_offset = i; 692 } 693 } 694 } 695 696 if (nmatch == 0) { 697 *err_code = SCANOPT_ERR_OPT_UNRECOGNIZED; 698 *opt_offset = -1; 699 } 700 else if (nmatch > 1) { 701 *err_code = SCANOPT_ERR_OPT_AMBIGUOUS; 702 *opt_offset = -1; 703 } 704 705 return *err_code ? 0 : 1; 706 } 707 708 709 int scanopt (svoid, arg, optindex) 710 scanopt_t *svoid; 711 char **arg; 712 int *optindex; 713 { 714 char *optname = NULL, *optarg = NULL, *pstart; 715 int namelen = 0, arglen = 0; 716 int errcode = 0, has_next; 717 const optspec_t *optp; 718 struct _scanopt_t *s; 719 struct _aux *auxp; 720 int is_short; 721 int opt_offset = -1; 722 723 s = (struct _scanopt_t *) svoid; 724 725 /* Normalize return-parameters. */ 726 SAFE_ASSIGN (arg, NULL); 727 SAFE_ASSIGN (optindex, s->index); 728 729 if (s->index >= s->argc) 730 return 0; 731 732 /* pstart always points to the start of our current scan. */ 733 pstart = s->argv[s->index] + s->subscript; 734 if (!pstart) 735 return 0; 736 737 if (s->subscript == 0) { 738 739 /* test for exact match of "--" */ 740 if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) { 741 SAFE_ASSIGN (optindex, s->index + 1); 742 INC_INDEX (s, 1); 743 return 0; 744 } 745 746 /* Match an opt. */ 747 if (matchlongopt 748 (pstart, &optname, &namelen, &optarg, &arglen)) { 749 750 /* it LOOKS like an opt, but is it one?! */ 751 if (!find_opt 752 (s, 1, optname, namelen, &errcode, 753 &opt_offset)) { 754 scanopt_err (s, opt_offset, 0, errcode); 755 return errcode; 756 } 757 /* We handle this below. */ 758 is_short = 0; 759 760 /* Check for short opt. */ 761 } 762 else if (pstart[0] == '-' && pstart[1]) { 763 /* Pass through to below. */ 764 is_short = 1; 765 s->subscript++; 766 pstart++; 767 } 768 769 else { 770 /* It's not an option. We're done. */ 771 return 0; 772 } 773 } 774 775 /* We have to re-check the subscript status because it 776 * may have changed above. */ 777 778 if (s->subscript != 0) { 779 780 /* we are somewhere in a run of short opts, 781 * e.g., at the 'z' in `tar -xzf` */ 782 783 optname = pstart; 784 namelen = 1; 785 is_short = 1; 786 787 if (!find_opt 788 (s, 0, pstart, namelen, &errcode, &opt_offset)) { 789 return scanopt_err (s, opt_offset, 1, errcode); 790 } 791 792 optarg = pstart + 1; 793 if (!*optarg) { 794 optarg = NULL; 795 arglen = 0; 796 } 797 else 798 arglen = strlen (optarg); 799 } 800 801 /* At this point, we have a long or short option matched at opt_offset into 802 * the s->options array (and corresponding aux array). 803 * A trailing argument is in {optarg,arglen}, if any. 804 */ 805 806 /* Look ahead in argv[] to see if there is something 807 * that we can use as an argument (if needed). */ 808 has_next = s->index + 1 < s->argc 809 && strcmp ("--", s->argv[s->index + 1]) != 0; 810 811 optp = s->options + opt_offset; 812 auxp = s->aux + opt_offset; 813 814 /* case: no args allowed */ 815 if (auxp->flags & ARG_NONE) { 816 if (optarg && !is_short) { 817 scanopt_err (s, opt_offset, is_short, errcode = 818 SCANOPT_ERR_ARG_NOT_ALLOWED); 819 INC_INDEX (s, 1); 820 return errcode; 821 } 822 else if (!optarg) 823 INC_INDEX (s, 1); 824 else 825 s->subscript++; 826 return optp->r_val; 827 } 828 829 /* case: required */ 830 if (auxp->flags & ARG_REQ) { 831 if (!optarg && !has_next) 832 return scanopt_err (s, opt_offset, is_short, 833 SCANOPT_ERR_ARG_NOT_FOUND); 834 835 if (!optarg) { 836 /* Let the next argv element become the argument. */ 837 SAFE_ASSIGN (arg, s->argv[s->index + 1]); 838 INC_INDEX (s, 2); 839 } 840 else { 841 SAFE_ASSIGN (arg, (char *) optarg); 842 INC_INDEX (s, 1); 843 } 844 return optp->r_val; 845 } 846 847 /* case: optional */ 848 if (auxp->flags & ARG_OPT) { 849 SAFE_ASSIGN (arg, optarg); 850 INC_INDEX (s, 1); 851 return optp->r_val; 852 } 853 854 855 /* Should not reach here. */ 856 return 0; 857 } 858 859 860 void scanopt_destroy (svoid) 861 scanopt_t *svoid; 862 { 863 struct _scanopt_t *s; 864 865 s = (struct _scanopt_t *) svoid; 866 if (s) { 867 free(s->aux); 868 free (s); 869 } 870 } 871