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