[BACK]Return to scanopt.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / lex

Annotation of src/usr.bin/lex/scanopt.c, Revision 1.3

1.3     ! mmcc        1: /* $OpenBSD: scanopt.c,v 1.2 2015/11/19 22:16:43 tedu Exp $ */
1.1       tedu        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++) {
1.3     ! mmcc      194:                const u_char *p, *pname;
1.1       tedu      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;
1.3     ! mmcc      205:                        pname = (const u_char *)(opt->opt_fmt + 2);
1.1       tedu      206:                        s->has_long = 1;
                    207:                }
                    208:                else {
1.3     ! mmcc      209:                        pname = (const u_char *)(opt->opt_fmt + 1);
1.1       tedu      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') {
1.3     ! mmcc      486:                                                if (isspace ((u_char)(*p))
1.1       tedu      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: }