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

Annotation of src/usr.bin/make/varmodifiers.c, Revision 1.7

1.7     ! espie       1: /*     $OpenPackages$ */
        !             2: /*     $OpenBSD: var.c,v 1.12 1999/09/28 21:57:04 espie Exp $  */
1.1       espie       3: /*     $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $        */
                      4:
                      5: /*
                      6:  * Copyright (c) 1999 Marc Espie.
                      7:  *
                      8:  * Extensive code changes for the OpenBSD project.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     21:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     22:  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
                     23:  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     24:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     25:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     26:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     27:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     28:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
                     29:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31: /*
                     32:  * Copyright (c) 1988, 1989, 1990, 1993
                     33:  *     The Regents of the University of California.  All rights reserved.
                     34:  * Copyright (c) 1989 by Berkeley Softworks
                     35:  * All rights reserved.
                     36:  *
                     37:  * This code is derived from software contributed to Berkeley by
                     38:  * Adam de Boor.
                     39:  *
                     40:  * Redistribution and use in source and binary forms, with or without
                     41:  * modification, are permitted provided that the following conditions
                     42:  * are met:
                     43:  * 1. Redistributions of source code must retain the above copyright
                     44:  *    notice, this list of conditions and the following disclaimer.
                     45:  * 2. Redistributions in binary form must reproduce the above copyright
                     46:  *    notice, this list of conditions and the following disclaimer in the
                     47:  *    documentation and/or other materials provided with the distribution.
                     48:  * 3. All advertising materials mentioning features or use of this software
                     49:  *    must display the following acknowledgement:
                     50:  *     This product includes software developed by the University of
                     51:  *     California, Berkeley and its contributors.
                     52:  * 4. Neither the name of the University nor the names of its contributors
                     53:  *    may be used to endorse or promote products derived from this software
                     54:  *    without specific prior written permission.
                     55:  *
                     56:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     57:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     58:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     59:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     60:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     61:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     62:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     63:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     64:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     65:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     66:  * SUCH DAMAGE.
                     67:  */
                     68:
1.7     ! espie      69: /* VarModifiers_Apply is mostly a constituent function of Var_Parse, it
        !            70:  * is also called directly by Var_SubstVar.  */
        !            71:
        !            72:
1.1       espie      73: #include    <ctype.h>
                     74: #ifndef MAKE_BOOTSTRAP
                     75: #include    <sys/types.h>
                     76: #include    <regex.h>
                     77: #endif
                     78: #include "make.h"
                     79: #include "buf.h"
                     80: #include "varmodifiers.h"
                     81:
                     82: /* Var*Pattern flags */
                     83: #define VAR_SUB_GLOBAL 0x01    /* Apply substitution globally */
                     84: #define VAR_SUB_ONE    0x02    /* Apply substitution to one word */
1.7     ! espie      85: #define VAR_SUB_MATCHED 0x04   /* There was a match */
        !            86: #define VAR_MATCH_START 0x08   /* Match at start of word */
1.1       espie      87: #define VAR_MATCH_END  0x10    /* Match at end of word */
                     88:
1.7     ! espie      89: /* Modifiers flags */
        !            90: #define VAR_EQUAL      0x20
        !            91: #define VAR_MAY_EQUAL  0x40
        !            92: #define VAR_ADD_EQUAL  0x80
        !            93: #define VAR_BANG_EQUAL 0x100
        !            94:
1.1       espie      95: typedef struct {
1.7     ! espie      96:     char         *lbuffer; /* left string to free */
        !            97:     char         *lhs;     /* String to match */
        !            98:     size_t       leftLen;  /* Length of string */
        !            99:     char         *rhs;     /* Replacement string (w/ &'s removed) */
        !           100:     size_t       rightLen; /* Length of replacement */
        !           101:     int          flags;
1.1       espie     102: } VarPattern;
                    103:
1.7     ! espie     104: struct LoopStuff {
        !           105:     char       *var;
        !           106:     char       *expand;
        !           107:     SymTable   *ctxt;
        !           108:     Boolean    err;
        !           109: };
        !           110:
        !           111: static Boolean VarHead(struct Name *, Boolean, Buffer, void *);
        !           112: static Boolean VarTail(struct Name *, Boolean, Buffer, void *);
        !           113: static Boolean VarSuffix(struct Name *, Boolean, Buffer, void *);
        !           114: static Boolean VarRoot(struct Name *, Boolean, Buffer, void *);
        !           115: static Boolean VarMatch(struct Name *, Boolean, Buffer, void *);
        !           116: static Boolean VarSYSVMatch(struct Name *, Boolean, Buffer, void *);
        !           117: static Boolean VarNoMatch(struct Name *, Boolean, Buffer, void *);
        !           118: static Boolean VarUniq(struct Name *, Boolean, Buffer, void *);
        !           119: static Boolean VarLoop(struct Name *, Boolean, Buffer, void *);
        !           120:
        !           121:
1.1       espie     122: #ifndef MAKE_BOOTSTRAP
1.7     ! espie     123: static void VarREError(int, regex_t *, const char *);
        !           124: static Boolean VarRESubstitute(struct Name *, Boolean, Buffer, void *);
        !           125: static char *do_regex(const char *, const struct Name *, void *);
        !           126:
1.1       espie     127: typedef struct {
                    128:     regex_t      re;
1.7     ! espie     129:     int          nsub;
1.1       espie     130:     regmatch_t  *matches;
                    131:     char        *replace;
1.7     ! espie     132:     int          flags;
1.1       espie     133: } VarREPattern;
                    134: #endif
                    135:
1.7     ! espie     136: static Boolean VarSubstitute(struct Name *, Boolean, Buffer, void *);
        !           137: static char *VarGetPattern(SymTable *, int, const char **, int, int,
        !           138:     size_t *, VarPattern *);
        !           139: static char *VarQuote(const char *, const struct Name *, void *);
        !           140: static char *VarModify(char *, Boolean (*)(struct Name *, Boolean, Buffer, void *), void *);
        !           141:
        !           142: static void *check_empty(const char **, SymTable *, Boolean, int);
        !           143: static char *do_upper(const char *, const struct Name *, void *);
        !           144: static char *do_lower(const char *, const struct Name *, void *);
        !           145: static void *check_shcmd(const char **, SymTable *, Boolean, int);
        !           146: static char *do_shcmd(const char *, const struct Name *, void *);
        !           147: static char *do_sort(const char *, const struct Name *, void *);
        !           148: static char *finish_loop(const char *, const struct Name *, void *);
        !           149: static int NameCompare(const void *, const void *);
        !           150: static char *do_label(const char *, const struct Name *, void *);
        !           151: static char *do_path(const char *, const struct Name *, void *);
        !           152: static char *do_def(const char *, const struct Name *, void *);
        !           153: static char *do_undef(const char *, const struct Name *, void *);
        !           154: static char *do_assign(const char *, const struct Name *, void *);
        !           155: static char *do_exec(const char *, const struct Name *, void *);
        !           156:
        !           157: static void *assign_get_value(const char **, SymTable *, Boolean, int);
        !           158: static void *get_cmd(const char **, SymTable *, Boolean, int);
        !           159: static void *get_value(const char **, SymTable *, Boolean, int);
        !           160: static void *get_stringarg(const char **, SymTable *, Boolean, int);
        !           161: static void free_stringarg(void *);
        !           162: static void *get_patternarg(const char **, SymTable *, Boolean, int);
        !           163: static void *get_spatternarg(const char **, SymTable *, Boolean, int);
        !           164: static void free_patternarg(void *);
        !           165: static void free_looparg(void *);
        !           166: static void *get_sysvpattern(const char **, SymTable *, Boolean, int);
        !           167: static void *get_loop(const char **, SymTable *, Boolean, int);
        !           168: static char *LoopGrab(const char **);
        !           169:
        !           170: static struct Name dummy;
        !           171: static struct Name *dummy_arg = &dummy;
        !           172:
        !           173: static struct modifier {
        !           174:        Boolean atstart;
        !           175:        void * (*getarg)(const char **, SymTable *, Boolean, int);
        !           176:        char * (*apply)(const char *, const struct Name *, void *);
        !           177:        Boolean (*word_apply)(struct Name *, Boolean, Buffer, void *);
        !           178:        void   (*freearg)(void *);
        !           179: } *choose_mod[256],
        !           180:     match_mod = {FALSE, get_stringarg, NULL, VarMatch, free_stringarg},
        !           181:     nomatch_mod = {FALSE, get_stringarg, NULL, VarNoMatch, free_stringarg},
        !           182:     subst_mod = {FALSE, get_spatternarg, NULL, VarSubstitute, free_patternarg},
1.1       espie     183: #ifndef MAKE_BOOTSTRAP
1.7     ! espie     184:     resubst_mod = {FALSE, get_patternarg, do_regex, NULL, free_patternarg},
1.1       espie     185: #endif
1.7     ! espie     186:     quote_mod = {FALSE, check_empty, VarQuote, NULL , NULL},
        !           187:     tail_mod = {FALSE, check_empty, NULL, VarTail, NULL},
        !           188:     head_mod = {FALSE, check_empty, NULL, VarHead, NULL},
        !           189:     suffix_mod = {FALSE, check_empty, NULL, VarSuffix, NULL},
        !           190:     root_mod = {FALSE, check_empty, NULL, VarRoot, NULL},
        !           191:     upper_mod = {FALSE, check_empty, do_upper, NULL, NULL},
        !           192:     lower_mod = {FALSE, check_empty, do_lower, NULL, NULL},
        !           193:     shcmd_mod = {FALSE, check_shcmd, do_shcmd, NULL, NULL},
        !           194:     sysv_mod = {FALSE, get_sysvpattern, NULL, VarSYSVMatch, free_patternarg},
        !           195:     uniq_mod = {FALSE, check_empty, NULL, VarUniq, NULL},
        !           196:     sort_mod = {FALSE, check_empty, do_sort, NULL, NULL},
        !           197:     loop_mod = {FALSE, get_loop, finish_loop, VarLoop, free_looparg},
        !           198:     undef_mod = {TRUE, get_value, do_undef, NULL, NULL},
        !           199:     def_mod = {TRUE, get_value, do_def, NULL, NULL},
        !           200:     label_mod = {TRUE, check_empty, do_label, NULL, NULL},
        !           201:     path_mod = {TRUE, check_empty, do_path, NULL, NULL},
        !           202:     assign_mod = {TRUE, assign_get_value, do_assign, NULL, free_patternarg},
        !           203:     exec_mod = {TRUE, get_cmd, do_exec, NULL, free_patternarg}
        !           204: ;
1.1       espie     205:
1.7     ! espie     206: void
        !           207: VarModifiers_Init()
1.1       espie     208: {
1.7     ! espie     209:     choose_mod['M'] = &match_mod;
        !           210:     choose_mod['N'] = &nomatch_mod;
        !           211:     choose_mod['S'] = &subst_mod;
        !           212: #ifndef MAKE_BOOTSTRAP
        !           213:     choose_mod['C'] = &resubst_mod;
        !           214: #endif
        !           215:     choose_mod['Q'] = &quote_mod;
        !           216:     choose_mod['T'] = &tail_mod;
        !           217:     choose_mod['H'] = &head_mod;
        !           218:     choose_mod['E'] = &suffix_mod;
        !           219:     choose_mod['R'] = &root_mod;
        !           220:     if (FEATURES(FEATURE_UPPERLOWER)) {
        !           221:        choose_mod['U'] = &upper_mod;
        !           222:        choose_mod['L'] = &lower_mod;
        !           223:     }
        !           224:     if (FEATURES(FEATURE_SUNSHCMD))
        !           225:        choose_mod['s'] = &shcmd_mod;
        !           226:     if (FEATURES(FEATURE_UNIQ))
        !           227:        choose_mod['u'] = &uniq_mod;
        !           228:     if (FEATURES(FEATURE_SORT))
        !           229:        choose_mod['O'] = &sort_mod;
        !           230:     if (FEATURES(FEATURE_ODE)) {
        !           231:        choose_mod['@'] = &loop_mod;
        !           232:        choose_mod['D'] = &def_mod;
        !           233:        choose_mod['U'] = &undef_mod;
        !           234:        choose_mod['L'] = &label_mod;
        !           235:        choose_mod['P'] = &path_mod;
        !           236:     }
        !           237:     if (FEATURES(FEATURE_ASSIGN))
        !           238:        choose_mod[':'] = &assign_mod;
        !           239:     if (FEATURES(FEATURE_EXECMOD))
        !           240:        choose_mod['!'] = &exec_mod;
1.1       espie     241: }
                    242:
1.7     ! espie     243: /* All modifiers handle addSpace (need to add a space before placing the
        !           244:  * next word into the buffer) and propagate it when necessary.
1.1       espie     245:  */
                    246:
                    247: /*-
                    248:  *-----------------------------------------------------------------------
                    249:  * VarHead --
1.7     ! espie     250:  *     Remove the tail of the given word and add the result to the given
1.1       espie     251:  *     buffer.
                    252:  *-----------------------------------------------------------------------
                    253:  */
                    254: static Boolean
                    255: VarHead(word, addSpace, buf, dummy)
1.7     ! espie     256:     struct Name *word;
        !           257:     Boolean    addSpace;
        !           258:     Buffer     buf;
1.6       espie     259:     void       *dummy          UNUSED;
1.1       espie     260: {
1.6       espie     261:     const char *slash;
1.1       espie     262:
1.7     ! espie     263:     slash = lastchar(word->s, word->e, '/');
1.1       espie     264:     if (slash != NULL) {
                    265:        if (addSpace)
                    266:            Buf_AddSpace(buf);
1.7     ! espie     267:        Buf_AddInterval(buf, word->s, slash);
1.1       espie     268:     } else {
1.7     ! espie     269:        /* If no directory part, give . (q.v. the POSIX standard).  */
1.1       espie     270:        if (addSpace)
                    271:            Buf_AddString(buf, " .");
                    272:        else
                    273:            Buf_AddChar(buf, '.');
                    274:     }
1.6       espie     275:     return TRUE;
1.1       espie     276: }
                    277:
                    278: /*-
                    279:  *-----------------------------------------------------------------------
                    280:  * VarTail --
1.7     ! espie     281:  *     Remove the head of the given word add the result to the given
1.1       espie     282:  *     buffer.
                    283:  *-----------------------------------------------------------------------
                    284:  */
                    285: static Boolean
                    286: VarTail(word, addSpace, buf, dummy)
1.7     ! espie     287:     struct Name *word;
        !           288:     Boolean    addSpace;
        !           289:     Buffer     buf;
1.6       espie     290:     void       *dummy          UNUSED;
1.1       espie     291: {
1.6       espie     292:     const char *slash;
1.1       espie     293:
1.7     ! espie     294:     if (addSpace)
1.1       espie     295:        Buf_AddSpace(buf);
1.7     ! espie     296:     slash = lastchar(word->s, word->e, '/');
1.1       espie     297:     if (slash != NULL)
1.7     ! espie     298:        Buf_AddInterval(buf, slash+1, word->e);
1.1       espie     299:     else
1.7     ! espie     300:        Buf_AddInterval(buf, word->s, word->e);
1.6       espie     301:     return TRUE;
1.1       espie     302: }
                    303:
                    304: /*-
                    305:  *-----------------------------------------------------------------------
                    306:  * VarSuffix --
1.7     ! espie     307:  *     Add the suffix of the given word to the given buffer.
1.1       espie     308:  *-----------------------------------------------------------------------
                    309:  */
                    310: static Boolean
                    311: VarSuffix(word, addSpace, buf, dummy)
1.7     ! espie     312:     struct Name *word;
        !           313:     Boolean    addSpace;
        !           314:     Buffer     buf;
1.6       espie     315:     void       *dummy          UNUSED;
1.1       espie     316: {
1.6       espie     317:     const char *dot;
1.1       espie     318:
1.7     ! espie     319:     dot = lastchar(word->s, word->e, '.');
1.1       espie     320:     if (dot != NULL) {
                    321:        if (addSpace)
                    322:            Buf_AddSpace(buf);
1.7     ! espie     323:        Buf_AddInterval(buf, dot+1, word->e);
1.1       espie     324:        addSpace = TRUE;
                    325:     }
1.6       espie     326:     return addSpace;
1.1       espie     327: }
                    328:
                    329: /*-
                    330:  *-----------------------------------------------------------------------
                    331:  * VarRoot --
1.7     ! espie     332:  *     Remove the suffix of the given word and add the result to the
1.1       espie     333:  *     buffer.
                    334:  *-----------------------------------------------------------------------
                    335:  */
                    336: static Boolean
                    337: VarRoot(word, addSpace, buf, dummy)
1.7     ! espie     338:     struct Name *word;
        !           339:     Boolean    addSpace;
        !           340:     Buffer     buf;
1.6       espie     341:     void       *dummy          UNUSED;
1.1       espie     342: {
1.6       espie     343:     const char *dot;
1.1       espie     344:
                    345:     if (addSpace)
                    346:        Buf_AddSpace(buf);
1.7     ! espie     347:     dot = lastchar(word->s, word->e, '.');
1.1       espie     348:     if (dot != NULL)
1.7     ! espie     349:        Buf_AddInterval(buf, word->s, dot);
1.1       espie     350:     else
1.7     ! espie     351:        Buf_AddInterval(buf, word->s, word->e);
1.6       espie     352:     return TRUE;
1.1       espie     353: }
                    354:
                    355: /*-
                    356:  *-----------------------------------------------------------------------
                    357:  * VarMatch --
1.7     ! espie     358:  *     Add the word to the buffer if it matches the given pattern.
1.1       espie     359:  *-----------------------------------------------------------------------
                    360:  */
                    361: static Boolean
                    362: VarMatch(word, addSpace, buf, pattern)
1.7     ! espie     363:     struct Name *word;
        !           364:     Boolean    addSpace;
        !           365:     Buffer     buf;
        !           366:     void       *pattern;       /* Pattern the word must match */
        !           367: {
        !           368:     if (Str_Match(word->s, (const char *)pattern)) {
        !           369:        if (addSpace)
        !           370:            Buf_AddSpace(buf);
        !           371:        Buf_AddInterval(buf, word->s, word->e);
        !           372:        return TRUE;
        !           373:     } else
        !           374:        return addSpace;
        !           375: }
        !           376:
        !           377: /*-
        !           378:  *-----------------------------------------------------------------------
        !           379:  * VarNoMatch --
        !           380:  *     Add the word to the buffer if it doesn't match the given pattern.
        !           381:  *-----------------------------------------------------------------------
        !           382:  */
        !           383: static Boolean
        !           384: VarNoMatch(word, addSpace, buf, pattern)
        !           385:     struct Name *word;
        !           386:     Boolean    addSpace;
        !           387:     Buffer     buf;
        !           388:     void       *pattern;       /* Pattern the word must not match */
        !           389: {
        !           390:     if (!Str_Match(word->s, (const char *)pattern)) {
        !           391:        if (addSpace)
        !           392:            Buf_AddSpace(buf);
        !           393:        Buf_AddInterval(buf, word->s, word->e);
        !           394:        return TRUE;
        !           395:     } else
        !           396:        return addSpace;
        !           397: }
        !           398:
        !           399: static Boolean
        !           400: VarUniq(word, addSpace, buf, lastp)
        !           401:     struct Name        *word;
        !           402:     Boolean    addSpace;
        !           403:     Buffer     buf;
        !           404:     void       *lastp;
1.1       espie     405: {
1.7     ! espie     406:     struct Name *last = (struct Name *)lastp;
        !           407:
        !           408:        /* does not match */
        !           409:     if (last->s == NULL || last->e - last->s != word->e - word->s ||
        !           410:        strncmp(word->s, last->s, word->e - word->s) != 0) {
1.1       espie     411:        if (addSpace)
                    412:            Buf_AddSpace(buf);
1.7     ! espie     413:        Buf_AddInterval(buf, word->s, word->e);
1.1       espie     414:        addSpace = TRUE;
                    415:     }
1.7     ! espie     416:     last->s = word->s;
        !           417:     last->e = word->e;
1.1       espie     418:     return addSpace;
                    419: }
                    420:
1.7     ! espie     421: static Boolean
        !           422: VarLoop(word, addSpace, buf, vp)
        !           423:     struct Name        *word;
        !           424:     Boolean    addSpace;
        !           425:     Buffer     buf;
        !           426:     void       *vp;
        !           427: {
        !           428:     struct LoopStuff *v = (struct LoopStuff *)vp;
        !           429:
        !           430:     if (addSpace)
        !           431:        Buf_AddSpace(buf);
        !           432:     Var_SubstVar(buf, v->expand, v->var, word->s);
        !           433:     return TRUE;
        !           434: }
        !           435:
        !           436: static char *
        !           437: finish_loop(s, n, p)
        !           438:        const char *s;
        !           439:        const struct Name *n    UNUSED;
        !           440:        void *p;
        !           441: {
        !           442:        struct LoopStuff *l = (struct LoopStuff *)p;
        !           443:
        !           444:        return Var_Subst(s, l->ctxt, l->err);
        !           445: }
        !           446:
        !           447: static int
        !           448: NameCompare(ap, bp)
        !           449:        const void *ap;
        !           450:        const void *bp;
        !           451: {
        !           452:        struct Name *a, *b;
        !           453:        size_t n, m;
        !           454:        int c;
        !           455:
        !           456:        a = (struct Name *)ap;
        !           457:        b = (struct Name *)bp;
        !           458:        n = a->e - a->s;
        !           459:        m = b->e - b->s;
        !           460:        if (n < m) {
        !           461:                c = strncmp(a->s, b->s, n);
        !           462:                if (c != 0)
        !           463:                    return c;
        !           464:                else
        !           465:                    return -1;
        !           466:        } else if (m < n) {
        !           467:                c = strncmp(a->s, b->s, m);
        !           468:                if (c != 0)
        !           469:                    return c;
        !           470:                else
        !           471:                    return 1;
        !           472:        } else
        !           473:            return strncmp(a->s, b->s, n);
        !           474: }
        !           475:
        !           476: static char *
        !           477: do_sort(s, dummy, arg)
        !           478:     const char *s;
        !           479:     const struct Name *dummy UNUSED;
        !           480:     void       *arg    UNUSED;
        !           481: {
        !           482:     struct Name *t;
        !           483:     unsigned long n, i, j;
        !           484:     const char *start, *end;
        !           485:
        !           486:     n = 1024;  /* start at 1024 words */
        !           487:     t = (struct Name *)emalloc(sizeof(struct Name) * n);
        !           488:     start = s;
        !           489:     end = start;
        !           490:
        !           491:     for (i = 0;; i++) {
        !           492:        if (i == n) {
        !           493:                n *= 2;
        !           494:                t = (struct Name *)erealloc(t, sizeof(struct Name) * n);
        !           495:        }
        !           496:        start = iterate_words(&end);
        !           497:        if (start == NULL)
        !           498:            break;
        !           499:        t[i].s = start;
        !           500:        t[i].e = end;
        !           501:     }
        !           502:     if (i > 0) {
        !           503:        BUFFER buf;
        !           504:
        !           505:        Buf_Init(&buf, end - s);
        !           506:        qsort(t, i, sizeof(struct Name), NameCompare);
        !           507:        Buf_AddInterval(&buf, t[0].s, t[0].e);
        !           508:        for (j = 1; j < i; j++) {
        !           509:                Buf_AddSpace(&buf);
        !           510:                Buf_AddInterval(&buf, t[j].s, t[j].e);
        !           511:        }
        !           512:        free(t);
        !           513:        return Buf_Retrieve(&buf);
        !           514:     } else {
        !           515:        free(t);
        !           516:        return "";
        !           517:     }
        !           518: }
        !           519:
        !           520: static char *
        !           521: do_label(s, n, arg)
        !           522:     const char *s      UNUSED;
        !           523:     const struct Name *n;
        !           524:     void *arg          UNUSED;
        !           525: {
        !           526:     return interval_dup(n->s, n->e);
        !           527: }
        !           528:
        !           529: static char *
        !           530: do_path(s, n, arg)
        !           531:     const char *s      UNUSED;
        !           532:     const struct Name *n;
        !           533:     void *arg          UNUSED;
        !           534: {
        !           535:     GNode *gn;
        !           536:
        !           537:     gn = Targ_FindNode(n->s, n->e, TARG_NOCREATE);
        !           538:     if (gn == NULL)
        !           539:        return interval_dup(n->s, n->e);
        !           540:     else
        !           541:        return strdup(gn->path);
        !           542: }
        !           543:
        !           544: static char *
        !           545: do_def(s, n, arg)
        !           546:     const char *s;
        !           547:     const struct Name *n       UNUSED;
        !           548:     void *arg;
        !           549: {
        !           550:     VarPattern *v = (VarPattern *)arg;
        !           551:     if (s == NULL) {
        !           552:        free_patternarg(v);
        !           553:        return NULL;
        !           554:     } else
        !           555:        return v->lbuffer;
        !           556: }
        !           557:
        !           558: static char *
        !           559: do_undef(s, n, arg)
        !           560:     const char *s;
        !           561:     const struct Name *n       UNUSED;
        !           562:     void *arg;
        !           563: {
        !           564:     VarPattern *v = (VarPattern *)arg;
        !           565:     if (s != NULL) {
        !           566:        free_patternarg(v);
        !           567:        return NULL;
        !           568:     } else
        !           569:        return v->lbuffer;
        !           570: }
        !           571:
        !           572: static char *
        !           573: do_assign(s, n, arg)
        !           574:     const char *s;
        !           575:     const struct Name *n;
        !           576:     void *arg;
        !           577: {
        !           578:     VarPattern *v = (VarPattern *)arg;
        !           579:     char *msg;
        !           580:     char *result;
        !           581:
        !           582:     switch (v->flags) {
        !           583:     case VAR_EQUAL:
        !           584:        Var_Set_interval(n->s, n->e, v->lbuffer, VAR_GLOBAL);
        !           585:        break;
        !           586:     case VAR_MAY_EQUAL:
        !           587:        if (s == NULL)
        !           588:            Var_Set_interval(n->s, n->e, v->lbuffer, VAR_GLOBAL);
        !           589:        break;
        !           590:     case VAR_ADD_EQUAL:
        !           591:        if (s == NULL)
        !           592:            Var_Set_interval(n->s, n->e, v->lbuffer, VAR_GLOBAL);
        !           593:        else
        !           594:            Var_Append_interval(n->s, n->e, v->lbuffer, VAR_GLOBAL);
        !           595:        break;
        !           596:     case VAR_BANG_EQUAL:
        !           597:        result = Cmd_Exec(v->lbuffer, &msg);
        !           598:        if (result != NULL) {
        !           599:                Var_Set_interval(n->s, n->e, result, VAR_GLOBAL);
        !           600:                free(result);
        !           601:        } else
        !           602:                Error(msg, v->lbuffer);
        !           603:        break;
        !           604:
        !           605:     }
        !           606:     return NULL;
        !           607: }
        !           608:
        !           609: static char *
        !           610: do_exec(s, n, arg)
        !           611:     const char *s              UNUSED;
        !           612:     const struct Name *n       UNUSED;
        !           613:     void *arg;
        !           614: {
        !           615:     VarPattern *v = (VarPattern *)arg;
        !           616:     char *msg;
        !           617:     char *result;
        !           618:
        !           619:     result = Cmd_Exec(v->lbuffer, &msg);
        !           620:     if (result == NULL)
        !           621:        Error(msg, v->lbuffer);
        !           622:     return result;
        !           623: }
        !           624:
1.1       espie     625: /*-
                    626:  *-----------------------------------------------------------------------
                    627:  * VarSYSVMatch --
1.7     ! espie     628:  *     Add the word to the buffer if it matches the given pattern.
        !           629:  *     Used to implement the System V % modifiers.
1.1       espie     630:  *-----------------------------------------------------------------------
                    631:  */
                    632: static Boolean
                    633: VarSYSVMatch(word, addSpace, buf, patp)
1.7     ! espie     634:     struct Name *word;
        !           635:     Boolean    addSpace;
        !           636:     Buffer     buf;
        !           637:     void       *patp;  /* Pattern the word must match */
        !           638: {
        !           639:     size_t     len;
        !           640:     const char *ptr;
        !           641:     VarPattern *pat = (VarPattern *)patp;
1.1       espie     642:
1.7     ! espie     643:     if (*word->s != '\0') {
1.1       espie     644:            if (addSpace)
                    645:                Buf_AddSpace(buf);
1.7     ! espie     646:            if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
1.1       espie     647:                Str_SYSVSubst(buf, pat->rhs, ptr, len);
                    648:            else
1.7     ! espie     649:                Buf_AddInterval(buf, word->s, word->e);
        !           650:            return TRUE;
        !           651:     } else
        !           652:        return addSpace;
1.1       espie     653: }
                    654:
1.7     ! espie     655: void *
        !           656: get_sysvpattern(p, ctxt, err, endc)
        !           657:     const char         **p;
        !           658:     SymTable           *ctxt   UNUSED;
        !           659:     Boolean            err     UNUSED;
        !           660:     int                        endc;
1.1       espie     661: {
1.7     ! espie     662:     static VarPattern  pattern;
        !           663:     const char         *cp, *cp2;
        !           664:     int cnt = 0;
        !           665:     char startc = endc == ')' ? '(' : '}';
        !           666:
        !           667:     for (cp = *p;; cp++) {
        !           668:        if (*cp == '=' && cnt == 0)
        !           669:            break;
        !           670:        if (*cp == '\0')
        !           671:            return NULL;
        !           672:        if (*cp == startc)
        !           673:            cnt++;
        !           674:        else if (*cp == endc) {
        !           675:            cnt--;
        !           676:            if (cnt < 0)
        !           677:                return NULL;
        !           678:        }
        !           679:     }
        !           680:     for (cp2 = cp+1;; cp2++) {
        !           681:        if ((*cp2 == ':' || *cp2 == endc) && cnt == 0)
        !           682:            break;
        !           683:        if (*cp2 == startc)
        !           684:            cnt++;
        !           685:        else if (*cp2 == endc) {
        !           686:            cnt--;
        !           687:            if (cnt < 0)
        !           688:                return NULL;
        !           689:        }
1.1       espie     690:     }
1.7     ! espie     691:
        !           692:     pattern.lhs = interval_dup(*p, cp);
        !           693:     pattern.leftLen = cp - *p;
        !           694:     pattern.rhs = interval_dup(cp+1, cp2);
        !           695:     pattern.rightLen = cp2 - (cp+1);
        !           696:     pattern.flags = 0;
        !           697:     *p = cp2;
        !           698:     return &pattern;
1.1       espie     699: }
                    700:
                    701:
                    702: /*-
                    703:  *-----------------------------------------------------------------------
                    704:  * VarSubstitute --
1.7     ! espie     705:  *     Perform a string-substitution on the given word, Adding the
        !           706:  *     result to the given buffer.
1.1       espie     707:  *-----------------------------------------------------------------------
                    708:  */
                    709: static Boolean
                    710: VarSubstitute(word, addSpace, buf, patternp)
1.7     ! espie     711:     struct Name *word;
        !           712:     Boolean    addSpace;
        !           713:     Buffer     buf;
        !           714:     void       *patternp;      /* Pattern for substitution */
        !           715: {
        !           716:     size_t     wordLen;    /* Length of word */
        !           717:     const char *cp;        /* General pointer */
        !           718:     VarPattern *pattern = (VarPattern *)patternp;
1.1       espie     719:
1.7     ! espie     720:     wordLen = word->e - word->s;
1.1       espie     721:     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
                    722:        (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
1.7     ! espie     723:        /* Still substituting -- break it down into simple anchored cases
        !           724:         * and if none of them fits, perform the general substitution case.  */
1.1       espie     725:        if ((pattern->flags & VAR_MATCH_START) &&
1.7     ! espie     726:            (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
        !           727:                /* Anchored at start and beginning of word matches pattern.  */
1.1       espie     728:                if ((pattern->flags & VAR_MATCH_END) &&
                    729:                    (wordLen == pattern->leftLen)) {
1.7     ! espie     730:                        /* Also anchored at end and matches to the end (word
1.1       espie     731:                         * is same length as pattern) add space and rhs only
1.7     ! espie     732:                         * if rhs is non-null.  */
1.1       espie     733:                        if (pattern->rightLen != 0) {
                    734:                            if (addSpace)
                    735:                                Buf_AddSpace(buf);
                    736:                            addSpace = TRUE;
1.7     ! espie     737:                            Buf_AddChars(buf, pattern->rightLen,
        !           738:                                         pattern->rhs);
1.1       espie     739:                        }
                    740:                        pattern->flags |= VAR_SUB_MATCHED;
                    741:                } else if (pattern->flags & VAR_MATCH_END) {
1.7     ! espie     742:                    /* Doesn't match to end -- copy word wholesale.  */
1.1       espie     743:                    goto nosub;
                    744:                } else {
1.7     ! espie     745:                    /* Matches at start but need to copy in
        !           746:                     * trailing characters.  */
1.1       espie     747:                    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
                    748:                        if (addSpace)
                    749:                            Buf_AddSpace(buf);
                    750:                        addSpace = TRUE;
                    751:                    }
                    752:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    753:                    Buf_AddChars(buf, wordLen - pattern->leftLen,
1.7     ! espie     754:                                 word->s + pattern->leftLen);
1.1       espie     755:                    pattern->flags |= VAR_SUB_MATCHED;
                    756:                }
                    757:        } else if (pattern->flags & VAR_MATCH_START) {
1.7     ! espie     758:            /* Had to match at start of word and didn't -- copy whole word.  */
1.1       espie     759:            goto nosub;
                    760:        } else if (pattern->flags & VAR_MATCH_END) {
1.7     ! espie     761:            /* Anchored at end, Find only place match could occur (leftLen
1.1       espie     762:             * characters from the end of the word) and see if it does. Note
                    763:             * that because the $ will be left at the end of the lhs, we have
1.7     ! espie     764:             * to use strncmp.  */
        !           765:            cp = word->s + (wordLen - pattern->leftLen);
        !           766:            if (cp >= word->s &&
        !           767:                strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
        !           768:                /* Match found. If we will place characters in the buffer,
1.1       espie     769:                 * add a space before hand as indicated by addSpace, then
                    770:                 * stuff in the initial, unmatched part of the word followed
1.7     ! espie     771:                 * by the right-hand-side.  */
        !           772:                if (((cp - word->s) + pattern->rightLen) != 0) {
1.1       espie     773:                    if (addSpace)
                    774:                        Buf_AddSpace(buf);
                    775:                    addSpace = TRUE;
                    776:                }
1.7     ! espie     777:                Buf_AddInterval(buf, word->s, cp);
1.1       espie     778:                Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    779:                pattern->flags |= VAR_SUB_MATCHED;
                    780:            } else {
1.7     ! espie     781:                /* Had to match at end and didn't. Copy entire word.  */
1.1       espie     782:                goto nosub;
                    783:            }
                    784:        } else {
1.7     ! espie     785:            /* Pattern is unanchored: search for the pattern in the word using
        !           786:             * strstr, copying unmatched portions and the
1.1       espie     787:             * right-hand-side for each match found, handling non-global
                    788:             * substitutions correctly, etc. When the loop is done, any
                    789:             * remaining part of the word (word and wordLen are adjusted
                    790:             * accordingly through the loop) is copied straight into the
                    791:             * buffer.
1.7     ! espie     792:             * addSpace is set to FALSE as soon as a space is added to the
        !           793:             * buffer.  */
        !           794:            Boolean done;
1.1       espie     795:            size_t origSize;
                    796:
                    797:            done = FALSE;
                    798:            origSize = Buf_Size(buf);
                    799:            while (!done) {
1.7     ! espie     800:                cp = strstr(word->s, pattern->lhs);
        !           801:                if (cp != NULL) {
        !           802:                    if (addSpace && (cp - word->s) + pattern->rightLen != 0){
1.1       espie     803:                        Buf_AddSpace(buf);
                    804:                        addSpace = FALSE;
                    805:                    }
1.7     ! espie     806:                    Buf_AddInterval(buf, word->s, cp);
1.1       espie     807:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.7     ! espie     808:                    wordLen -= (cp - word->s) + pattern->leftLen;
        !           809:                    word->s = cp + pattern->leftLen;
        !           810:                    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
1.1       espie     811:                        done = TRUE;
                    812:                    pattern->flags |= VAR_SUB_MATCHED;
1.7     ! espie     813:                } else
1.1       espie     814:                    done = TRUE;
                    815:            }
                    816:            if (wordLen != 0) {
                    817:                if (addSpace)
                    818:                    Buf_AddSpace(buf);
1.7     ! espie     819:                Buf_AddChars(buf, wordLen, word->s);
1.1       espie     820:            }
1.7     ! espie     821:            /* If added characters to the buffer, need to add a space
1.1       espie     822:             * before we add any more. If we didn't add any, just return
1.7     ! espie     823:             * the previous value of addSpace.  */
        !           824:            return Buf_Size(buf) != origSize || addSpace;
1.1       espie     825:        }
1.7     ! espie     826:        return addSpace;
1.1       espie     827:     }
                    828:  nosub:
                    829:     if (addSpace)
                    830:        Buf_AddSpace(buf);
1.7     ! espie     831:     Buf_AddChars(buf, wordLen, word->s);
        !           832:     return TRUE;
1.1       espie     833: }
                    834:
                    835: #ifndef MAKE_BOOTSTRAP
                    836: /*-
                    837:  *-----------------------------------------------------------------------
                    838:  * VarREError --
                    839:  *     Print the error caused by a regcomp or regexec call.
                    840:  *-----------------------------------------------------------------------
                    841:  */
                    842: static void
                    843: VarREError(err, pat, str)
1.7     ! espie     844:     int        err;
        !           845:     regex_t    *pat;
        !           846:     const char *str;
1.1       espie     847: {
1.7     ! espie     848:     char       *errbuf;
        !           849:     int        errlen;
1.1       espie     850:
                    851:     errlen = regerror(err, pat, 0, 0);
                    852:     errbuf = emalloc(errlen);
                    853:     regerror(err, pat, errbuf, errlen);
                    854:     Error("%s: %s", str, errbuf);
                    855:     free(errbuf);
                    856: }
                    857:
                    858: /*-
                    859:  *-----------------------------------------------------------------------
                    860:  * VarRESubstitute --
                    861:  *     Perform a regex substitution on the given word, placing the
                    862:  *     result in the passed buffer.
                    863:  *-----------------------------------------------------------------------
                    864:  */
                    865: static Boolean
                    866: VarRESubstitute(word, addSpace, buf, patternp)
1.7     ! espie     867:     struct Name                *word;
        !           868:     Boolean            addSpace;
        !           869:     Buffer             buf;
        !           870:     void               *patternp;
        !           871: {
        !           872:     VarREPattern       *pat;
        !           873:     int                xrv;
        !           874:     const char         *wp;
        !           875:     char               *rp;
        !           876:     int                added;
1.1       espie     877:
                    878: #define MAYBE_ADD_SPACE()              \
1.7     ! espie     879:        if (addSpace && !added)         \
1.1       espie     880:            Buf_AddSpace(buf);          \
                    881:        added = 1
                    882:
                    883:     added = 0;
1.7     ! espie     884:     wp = word->s;
1.1       espie     885:     pat = patternp;
                    886:
                    887:     if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
                    888:        (VAR_SUB_ONE|VAR_SUB_MATCHED))
                    889:        xrv = REG_NOMATCH;
                    890:     else {
                    891:     tryagain:
                    892:        xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
                    893:     }
                    894:
                    895:     switch (xrv) {
                    896:     case 0:
                    897:        pat->flags |= VAR_SUB_MATCHED;
                    898:        if (pat->matches[0].rm_so > 0) {
                    899:            MAYBE_ADD_SPACE();
                    900:            Buf_AddChars(buf, pat->matches[0].rm_so, wp);
                    901:        }
                    902:
                    903:        for (rp = pat->replace; *rp; rp++) {
1.7     ! espie     904:            if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
1.1       espie     905:                MAYBE_ADD_SPACE();
1.7     ! espie     906:                Buf_AddChar(buf,rp[1]);
1.1       espie     907:                rp++;
                    908:            }
1.7     ! espie     909:            else if (*rp == '&' || (*rp == '\\' && isdigit(rp[1]))) {
1.1       espie     910:                int n;
                    911:                const char *subbuf;
                    912:                int sublen;
                    913:                char errstr[3];
                    914:
                    915:                if (*rp == '&') {
                    916:                    n = 0;
                    917:                    errstr[0] = '&';
                    918:                    errstr[1] = '\0';
                    919:                } else {
                    920:                    n = rp[1] - '0';
                    921:                    errstr[0] = '\\';
                    922:                    errstr[1] = rp[1];
                    923:                    errstr[2] = '\0';
                    924:                    rp++;
                    925:                }
                    926:
                    927:                if (n > pat->nsub) {
                    928:                    Error("No subexpression %s", &errstr[0]);
                    929:                    subbuf = "";
                    930:                    sublen = 0;
1.7     ! espie     931:                } else if (pat->matches[n].rm_so == -1 &&
        !           932:                           pat->matches[n].rm_eo == -1) {
1.1       espie     933:                    Error("No match for subexpression %s", &errstr[0]);
                    934:                    subbuf = "";
                    935:                    sublen = 0;
1.7     ! espie     936:                } else {
1.1       espie     937:                    subbuf = wp + pat->matches[n].rm_so;
                    938:                    sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
                    939:                }
                    940:
                    941:                if (sublen > 0) {
                    942:                    MAYBE_ADD_SPACE();
                    943:                    Buf_AddChars(buf, sublen, subbuf);
                    944:                }
                    945:            } else {
                    946:                MAYBE_ADD_SPACE();
                    947:                Buf_AddChar(buf, *rp);
                    948:            }
                    949:        }
                    950:        wp += pat->matches[0].rm_eo;
                    951:        if (pat->flags & VAR_SUB_GLOBAL)
                    952:            goto tryagain;
                    953:        if (*wp) {
                    954:            MAYBE_ADD_SPACE();
1.7     ! espie     955:            Buf_AddString(buf, wp);
1.1       espie     956:        }
                    957:        break;
                    958:     default:
                    959:        VarREError(xrv, &pat->re, "Unexpected regex error");
                    960:        /* fall through */
                    961:     case REG_NOMATCH:
                    962:        if (*wp) {
                    963:            MAYBE_ADD_SPACE();
1.7     ! espie     964:            Buf_AddString(buf, wp);
1.1       espie     965:        }
                    966:        break;
                    967:     }
1.7     ! espie     968:     return addSpace||added;
1.1       espie     969: }
                    970: #endif
                    971:
                    972: /*-
                    973:  *-----------------------------------------------------------------------
                    974:  * VarModify --
                    975:  *     Modify each of the words of the passed string using the given
                    976:  *     function. Used to implement all modifiers.
                    977:  *
                    978:  * Results:
                    979:  *     A string of all the words modified appropriately.
                    980:  *-----------------------------------------------------------------------
                    981:  */
                    982: static char *
1.5       espie     983: VarModify(str, modProc, datum)
1.7     ! espie     984:     char         *str;         /* String whose words should be trimmed */
        !           985:                                /* Function to use to modify them */
        !           986:     Boolean      (*modProc)(struct Name *, Boolean, Buffer, void *);
        !           987:     void         *datum;       /* Datum to pass it */
1.1       espie     988: {
1.7     ! espie     989:     BUFFER       buf;          /* Buffer for the new string */
        !           990:     Boolean      addSpace;     /* TRUE if need to add a space to the
1.5       espie     991:                                 * buffer before adding the trimmed
                    992:                                 * word */
1.7     ! espie     993:     struct Name          word;
1.2       espie     994:
1.1       espie     995:     Buf_Init(&buf, 0);
                    996:     addSpace = FALSE;
                    997:
1.7     ! espie     998:     word.e = str;
1.1       espie     999:
1.7     ! espie    1000:     while ((word.s = iterate_words(&word.e)) != NULL) {
        !          1001:        char termc;
1.1       espie    1002:
1.7     ! espie    1003:        termc = *word.e;
        !          1004:        *((char *)(word.e)) = '\0';
        !          1005:        addSpace = (*modProc)(&word, addSpace, &buf, datum);
        !          1006:        *((char *)(word.e)) = termc;
1.5       espie    1007:     }
1.1       espie    1008:     return Buf_Retrieve(&buf);
                   1009: }
                   1010:
                   1011: /*-
                   1012:  *-----------------------------------------------------------------------
                   1013:  * VarGetPattern --
                   1014:  *     Pass through the tstr looking for 1) escaped delimiters,
                   1015:  *     '$'s and backslashes (place the escaped character in
                   1016:  *     uninterpreted) and 2) unescaped $'s that aren't before
                   1017:  *     the delimiter (expand the variable substitution).
                   1018:  *     Return the expanded string or NULL if the delimiter was missing
                   1019:  *     If pattern is specified, handle escaped ampersands, and replace
                   1020:  *     unescaped ampersands with the lhs of the pattern.
                   1021:  *
                   1022:  * Results:
                   1023:  *     A string of all the words modified appropriately.
                   1024:  *     If length is specified, return the string length of the buffer
                   1025:  *-----------------------------------------------------------------------
                   1026:  */
                   1027: static char *
1.7     ! espie    1028: VarGetPattern(ctxt, err, tstr, delim1, delim2, length, pattern)
        !          1029:     SymTable   *ctxt;
        !          1030:     int        err;
        !          1031:     const char **tstr;
        !          1032:     int        delim1;
        !          1033:     int        delim2;
        !          1034:     size_t     *length;
        !          1035:     VarPattern *pattern;
1.1       espie    1036: {
1.7     ! espie    1037:     const char *cp;
        !          1038:     char       *result;
        !          1039:     BUFFER     buf;
        !          1040:     size_t     junk;
1.1       espie    1041:
                   1042:     Buf_Init(&buf, 0);
                   1043:     if (length == NULL)
                   1044:        length = &junk;
                   1045:
1.7     ! espie    1046: #define IS_A_MATCH(cp, delim1, delim2) \
        !          1047:     (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
        !          1048:      cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
1.1       espie    1049:
                   1050:     /*
                   1051:      * Skim through until the matching delimiter is found;
                   1052:      * pick up variable substitutions on the way. Also allow
                   1053:      * backslashes to quote the delimiter, $, and \, but don't
                   1054:      * touch other backslashes.
                   1055:      */
1.7     ! espie    1056:     for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
        !          1057:        if (IS_A_MATCH(cp, delim1, delim2)) {
1.1       espie    1058:            Buf_AddChar(&buf, cp[1]);
                   1059:            cp++;
                   1060:        } else if (*cp == '$') {
1.7     ! espie    1061:                /* Allowed at end of pattern */
        !          1062:            if (cp[1] == delim1 || cp[1] == delim2)
        !          1063:                Buf_AddChar(&buf, *cp);
1.1       espie    1064:            else {
1.7     ! espie    1065:                size_t len;
        !          1066:
        !          1067:                /* If unescaped dollar sign not before the delimiter,
        !          1068:                 * assume it's a variable substitution and recurse.  */
        !          1069:                (void)Var_ParseBuffer(&buf, cp, ctxt, err, &len);
1.1       espie    1070:                cp += len - 1;
                   1071:            }
1.7     ! espie    1072:        } else if (pattern && *cp == '&')
1.1       espie    1073:            Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
                   1074:        else
                   1075:            Buf_AddChar(&buf, *cp);
                   1076:     }
                   1077:
1.7     ! espie    1078:     *length = Buf_Size(&buf);
        !          1079:     result = Buf_Retrieve(&buf);
        !          1080:
        !          1081:     if (*cp != delim1 && *cp != delim2) {
1.1       espie    1082:        *tstr = cp;
                   1083:        *length = 0;
1.7     ! espie    1084:        free(result);
1.1       espie    1085:        return NULL;
                   1086:     }
                   1087:     else {
                   1088:        *tstr = ++cp;
1.7     ! espie    1089:        return result;
        !          1090:     }
        !          1091: }
        !          1092:
        !          1093: /*-
        !          1094:  *-----------------------------------------------------------------------
        !          1095:  * VarQuote --
        !          1096:  *     Quote shell meta-characters in the string
        !          1097:  *
        !          1098:  * Results:
        !          1099:  *     The quoted string
        !          1100:  *-----------------------------------------------------------------------
        !          1101:  */
        !          1102: static char *
        !          1103: VarQuote(str, n, dummy)
        !          1104:     const char *str;
        !          1105:     const struct Name *n       UNUSED;
        !          1106:     void       *dummy          UNUSED;
        !          1107: {
        !          1108:
        !          1109:     BUFFER       buf;
        !          1110:     /* This should cover most shells :-( */
        !          1111:     static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
        !          1112:
        !          1113:     Buf_Init(&buf, MAKE_BSIZE);
        !          1114:     for (; *str; str++) {
        !          1115:        if (strchr(meta, *str) != NULL)
        !          1116:            Buf_AddChar(&buf, '\\');
        !          1117:        Buf_AddChar(&buf, *str);
        !          1118:     }
        !          1119:     return Buf_Retrieve(&buf);
        !          1120: }
        !          1121:
        !          1122: static void *
        !          1123: check_empty(p, ctxt, b, endc)
        !          1124:     const char **p;
        !          1125:     SymTable   *ctxt           UNUSED;
        !          1126:     Boolean    b               UNUSED;
        !          1127:     int                endc;
        !          1128: {
        !          1129:     dummy_arg->s = NULL;
        !          1130:     if ((*p)[1] == endc || (*p)[1] == ':') {
        !          1131:        (*p)++;
        !          1132:        return dummy_arg;
        !          1133:     } else
        !          1134:        return NULL;
        !          1135: }
        !          1136:
        !          1137: static void *
        !          1138: check_shcmd(p, ctxt, b, endc)
        !          1139:     const char **p;
        !          1140:     SymTable   *ctxt           UNUSED;
        !          1141:     Boolean    b               UNUSED;
        !          1142:     int                endc;
        !          1143: {
        !          1144:     if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
        !          1145:        (*p)+=2;
        !          1146:        return dummy_arg;
        !          1147:     } else
        !          1148:        return NULL;
        !          1149: }
        !          1150:
        !          1151:
        !          1152: static char *
        !          1153: do_shcmd(s, n, arg)
        !          1154:     const char *s;
        !          1155:     const struct Name *n       UNUSED;
        !          1156:     void       *arg            UNUSED;
        !          1157: {
        !          1158:     char       *err;
        !          1159:     char       *t;
        !          1160:
        !          1161:     t = Cmd_Exec(s, &err);
        !          1162:     if (err)
        !          1163:        Error(err, s);
        !          1164:     return t;
        !          1165: }
        !          1166:
        !          1167: static void *
        !          1168: get_stringarg(p, ctxt, b, endc)
        !          1169:     const char **p;
        !          1170:     SymTable   *ctxt           UNUSED;
        !          1171:     Boolean    b               UNUSED;
        !          1172:     int                endc;
        !          1173: {
        !          1174:     const char *cp;
        !          1175:     char       *s;
        !          1176:
        !          1177:     for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
        !          1178:        if (*cp == '\\') {
        !          1179:            if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
        !          1180:                cp++;
        !          1181:        } else if (*cp == '\0')
        !          1182:            return NULL;
1.1       espie    1183:     }
1.7     ! espie    1184:     s = escape_dup(*p+1, cp, ":)}");
        !          1185:     *p = cp;
        !          1186:     return s;
        !          1187: }
        !          1188:
        !          1189: static void
        !          1190: free_stringarg(arg)
        !          1191:     void *arg;
        !          1192: {
        !          1193:     free(arg);
        !          1194: }
        !          1195:
        !          1196: static char *
        !          1197: do_upper(s, n, arg)
        !          1198:     const char *s;
        !          1199:     const struct Name *n       UNUSED;
        !          1200:     void       *arg            UNUSED;
        !          1201: {
        !          1202:     size_t     len, i;
        !          1203:     char       *t;
        !          1204:
        !          1205:     len = strlen(s);
        !          1206:     t = emalloc(len+1);
        !          1207:     for (i = 0; i < len; i++)
        !          1208:        t[i] = toupper(s[i]);
        !          1209:     t[len] = '\0';
        !          1210:     return t;
        !          1211: }
        !          1212:
        !          1213: static char *
        !          1214: do_lower(s, n, arg)
        !          1215:     const char *s;
        !          1216:     const struct Name *n       UNUSED;
        !          1217:     void       *arg            UNUSED;
        !          1218: {
        !          1219:     size_t     len, i;
        !          1220:     char       *t;
        !          1221:
        !          1222:     len = strlen(s);
        !          1223:     t = emalloc(len+1);
        !          1224:     for (i = 0; i < len; i++)
        !          1225:        t[i] = tolower(s[i]);
        !          1226:     t[len] = '\0';
        !          1227:     return t;
1.1       espie    1228: }
                   1229:
1.7     ! espie    1230: /* Extract anchors */
        !          1231: static void *
        !          1232: get_spatternarg(p, ctxt, err, endc)
        !          1233:     const char **p;
1.1       espie    1234:     SymTable   *ctxt;
                   1235:     Boolean    err;
1.7     ! espie    1236:     int                endc;
        !          1237: {
        !          1238:     VarPattern *pattern;
        !          1239:
        !          1240:     pattern = get_patternarg(p, ctxt, err, endc);
        !          1241:     if (pattern != NULL && pattern->leftLen > 0) {
        !          1242:        if (pattern->lhs[pattern->leftLen-1] == '$') {
        !          1243:                pattern->leftLen--;
        !          1244:                pattern->flags |= VAR_MATCH_END;
        !          1245:        }
        !          1246:        if (pattern->lhs[0] == '^') {
        !          1247:                pattern->lhs++;
        !          1248:                pattern->leftLen--;
        !          1249:                pattern->flags |= VAR_MATCH_START;
        !          1250:                }
        !          1251:     }
        !          1252:     return pattern;
        !          1253: }
        !          1254:
        !          1255: static void
        !          1256: free_looparg(arg)
        !          1257:     void *arg;
1.1       espie    1258: {
1.7     ! espie    1259:     struct LoopStuff *l = (struct LoopStuff *)arg;
1.1       espie    1260:
1.7     ! espie    1261:     free(l->var);
        !          1262:     free(l->expand);
        !          1263: }
1.1       espie    1264:
1.7     ! espie    1265: static char *
        !          1266: LoopGrab(s)
        !          1267:     const char **s;
        !          1268: {
        !          1269:     const char *p, *start;
1.1       espie    1270:
1.7     ! espie    1271:     start = *s;
        !          1272:     for (p = start; *p != '@'; p++) {
        !          1273:        if (*p == '\\')
        !          1274:            p++;
        !          1275:        if (*p == 0)
        !          1276:                return NULL;
        !          1277:     }
        !          1278:     *s = p+1;
        !          1279:     return escape_dup(start, p, "@\\");
        !          1280: }
        !          1281:
        !          1282: static void *
        !          1283: get_loop(p, ctxt, err, endc)
        !          1284:     const char         **p;
        !          1285:     SymTable   *ctxt;
        !          1286:     Boolean    err;
        !          1287:     int                endc;
        !          1288: {
        !          1289:     static struct LoopStuff    loop;
        !          1290:     const char *s;
1.1       espie    1291:
1.7     ! espie    1292:     s = *p +1;
1.1       espie    1293:
1.7     ! espie    1294:     loop.var = NULL;
        !          1295:     loop.expand = NULL;
        !          1296:     loop.ctxt =ctxt;
        !          1297:     loop.err = err;
        !          1298:     loop.var = LoopGrab(&s);
        !          1299:     if (loop.var != NULL) {
        !          1300:        loop.expand = LoopGrab(&s);
        !          1301:        if (*s == endc || *s == ':') {
        !          1302:            *p = s;
        !          1303:            return &loop;
        !          1304:        }
        !          1305:     }
        !          1306:     free_looparg(&loop);
        !          1307:     return NULL;
        !          1308: }
1.1       espie    1309:
1.7     ! espie    1310: static void *
        !          1311: get_patternarg(p, ctxt, err, endc)
        !          1312:     const char **p;
        !          1313:     SymTable   *ctxt;
        !          1314:     Boolean    err;
        !          1315:     int                endc;
        !          1316: {
        !          1317:     static VarPattern  pattern;        /* just one for the whole system */
        !          1318:     char       delim;
        !          1319:     const char *s;
1.1       espie    1320:
1.7     ! espie    1321:     s = *p;
1.1       espie    1322:
1.7     ! espie    1323:     pattern.flags = 0;
        !          1324:     delim = s[1];
        !          1325:     if (delim == '\0')
        !          1326:        return NULL;
        !          1327:     s += 2;
1.1       espie    1328:
1.7     ! espie    1329:     pattern.rhs = NULL;
        !          1330:     pattern.lhs = VarGetPattern(ctxt, err, &s, delim, delim,
        !          1331:        &pattern.leftLen, NULL);
        !          1332:     pattern.lbuffer = pattern.lhs;
        !          1333:     if (pattern.lhs != NULL) {
        !          1334:        pattern.rhs = VarGetPattern(ctxt, err, &s, delim, delim,
        !          1335:            &pattern.rightLen, &pattern);
        !          1336:        if (pattern.rhs != NULL) {
        !          1337:            /* Check for global substitution. If 'g' after the final
        !          1338:             * delimiter, substitution is global and is marked that
        !          1339:             * way.  */
        !          1340:            for (;; s++) {
        !          1341:                switch (*s) {
        !          1342:                case 'g':
        !          1343:                    pattern.flags |= VAR_SUB_GLOBAL;
        !          1344:                    continue;
        !          1345:                case '1':
        !          1346:                    pattern.flags |= VAR_SUB_ONE;
        !          1347:                    continue;
1.1       espie    1348:                }
                   1349:                break;
                   1350:            }
1.7     ! espie    1351:            if (*s == endc || *s == ':') {
        !          1352:                *p = s;
        !          1353:                return &pattern;
        !          1354:            }
        !          1355:        }
        !          1356:     }
        !          1357:     free_patternarg(&pattern);
        !          1358:     return NULL;
        !          1359: }
        !          1360:
        !          1361: static void *
        !          1362: assign_get_value(p, ctxt, err, endc)
        !          1363:     const char         **p;
        !          1364:     SymTable   *ctxt;
        !          1365:     Boolean    err;
        !          1366:     int                endc;
        !          1367: {
        !          1368:     const char *s;
        !          1369:     int flags;
        !          1370:     VarPattern *arg;
        !          1371:
        !          1372:     s = *p + 1;
        !          1373:     if (s[0] == '=')
        !          1374:        flags = VAR_EQUAL;
        !          1375:     else if (s[0] == '?' && s[1] == '=')
        !          1376:        flags = VAR_MAY_EQUAL;
        !          1377:     else if (s[0] == '+' && s[1] == '=')
        !          1378:        flags = VAR_ADD_EQUAL;
        !          1379:     else if (s[0] == '!' && s[1] == '=')
        !          1380:        flags = VAR_BANG_EQUAL;
        !          1381:     else
        !          1382:        return NULL;
1.1       espie    1383:
1.7     ! espie    1384:     arg = get_value(&s, ctxt, err, endc);
        !          1385:     if (arg != NULL) {
        !          1386:        *p = s;
        !          1387:        arg->flags = flags;
        !          1388:     }
        !          1389:     return arg;
        !          1390: }
1.1       espie    1391:
1.7     ! espie    1392: static void *
        !          1393: get_value(p, ctxt, err, endc)
        !          1394:     const char         **p;
        !          1395:     SymTable   *ctxt;
        !          1396:     Boolean    err;
        !          1397:     int                endc;
        !          1398: {
        !          1399:     static VarPattern pattern;
        !          1400:     const char *s;
1.1       espie    1401:
1.7     ! espie    1402:     s = *p + 1;
        !          1403:     pattern.rhs = NULL;
        !          1404:     pattern.lbuffer = VarGetPattern(ctxt, err, &s, ':', endc,
        !          1405:        &pattern.leftLen, NULL);
        !          1406:     if (s[-1] == endc || s[-1] == ':') {
        !          1407:        *p = s-1;
        !          1408:        return &pattern;
        !          1409:     }
        !          1410:     free_patternarg(&pattern);
        !          1411:     return NULL;
        !          1412: }
1.1       espie    1413:
1.7     ! espie    1414: static void *
        !          1415: get_cmd(p, ctxt, err, endc)
        !          1416:     const char         **p;
        !          1417:     SymTable   *ctxt;
        !          1418:     Boolean    err;
        !          1419:     int                endc    UNUSED;
        !          1420: {
        !          1421:     static VarPattern pattern;
        !          1422:     const char *s;
1.1       espie    1423:
1.7     ! espie    1424:     s = *p + 1;
        !          1425:     pattern.rhs = NULL;
        !          1426:     pattern.lbuffer = VarGetPattern(ctxt, err, &s, '!', '!',
        !          1427:        &pattern.leftLen, NULL);
        !          1428:     if (s[-1] == '!') {
        !          1429:        *p = s-1;
        !          1430:        return &pattern;
1.1       espie    1431:     }
1.7     ! espie    1432:     free_patternarg(&pattern);
        !          1433:     return NULL;
        !          1434: }
1.1       espie    1435:
1.7     ! espie    1436: static void
        !          1437: free_patternarg(p)
        !          1438:     void *p;
        !          1439: {
        !          1440:     VarPattern *vp = (VarPattern *)p;
        !          1441:     free(vp->lbuffer);
        !          1442:     free(vp->rhs);
1.1       espie    1443: }
                   1444:
1.7     ! espie    1445: #ifndef MAKE_BOOTSTRAP
1.1       espie    1446: static char *
1.7     ! espie    1447: do_regex(s, n, arg)
        !          1448:     const char *s;
        !          1449:     const struct Name *n       UNUSED;
        !          1450:     void       *arg;
        !          1451: {
        !          1452:     VarREPattern p2;
        !          1453:     VarPattern *p = (VarPattern *)arg;
        !          1454:     int        error;
        !          1455:     char       *result;
        !          1456:
        !          1457:     error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
        !          1458:     if (error) {
        !          1459:        VarREError(error, &p2.re, "RE substitution error");
        !          1460:        return var_Error;
        !          1461:     }
        !          1462:     p2.nsub = p2.re.re_nsub + 1;
        !          1463:     p2.replace = p->rhs;
        !          1464:     p2.flags = p->flags;
        !          1465:     if (p2.nsub < 1)
        !          1466:        p2.nsub = 1;
        !          1467:     if (p2.nsub > 10)
        !          1468:        p2.nsub = 10;
        !          1469:     p2.matches = emalloc(p2.nsub * sizeof(regmatch_t));
        !          1470:     result = VarModify((char *)s, VarRESubstitute, &p2);
        !          1471:     regfree(&p2.re);
        !          1472:     free(p2.matches);
        !          1473:     return result;
        !          1474: }
        !          1475: #endif
        !          1476:
        !          1477: char *
        !          1478: VarModifiers_Apply(str, name, ctxt, err, freePtr, start, endc, lengthPtr)
        !          1479:     char       *str;
        !          1480:     const struct Name *name;
        !          1481:     SymTable   *ctxt;
        !          1482:     Boolean    err;
        !          1483:     Boolean    *freePtr;
        !          1484:     const char *start;
        !          1485:     int                endc;
        !          1486:     size_t     *lengthPtr;
1.1       espie    1487: {
1.7     ! espie    1488:     const char *tstr;
        !          1489:     Boolean    atstart;    /* Some ODE modifiers only make sense at start */
1.1       espie    1490:
1.7     ! espie    1491:     tstr = start;
        !          1492:     /*
        !          1493:      * Now we need to apply any modifiers the user wants applied.
        !          1494:      * These are:
        !          1495:      *           :M<pattern>   words which match the given <pattern>.
        !          1496:      *                         <pattern> is of the standard file
        !          1497:      *                         wildcarding form.
        !          1498:      *           :S<d><pat1><d><pat2><d>[g]
        !          1499:      *                         Substitute <pat2> for <pat1> in the value
        !          1500:      *           :C<d><pat1><d><pat2><d>[g]
        !          1501:      *                         Substitute <pat2> for regex <pat1> in the value
        !          1502:      *           :H            Substitute the head of each word
        !          1503:      *           :T            Substitute the tail of each word
        !          1504:      *           :E            Substitute the extension (minus '.') of
        !          1505:      *                         each word
        !          1506:      *           :R            Substitute the root of each word
        !          1507:      *                         (pathname minus the suffix).
        !          1508:      *           :lhs=rhs      Like :S, but the rhs goes to the end of
        !          1509:      *                         the invocation.
        !          1510:      */
        !          1511:
        !          1512:     atstart = TRUE;
        !          1513:     while (*tstr != endc && *tstr != '\0') {
        !          1514:        struct modifier *mod;
        !          1515:        void            *arg;
        !          1516:        char            *newStr;
        !          1517:
        !          1518:        tstr++;
        !          1519:        if (DEBUG(VAR))
        !          1520:            printf("Applying :%c to \"%s\"\n", *tstr, str);
1.2       espie    1521:
1.7     ! espie    1522:        mod = choose_mod[*tstr];
        !          1523:        arg = NULL;
1.1       espie    1524:
1.7     ! espie    1525:        if (mod != NULL && (!mod->atstart || atstart))
        !          1526:            arg = mod->getarg(&tstr, ctxt, err, endc);
        !          1527:        if (FEATURES(FEATURE_SYSVVARSUB) && arg == NULL) {
        !          1528:            mod = &sysv_mod;
        !          1529:            arg = mod->getarg(&tstr, ctxt, err, endc);
        !          1530:        }
        !          1531:        atstart = FALSE;
        !          1532:        if (arg != NULL) {
        !          1533:            if (str != NULL || (mod->atstart && name != NULL)) {
        !          1534:                if (mod->word_apply != NULL) {
        !          1535:                    newStr = VarModify(str, mod->word_apply, arg);
        !          1536:                    if (mod->apply != NULL) {
        !          1537:                        char *newStr2;
        !          1538:
        !          1539:                        newStr2 = mod->apply(newStr, name, arg);
        !          1540:                        free(newStr);
        !          1541:                        newStr = newStr2;
        !          1542:                    }
        !          1543:                } else
        !          1544:                    newStr = mod->apply(str, name, arg);
        !          1545:                if (*freePtr)
        !          1546:                    free(str);
        !          1547:                str = newStr;
        !          1548:                if (str != var_Error)
        !          1549:                    *freePtr = TRUE;
        !          1550:                else
        !          1551:                    *freePtr = FALSE;
        !          1552:            }
        !          1553:            if (mod->freearg != NULL)
        !          1554:                mod->freearg(arg);
        !          1555:        } else {
        !          1556:            Error("Bad modifier: %s\n", tstr);
        !          1557:            /* Try skipping to end of var... */
        !          1558:            for (tstr++; *tstr != endc && *tstr != '\0';)
        !          1559:                tstr++;
        !          1560:            if (str != NULL && *freePtr)
        !          1561:                free(str);
        !          1562:            str = var_Error;
        !          1563:            freePtr = FALSE;
        !          1564:            break;
        !          1565:        }
        !          1566:        if (DEBUG(VAR))
        !          1567:            printf("Result is \"%s\"\n", str);
1.1       espie    1568:     }
1.7     ! espie    1569:     if (*tstr == '\0')
        !          1570:        Error("Unclosed variable specification");
        !          1571:
        !          1572:     *lengthPtr += tstr - start;
        !          1573:     return str;
1.1       espie    1574: }
1.7     ! espie    1575:
1.1       espie    1576: /*-
                   1577:  *-----------------------------------------------------------------------
                   1578:  * Var_GetHead --
                   1579:  *     Find the leading components of a (list of) filename(s).
                   1580:  *     XXX: VarHead does not replace foo by ., as (sun) System V make
                   1581:  *     does.
                   1582:  *-----------------------------------------------------------------------
                   1583:  */
                   1584: char *
1.7     ! espie    1585: Var_GetHead(s)
        !          1586:     char *s;
1.1       espie    1587: {
1.7     ! espie    1588:     return VarModify(s, VarHead, NULL);
1.1       espie    1589: }
                   1590:
                   1591: /*-
                   1592:  *-----------------------------------------------------------------------
                   1593:  * Var_GetTail --
                   1594:  *     Return the tail from each of a list of words. Used to set the
                   1595:  *     System V local variables.
                   1596:  *-----------------------------------------------------------------------
                   1597:  */
                   1598: char *
1.7     ! espie    1599: Var_GetTail(s)
        !          1600:     char *s;
1.1       espie    1601: {
1.7     ! espie    1602:     return VarModify(s, VarTail, NULL);
1.1       espie    1603: }