[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.46

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