[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.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: */