Annotation of src/usr.bin/lex/scanopt.c, Revision 1.1
1.1 ! tedu 1: /* $OpenBSD$ */
! 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 (*a++) == tolower (*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 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 Char *)(opt->opt_fmt + 2);
! 206: s->has_long = 1;
! 207: }
! 208: else {
! 209: pname = (const 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 ((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: int scanopt_destroy (svoid)
! 861: scanopt_t *svoid;
! 862: {
! 863: struct _scanopt_t *s;
! 864:
! 865: s = (struct _scanopt_t *) svoid;
! 866: if (s) {
! 867: if (s->aux)
! 868: free (s->aux);
! 869: free (s);
! 870: }
! 871: return 0;
! 872: }
! 873:
! 874:
! 875: /* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */