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