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

1.49    ! espie       1: /*     $OpenBSD: varmodifiers.c,v 1.48 2020/08/30 12:16:04 tb 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.49    ! espie      68: #include <assert.h>
1.9       espie      69: #include <ctype.h>
                     70: #include <sys/types.h>
                     71: #include <regex.h>
                     72: #include <stddef.h>
                     73: #include <stdio.h>
                     74: #include <stdlib.h>
                     75: #include <string.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.49    ! espie     103:        char      *lbuffer; /* Left string to free */
1.22      espie     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.9       espie     111: static bool VarHead(struct Name *, bool, Buffer, void *);
                    112: static bool VarTail(struct Name *, bool, Buffer, void *);
                    113: static bool VarSuffix(struct Name *, bool, Buffer, void *);
                    114: static bool VarRoot(struct Name *, bool, Buffer, void *);
                    115: static bool VarMatch(struct Name *, bool, Buffer, void *);
                    116: static bool VarSYSVMatch(struct Name *, bool, Buffer, void *);
                    117: static bool VarNoMatch(struct Name *, bool, Buffer, void *);
1.7       espie     118:
                    119:
                    120: static void VarREError(int, regex_t *, const char *);
1.9       espie     121: static bool VarRESubstitute(struct Name *, bool, Buffer, void *);
1.7       espie     122: static char *do_regex(const char *, const struct Name *, void *);
                    123:
1.1       espie     124: typedef struct {
1.22      espie     125:        regex_t   re;
                    126:        int       nsub;
                    127:        regmatch_t       *matches;
                    128:        char     *replace;
                    129:        int       flags;
1.1       espie     130: } VarREPattern;
                    131:
1.9       espie     132: static bool VarSubstitute(struct Name *, bool, Buffer, void *);
1.7       espie     133: static char *VarGetPattern(SymTable *, int, const char **, int, int,
                    134:     size_t *, VarPattern *);
                    135: static char *VarQuote(const char *, const struct Name *, void *);
1.9       espie     136: static char *VarModify(char *, bool (*)(struct Name *, bool, Buffer, void *), void *);
1.7       espie     137:
1.9       espie     138: static void *check_empty(const char **, SymTable *, bool, int);
1.26      espie     139: static void *check_quote(const char **, SymTable *, bool, int);
1.7       espie     140: static char *do_upper(const char *, const struct Name *, void *);
                    141: static char *do_lower(const char *, const struct Name *, void *);
1.9       espie     142: static void *check_shcmd(const char **, SymTable *, bool, int);
1.7       espie     143: static char *do_shcmd(const char *, const struct Name *, void *);
1.9       espie     144: static void *get_stringarg(const char **, SymTable *, bool, int);
1.7       espie     145: static void free_stringarg(void *);
1.9       espie     146: static void *get_patternarg(const char **, SymTable *, bool, int);
                    147: static void *get_spatternarg(const char **, SymTable *, bool, int);
1.10      espie     148: static void *common_get_patternarg(const char **, SymTable *, bool, int, bool);
1.7       espie     149: static void free_patternarg(void *);
1.9       espie     150: static void *get_sysvpattern(const char **, SymTable *, bool, int);
1.7       espie     151:
                    152: static struct Name dummy;
                    153: static struct Name *dummy_arg = &dummy;
                    154:
                    155: static struct modifier {
1.22      espie     156:            void * (*getarg)(const char **, SymTable *, bool, int);
                    157:            char * (*apply)(const char *, const struct Name *, void *);
                    158:            bool (*word_apply)(struct Name *, bool, Buffer, void *);
                    159:            void   (*freearg)(void *);
1.7       espie     160: } *choose_mod[256],
1.49    ! espie     161:        match_mod = {get_stringarg, NULL, VarMatch, free_stringarg},
        !           162:        nomatch_mod = {get_stringarg, NULL, VarNoMatch, free_stringarg},
        !           163:        subst_mod = {get_spatternarg, NULL, VarSubstitute, free_patternarg},
        !           164:        resubst_mod = {get_patternarg, do_regex, NULL, free_patternarg},
        !           165:        quote_mod = {check_quote, VarQuote, NULL , free},
        !           166:        tail_mod = {check_empty, NULL, VarTail, NULL},
        !           167:        head_mod = {check_empty, NULL, VarHead, NULL},
        !           168:        suffix_mod = {check_empty, NULL, VarSuffix, NULL},
        !           169:        root_mod = {check_empty, NULL, VarRoot, NULL},
        !           170:        upper_mod = {check_empty, do_upper, NULL, NULL},
        !           171:        lower_mod = {check_empty, do_lower, NULL, NULL},
        !           172:        shcmd_mod = {check_shcmd, do_shcmd, NULL, NULL},
        !           173:        sysv_mod = {get_sysvpattern, NULL, VarSYSVMatch, free_patternarg}
1.7       espie     174: ;
1.1       espie     175:
1.7       espie     176: void
                    177: VarModifiers_Init()
1.1       espie     178: {
1.22      espie     179:        choose_mod['M'] = &match_mod;
                    180:        choose_mod['N'] = &nomatch_mod;
                    181:        choose_mod['S'] = &subst_mod;
                    182:        choose_mod['C'] = &resubst_mod;
                    183:        choose_mod['Q'] = &quote_mod;
                    184:        choose_mod['T'] = &tail_mod;
                    185:        choose_mod['H'] = &head_mod;
                    186:        choose_mod['E'] = &suffix_mod;
                    187:        choose_mod['R'] = &root_mod;
1.49    ! espie     188:        choose_mod['U'] = &upper_mod;
        !           189:        choose_mod['L'] = &lower_mod;
        !           190:        choose_mod['s'] = &shcmd_mod;
1.1       espie     191: }
                    192:
1.7       espie     193: /* All modifiers handle addSpace (need to add a space before placing the
                    194:  * next word into the buffer) and propagate it when necessary.
1.1       espie     195:  */
                    196:
                    197: /*-
                    198:  *-----------------------------------------------------------------------
                    199:  * VarHead --
1.7       espie     200:  *     Remove the tail of the given word and add the result to the given
1.1       espie     201:  *     buffer.
                    202:  *-----------------------------------------------------------------------
                    203:  */
1.9       espie     204: static bool
1.13      espie     205: VarHead(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1       espie     206: {
1.22      espie     207:        const char      *slash;
1.1       espie     208:
1.22      espie     209:        slash = Str_rchri(word->s, word->e, '/');
                    210:        if (slash != NULL) {
                    211:                if (addSpace)
                    212:                        Buf_AddSpace(buf);
                    213:                Buf_Addi(buf, word->s, slash);
                    214:        } else {
                    215:                /* If no directory part, give . (q.v. the POSIX standard).  */
                    216:                if (addSpace)
                    217:                        Buf_AddString(buf, " .");
                    218:                else
                    219:                        Buf_AddChar(buf, '.');
                    220:        }
                    221:        return true;
1.1       espie     222: }
                    223:
                    224: /*-
                    225:  *-----------------------------------------------------------------------
                    226:  * VarTail --
1.7       espie     227:  *     Remove the head of the given word add the result to the given
1.1       espie     228:  *     buffer.
                    229:  *-----------------------------------------------------------------------
                    230:  */
1.9       espie     231: static bool
1.13      espie     232: VarTail(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1       espie     233: {
1.22      espie     234:        const char      *slash;
1.1       espie     235:
1.22      espie     236:        if (addSpace)
                    237:                Buf_AddSpace(buf);
                    238:        slash = Str_rchri(word->s, word->e, '/');
                    239:        if (slash != NULL)
                    240:                Buf_Addi(buf, slash+1, word->e);
                    241:        else
                    242:                Buf_Addi(buf, word->s, word->e);
                    243:        return true;
1.1       espie     244: }
                    245:
                    246: /*-
                    247:  *-----------------------------------------------------------------------
                    248:  * VarSuffix --
1.7       espie     249:  *     Add the suffix of the given word to the given buffer.
1.1       espie     250:  *-----------------------------------------------------------------------
                    251:  */
1.9       espie     252: static bool
1.13      espie     253: VarSuffix(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1       espie     254: {
1.22      espie     255:        const char      *dot;
1.1       espie     256:
1.22      espie     257:        dot = Str_rchri(word->s, word->e, '.');
                    258:        if (dot != NULL) {
                    259:                if (addSpace)
                    260:                        Buf_AddSpace(buf);
                    261:                Buf_Addi(buf, dot+1, word->e);
                    262:                addSpace = true;
                    263:        }
                    264:        return addSpace;
1.1       espie     265: }
                    266:
                    267: /*-
                    268:  *-----------------------------------------------------------------------
                    269:  * VarRoot --
1.7       espie     270:  *     Remove the suffix of the given word and add the result to the
1.1       espie     271:  *     buffer.
                    272:  *-----------------------------------------------------------------------
                    273:  */
1.9       espie     274: static bool
1.13      espie     275: VarRoot(struct Name *word, bool addSpace, Buffer buf, void *dummy UNUSED)
1.1       espie     276: {
1.22      espie     277:        const char      *dot;
1.1       espie     278:
1.22      espie     279:        if (addSpace)
                    280:                Buf_AddSpace(buf);
                    281:        dot = Str_rchri(word->s, word->e, '.');
                    282:        if (dot != NULL)
                    283:                Buf_Addi(buf, word->s, dot);
                    284:        else
                    285:                Buf_Addi(buf, word->s, word->e);
                    286:        return true;
1.1       espie     287: }
                    288:
                    289: /*-
                    290:  *-----------------------------------------------------------------------
                    291:  * VarMatch --
1.7       espie     292:  *     Add the word to the buffer if it matches the given pattern.
1.1       espie     293:  *-----------------------------------------------------------------------
                    294:  */
1.9       espie     295: static bool
1.49    ! espie     296: VarMatch(struct Name *word, bool addSpace, Buffer buf, void *pattern)
1.7       espie     297: {
1.41      espie     298:        const char *pat = pattern;
1.9       espie     299:
1.22      espie     300:        if (Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
                    301:                if (addSpace)
                    302:                        Buf_AddSpace(buf);
                    303:                Buf_Addi(buf, word->s, word->e);
                    304:                return true;
                    305:        } else
                    306:                return addSpace;
1.7       espie     307: }
                    308:
                    309: /*-
                    310:  *-----------------------------------------------------------------------
                    311:  * VarNoMatch --
                    312:  *     Add the word to the buffer if it doesn't match the given pattern.
                    313:  *-----------------------------------------------------------------------
                    314:  */
1.9       espie     315: static bool
1.49    ! espie     316: VarNoMatch(struct Name *word, bool addSpace, Buffer buf, void *pattern)
1.7       espie     317: {
1.41      espie     318:        const char *pat = pattern;
1.9       espie     319:
1.22      espie     320:        if (!Str_Matchi(word->s, word->e, pat, strchr(pat, '\0'))) {
                    321:                if (addSpace)
                    322:                        Buf_AddSpace(buf);
                    323:                Buf_Addi(buf, word->s, word->e);
                    324:                return true;
                    325:        } else
                    326:                return addSpace;
1.7       espie     327: }
                    328:
1.1       espie     329: /*-
                    330:  *-----------------------------------------------------------------------
                    331:  * VarSYSVMatch --
1.7       espie     332:  *     Add the word to the buffer if it matches the given pattern.
                    333:  *     Used to implement the System V % modifiers.
1.1       espie     334:  *-----------------------------------------------------------------------
                    335:  */
1.9       espie     336: static bool
1.22      espie     337: VarSYSVMatch(struct Name *word, bool addSpace, Buffer buf, void *patp)
1.7       espie     338: {
1.22      espie     339:        size_t  len;
                    340:        const char      *ptr;
1.40      espie     341:        VarPattern      *pat = patp;
1.1       espie     342:
1.22      espie     343:        if (*word->s != '\0') {
                    344:                if (addSpace)
                    345:                        Buf_AddSpace(buf);
                    346:                if ((ptr = Str_SYSVMatch(word->s, pat->lhs, &len)) != NULL)
                    347:                        Str_SYSVSubst(buf, pat->rhs, ptr, len);
                    348:                else
                    349:                        Buf_Addi(buf, word->s, word->e);
                    350:                return true;
                    351:        } else
                    352:                return addSpace;
1.1       espie     353: }
                    354:
1.7       espie     355: void *
1.26      espie     356: get_sysvpattern(const char **p, SymTable *ctxt UNUSED, bool err, int endc)
1.1       espie     357: {
1.22      espie     358:        VarPattern              *pattern;
                    359:        const char              *cp, *cp2;
1.46      espie     360:        BUFFER buf, buf2;
1.22      espie     361:        int cnt = 0;
                    362:        char startc = endc == ')' ? '(' : '{';
1.46      espie     363:
                    364:        Buf_Init(&buf, 0);
1.22      espie     365:        for (cp = *p;; cp++) {
                    366:                if (*cp == '=' && cnt == 0)
                    367:                        break;
1.46      espie     368:                if (*cp == '\0') {
                    369:                        Buf_Destroy(&buf);
1.22      espie     370:                        return NULL;
1.46      espie     371:                }
1.22      espie     372:                if (*cp == startc)
                    373:                        cnt++;
                    374:                else if (*cp == endc) {
                    375:                        cnt--;
1.46      espie     376:                        if (cnt < 0) {
                    377:                                Buf_Destroy(&buf);
1.22      espie     378:                                return NULL;
1.46      espie     379:                        }
                    380:                } else if (*cp == '$') {
                    381:                        if (cp[1] == '$')
                    382:                                cp++;
                    383:                        else {
                    384:                                size_t len;
                    385:                                (void)Var_ParseBuffer(&buf, cp, ctxt, err,
                    386:                                    &len);
                    387:                                cp += len - 1;
                    388:                                continue;
                    389:                        }
1.22      espie     390:                }
1.46      espie     391:                Buf_AddChar(&buf, *cp);
1.7       espie     392:        }
1.46      espie     393:
                    394:        Buf_Init(&buf2, 0);
1.22      espie     395:        for (cp2 = cp+1;; cp2++) {
1.30      espie     396:                if (((*cp2 == ':' && cp2[1] != endc) || *cp2 == endc) &&
                    397:                    cnt == 0)
1.22      espie     398:                        break;
1.26      espie     399:                if (*cp2 == '\0') {
                    400:                        Buf_Destroy(&buf);
1.46      espie     401:                        Buf_Destroy(&buf2);
1.22      espie     402:                        return NULL;
1.26      espie     403:                }
1.22      espie     404:                if (*cp2 == startc)
                    405:                        cnt++;
                    406:                else if (*cp2 == endc) {
                    407:                        cnt--;
1.26      espie     408:                        if (cnt < 0) {
                    409:                                Buf_Destroy(&buf);
1.46      espie     410:                                Buf_Destroy(&buf2);
1.22      espie     411:                                return NULL;
1.26      espie     412:                        }
                    413:                } else if (*cp2 == '$') {
                    414:                        if (cp2[1] == '$')
                    415:                                cp2++;
                    416:                        else {
                    417:                                size_t len;
1.46      espie     418:                                (void)Var_ParseBuffer(&buf2, cp2, ctxt, err,
1.26      espie     419:                                    &len);
                    420:                                cp2 += len - 1;
                    421:                                continue;
                    422:                        }
1.22      espie     423:                }
1.46      espie     424:                Buf_AddChar(&buf2, *cp2);
1.7       espie     425:        }
1.23      espie     426:
1.42      deraadt   427:        pattern = emalloc(sizeof(VarPattern));
1.46      espie     428:        pattern->lbuffer = pattern->lhs = Buf_Retrieve(&buf);
                    429:        pattern->leftLen = Buf_Size(&buf);
                    430:        pattern->rhs = Buf_Retrieve(&buf2);
                    431:        pattern->rightLen = Buf_Size(&buf2);
1.22      espie     432:        pattern->flags = 0;
                    433:        *p = cp2;
                    434:        return pattern;
1.1       espie     435: }
                    436:
                    437:
                    438: /*-
                    439:  *-----------------------------------------------------------------------
                    440:  * VarSubstitute --
1.7       espie     441:  *     Perform a string-substitution on the given word, Adding the
                    442:  *     result to the given buffer.
1.1       espie     443:  *-----------------------------------------------------------------------
                    444:  */
1.9       espie     445: static bool
1.23      espie     446: VarSubstitute(struct Name *word, bool addSpace, Buffer buf,
1.13      espie     447:     void *patternp) /* Pattern for substitution */
1.7       espie     448: {
                    449:     size_t     wordLen;    /* Length of word */
                    450:     const char *cp;        /* General pointer */
1.40      espie     451:     VarPattern *pattern = patternp;
1.1       espie     452:
1.7       espie     453:     wordLen = word->e - word->s;
1.1       espie     454:     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
                    455:        (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
1.7       espie     456:        /* Still substituting -- break it down into simple anchored cases
                    457:         * and if none of them fits, perform the general substitution case.  */
1.1       espie     458:        if ((pattern->flags & VAR_MATCH_START) &&
1.7       espie     459:            (strncmp(word->s, pattern->lhs, pattern->leftLen) == 0)) {
                    460:                /* Anchored at start and beginning of word matches pattern.  */
1.1       espie     461:                if ((pattern->flags & VAR_MATCH_END) &&
                    462:                    (wordLen == pattern->leftLen)) {
1.7       espie     463:                        /* Also anchored at end and matches to the end (word
1.1       espie     464:                         * is same length as pattern) add space and rhs only
1.7       espie     465:                         * if rhs is non-null.  */
1.1       espie     466:                        if (pattern->rightLen != 0) {
                    467:                            if (addSpace)
                    468:                                Buf_AddSpace(buf);
1.9       espie     469:                            addSpace = true;
1.7       espie     470:                            Buf_AddChars(buf, pattern->rightLen,
                    471:                                         pattern->rhs);
1.1       espie     472:                        }
                    473:                        pattern->flags |= VAR_SUB_MATCHED;
                    474:                } else if (pattern->flags & VAR_MATCH_END) {
1.7       espie     475:                    /* Doesn't match to end -- copy word wholesale.  */
1.1       espie     476:                    goto nosub;
                    477:                } else {
1.7       espie     478:                    /* Matches at start but need to copy in
                    479:                     * trailing characters.  */
1.1       espie     480:                    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
                    481:                        if (addSpace)
                    482:                            Buf_AddSpace(buf);
1.9       espie     483:                        addSpace = true;
1.1       espie     484:                    }
                    485:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    486:                    Buf_AddChars(buf, wordLen - pattern->leftLen,
1.7       espie     487:                                 word->s + pattern->leftLen);
1.1       espie     488:                    pattern->flags |= VAR_SUB_MATCHED;
                    489:                }
                    490:        } else if (pattern->flags & VAR_MATCH_START) {
1.7       espie     491:            /* Had to match at start of word and didn't -- copy whole word.  */
1.1       espie     492:            goto nosub;
                    493:        } else if (pattern->flags & VAR_MATCH_END) {
1.7       espie     494:            /* Anchored at end, Find only place match could occur (leftLen
1.1       espie     495:             * characters from the end of the word) and see if it does. Note
                    496:             * that because the $ will be left at the end of the lhs, we have
1.7       espie     497:             * to use strncmp.  */
                    498:            cp = word->s + (wordLen - pattern->leftLen);
                    499:            if (cp >= word->s &&
                    500:                strncmp(cp, pattern->lhs, pattern->leftLen) == 0) {
                    501:                /* Match found. If we will place characters in the buffer,
1.1       espie     502:                 * add a space before hand as indicated by addSpace, then
                    503:                 * stuff in the initial, unmatched part of the word followed
1.7       espie     504:                 * by the right-hand-side.  */
                    505:                if (((cp - word->s) + pattern->rightLen) != 0) {
1.1       espie     506:                    if (addSpace)
                    507:                        Buf_AddSpace(buf);
1.9       espie     508:                    addSpace = true;
1.1       espie     509:                }
1.9       espie     510:                Buf_Addi(buf, word->s, cp);
1.1       espie     511:                Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    512:                pattern->flags |= VAR_SUB_MATCHED;
                    513:            } else {
1.7       espie     514:                /* Had to match at end and didn't. Copy entire word.  */
1.1       espie     515:                goto nosub;
                    516:            }
                    517:        } else {
1.7       espie     518:            /* Pattern is unanchored: search for the pattern in the word using
                    519:             * strstr, copying unmatched portions and the
1.1       espie     520:             * right-hand-side for each match found, handling non-global
                    521:             * substitutions correctly, etc. When the loop is done, any
                    522:             * remaining part of the word (word and wordLen are adjusted
                    523:             * accordingly through the loop) is copied straight into the
                    524:             * buffer.
1.9       espie     525:             * addSpace is set to false as soon as a space is added to the
1.7       espie     526:             * buffer.  */
1.9       espie     527:            bool done;
1.1       espie     528:            size_t origSize;
                    529:
1.9       espie     530:            done = false;
1.1       espie     531:            origSize = Buf_Size(buf);
                    532:            while (!done) {
1.7       espie     533:                cp = strstr(word->s, pattern->lhs);
                    534:                if (cp != NULL) {
                    535:                    if (addSpace && (cp - word->s) + pattern->rightLen != 0){
1.1       espie     536:                        Buf_AddSpace(buf);
1.9       espie     537:                        addSpace = false;
1.1       espie     538:                    }
1.9       espie     539:                    Buf_Addi(buf, word->s, cp);
1.1       espie     540:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
1.7       espie     541:                    wordLen -= (cp - word->s) + pattern->leftLen;
                    542:                    word->s = cp + pattern->leftLen;
                    543:                    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0)
1.9       espie     544:                        done = true;
1.1       espie     545:                    pattern->flags |= VAR_SUB_MATCHED;
1.7       espie     546:                } else
1.9       espie     547:                    done = true;
1.1       espie     548:            }
                    549:            if (wordLen != 0) {
                    550:                if (addSpace)
                    551:                    Buf_AddSpace(buf);
1.7       espie     552:                Buf_AddChars(buf, wordLen, word->s);
1.1       espie     553:            }
1.7       espie     554:            /* If added characters to the buffer, need to add a space
1.1       espie     555:             * before we add any more. If we didn't add any, just return
1.7       espie     556:             * the previous value of addSpace.  */
                    557:            return Buf_Size(buf) != origSize || addSpace;
1.1       espie     558:        }
1.7       espie     559:        return addSpace;
1.1       espie     560:     }
                    561:  nosub:
                    562:     if (addSpace)
                    563:        Buf_AddSpace(buf);
1.7       espie     564:     Buf_AddChars(buf, wordLen, word->s);
1.9       espie     565:     return true;
1.1       espie     566: }
                    567:
                    568: /*-
                    569:  *-----------------------------------------------------------------------
                    570:  * VarREError --
                    571:  *     Print the error caused by a regcomp or regexec call.
                    572:  *-----------------------------------------------------------------------
                    573:  */
                    574: static void
1.13      espie     575: VarREError(int err, regex_t *pat, const char *str)
1.1       espie     576: {
1.22      espie     577:        char    *errbuf;
                    578:        int     errlen;
1.1       espie     579:
1.22      espie     580:        errlen = regerror(err, pat, 0, 0);
                    581:        errbuf = emalloc(errlen);
                    582:        regerror(err, pat, errbuf, errlen);
                    583:        Error("%s: %s", str, errbuf);
                    584:        free(errbuf);
1.1       espie     585: }
                    586:
                    587: /*-
                    588:  *-----------------------------------------------------------------------
                    589:  * VarRESubstitute --
                    590:  *     Perform a regex substitution on the given word, placing the
                    591:  *     result in the passed buffer.
                    592:  *-----------------------------------------------------------------------
                    593:  */
1.9       espie     594: static bool
1.13      espie     595: VarRESubstitute(struct Name *word, bool addSpace, Buffer buf, void *patternp)
1.7       espie     596: {
1.22      espie     597:        VarREPattern    *pat;
                    598:        int             xrv;
                    599:        const char              *wp;
                    600:        char            *rp;
                    601:        int             added;
1.1       espie     602:
                    603: #define MAYBE_ADD_SPACE()              \
1.7       espie     604:        if (addSpace && !added)         \
1.22      espie     605:                Buf_AddSpace(buf);      \
1.1       espie     606:        added = 1
                    607:
1.22      espie     608:        added = 0;
                    609:        wp = word->s;
                    610:        pat = patternp;
                    611:
                    612:        if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
                    613:            (VAR_SUB_ONE|VAR_SUB_MATCHED))
                    614:                xrv = REG_NOMATCH;
                    615:        else {
                    616:        tryagain:
                    617:                xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
1.1       espie     618:        }
                    619:
1.22      espie     620:        switch (xrv) {
                    621:        case 0:
                    622:                pat->flags |= VAR_SUB_MATCHED;
                    623:                if (pat->matches[0].rm_so > 0) {
                    624:                        MAYBE_ADD_SPACE();
                    625:                        Buf_AddChars(buf, pat->matches[0].rm_so, wp);
1.1       espie     626:                }
                    627:
1.22      espie     628:                for (rp = pat->replace; *rp; rp++) {
                    629:                        if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
                    630:                                MAYBE_ADD_SPACE();
                    631:                                Buf_AddChar(buf,rp[1]);
                    632:                                rp++;
                    633:                        }
1.23      espie     634:                        else if (*rp == '&' ||
1.35      espie     635:                            (*rp == '\\' && ISDIGIT(rp[1]))) {
1.22      espie     636:                                int n;
                    637:                                const char *subbuf;
                    638:                                int sublen;
                    639:                                char errstr[3];
                    640:
                    641:                                if (*rp == '&') {
                    642:                                        n = 0;
                    643:                                        errstr[0] = '&';
                    644:                                        errstr[1] = '\0';
                    645:                                } else {
                    646:                                        n = rp[1] - '0';
                    647:                                        errstr[0] = '\\';
                    648:                                        errstr[1] = rp[1];
                    649:                                        errstr[2] = '\0';
                    650:                                        rp++;
                    651:                                }
                    652:
1.45      espie     653:                                if (n >= pat->nsub) {
1.23      espie     654:                                        Error("No subexpression %s",
1.22      espie     655:                                            &errstr[0]);
                    656:                                        subbuf = "";
                    657:                                        sublen = 0;
                    658:                                } else if (pat->matches[n].rm_so == -1 &&
                    659:                                    pat->matches[n].rm_eo == -1) {
1.23      espie     660:                                        Error("No match for subexpression %s",
1.22      espie     661:                                            &errstr[0]);
                    662:                                        subbuf = "";
                    663:                                        sublen = 0;
                    664:                                } else {
                    665:                                        subbuf = wp + pat->matches[n].rm_so;
1.23      espie     666:                                        sublen = pat->matches[n].rm_eo -
1.22      espie     667:                                            pat->matches[n].rm_so;
                    668:                                }
                    669:
                    670:                                if (sublen > 0) {
                    671:                                        MAYBE_ADD_SPACE();
                    672:                                        Buf_AddChars(buf, sublen, subbuf);
                    673:                                }
                    674:                        } else {
                    675:                                MAYBE_ADD_SPACE();
                    676:                                Buf_AddChar(buf, *rp);
                    677:                        }
                    678:                }
                    679:                wp += pat->matches[0].rm_eo;
1.32      espie     680:                if (pat->flags & VAR_SUB_GLOBAL) {
                    681:                        /* like most modern tools, empty string matches
                    682:                         * should advance one char at a time...
                    683:                         */
                    684:                        if (pat->matches[0].rm_eo == 0)  {
                    685:                                if (*wp) {
                    686:                                        MAYBE_ADD_SPACE();
                    687:                                        Buf_AddChar(buf, *wp++);
                    688:                                } else
                    689:                                        break;
                    690:                        }
1.22      espie     691:                        goto tryagain;
1.32      espie     692:                }
1.22      espie     693:                if (*wp) {
                    694:                        MAYBE_ADD_SPACE();
                    695:                        Buf_AddString(buf, wp);
1.1       espie     696:                }
1.22      espie     697:                break;
                    698:        default:
                    699:                VarREError(xrv, &pat->re, "Unexpected regex error");
1.25      deraadt   700:               /* FALLTHROUGH */
1.22      espie     701:        case REG_NOMATCH:
                    702:                if (*wp) {
                    703:                        MAYBE_ADD_SPACE();
                    704:                        Buf_AddString(buf, wp);
1.1       espie     705:                }
1.22      espie     706:                break;
1.1       espie     707:        }
1.22      espie     708:        return addSpace||added;
1.1       espie     709: }
                    710:
                    711: /*-
                    712:  *-----------------------------------------------------------------------
                    713:  * VarModify --
                    714:  *     Modify each of the words of the passed string using the given
1.49    ! espie     715:  *     function. Used to implement most modifiers.
1.1       espie     716:  *
                    717:  * Results:
                    718:  *     A string of all the words modified appropriately.
                    719:  *-----------------------------------------------------------------------
                    720:  */
                    721: static char *
1.13      espie     722: VarModify(char *str,           /* String whose words should be trimmed */
1.7       espie     723:                                /* Function to use to modify them */
1.23      espie     724:     bool (*modProc)(struct Name *, bool, Buffer, void *),
1.13      espie     725:     void *datum)               /* Datum to pass it */
1.1       espie     726: {
1.22      espie     727:        BUFFER    buf;          /* Buffer for the new string */
                    728:        bool      addSpace;     /* true if need to add a space to the
                    729:                                     * buffer before adding the trimmed
                    730:                                     * word */
                    731:        struct Name       word;
                    732:
                    733:        Buf_Init(&buf, 0);
                    734:        addSpace = false;
                    735:
                    736:        word.e = str;
                    737:
                    738:        while ((word.s = iterate_words(&word.e)) != NULL) {
                    739:                char termc;
                    740:
                    741:                termc = *word.e;
                    742:                *((char *)(word.e)) = '\0';
                    743:                addSpace = (*modProc)(&word, addSpace, &buf, datum);
                    744:                *((char *)(word.e)) = termc;
                    745:        }
                    746:        return Buf_Retrieve(&buf);
1.1       espie     747: }
                    748:
                    749: /*-
                    750:  *-----------------------------------------------------------------------
                    751:  * VarGetPattern --
                    752:  *     Pass through the tstr looking for 1) escaped delimiters,
                    753:  *     '$'s and backslashes (place the escaped character in
                    754:  *     uninterpreted) and 2) unescaped $'s that aren't before
                    755:  *     the delimiter (expand the variable substitution).
                    756:  *     Return the expanded string or NULL if the delimiter was missing
                    757:  *     If pattern is specified, handle escaped ampersands, and replace
                    758:  *     unescaped ampersands with the lhs of the pattern.
                    759:  *
                    760:  * Results:
                    761:  *     A string of all the words modified appropriately.
                    762:  *     If length is specified, return the string length of the buffer
                    763:  *-----------------------------------------------------------------------
                    764:  */
                    765: static char *
1.23      espie     766: VarGetPattern(SymTable *ctxt, int err, const char **tstr, int delim1,
1.13      espie     767:     int delim2, size_t *length, VarPattern *pattern)
1.1       espie     768: {
1.22      espie     769:        const char      *cp;
                    770:        char    *result;
                    771:        BUFFER  buf;
                    772:        size_t  junk;
                    773:
                    774:        Buf_Init(&buf, 0);
                    775:        if (length == NULL)
                    776:                length = &junk;
1.1       espie     777:
1.7       espie     778: #define IS_A_MATCH(cp, delim1, delim2) \
1.22      espie     779:        (cp[0] == '\\' && (cp[1] == delim1 || cp[1] == delim2 || \
                    780:         cp[1] == '\\' || cp[1] == '$' || (pattern && cp[1] == '&')))
1.1       espie     781:
1.22      espie     782:        /*
                    783:         * Skim through until the matching delimiter is found;
                    784:         * pick up variable substitutions on the way. Also allow
                    785:         * backslashes to quote the delimiter, $, and \, but don't
                    786:         * touch other backslashes.
                    787:         */
                    788:        for (cp = *tstr; *cp != '\0' && *cp != delim1 && *cp != delim2; cp++) {
                    789:                if (IS_A_MATCH(cp, delim1, delim2)) {
                    790:                        Buf_AddChar(&buf, cp[1]);
                    791:                        cp++;
                    792:                } else if (*cp == '$') {
                    793:                        /* Allowed at end of pattern */
                    794:                        if (cp[1] == delim1 || cp[1] == delim2)
                    795:                                Buf_AddChar(&buf, *cp);
                    796:                        else {
                    797:                                size_t len;
                    798:
                    799:                                /* If unescaped dollar sign not before the
                    800:                                 * delimiter, assume it's a variable
                    801:                                 * substitution and recurse.  */
1.23      espie     802:                                (void)Var_ParseBuffer(&buf, cp, ctxt, err,
1.22      espie     803:                                    &len);
                    804:                                cp += len - 1;
                    805:                        }
                    806:                } else if (pattern && *cp == '&')
                    807:                        Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
                    808:                else
                    809:                        Buf_AddChar(&buf, *cp);
                    810:        }
1.1       espie     811:
1.22      espie     812:        *length = Buf_Size(&buf);
                    813:        result = Buf_Retrieve(&buf);
1.7       espie     814:
1.22      espie     815:        if (*cp != delim1 && *cp != delim2) {
                    816:                *tstr = cp;
                    817:                *length = 0;
                    818:                free(result);
                    819:                return NULL;
                    820:        }
                    821:        else {
                    822:                *tstr = ++cp;
                    823:                return result;
                    824:        }
1.7       espie     825: }
                    826:
                    827: /*-
                    828:  *-----------------------------------------------------------------------
                    829:  * VarQuote --
                    830:  *     Quote shell meta-characters in the string
                    831:  *
                    832:  * Results:
                    833:  *     The quoted string
                    834:  *-----------------------------------------------------------------------
                    835:  */
                    836: static char *
1.26      espie     837: VarQuote(const char *str, const struct Name *n UNUSED, void *islistp)
1.7       espie     838: {
1.40      espie     839:        int *p = islistp;
                    840:        int islist = *p;
1.7       espie     841:
1.22      espie     842:        BUFFER    buf;
                    843:        /* This should cover most shells :-( */
                    844:        static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.26      espie     845:        char *rep = meta;
                    846:        if (islist)
                    847:                rep += 3;
1.22      espie     848:
                    849:        Buf_Init(&buf, MAKE_BSIZE);
                    850:        for (; *str; str++) {
1.26      espie     851:                if (strchr(rep, *str) != NULL)
1.22      espie     852:                        Buf_AddChar(&buf, '\\');
                    853:                Buf_AddChar(&buf, *str);
                    854:        }
                    855:        return Buf_Retrieve(&buf);
1.7       espie     856: }
                    857:
                    858: static void *
1.13      espie     859: check_empty(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7       espie     860: {
1.22      espie     861:        dummy_arg->s = NULL;
                    862:        if ((*p)[1] == endc || (*p)[1] == ':') {
                    863:                (*p)++;
                    864:                return dummy_arg;
1.26      espie     865:        } else
                    866:                return NULL;
                    867: }
                    868:
                    869: static void *
                    870: check_quote(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
                    871: {
                    872:        int *qargs = emalloc(sizeof(int));
                    873:        *qargs = 0;
                    874:        if ((*p)[1] == 'L') {
                    875:                *qargs = 1;
                    876:                (*p)++;
                    877:        }
                    878:        if ((*p)[1] == endc || (*p)[1] == ':') {
                    879:                (*p)++;
                    880:                return qargs;
1.39      espie     881:        } else  {
                    882:                free(qargs);
1.22      espie     883:                return NULL;
1.39      espie     884:        }
1.7       espie     885: }
                    886:
                    887: static void *
1.13      espie     888: check_shcmd(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7       espie     889: {
1.22      espie     890:        if ((*p)[1] == 'h' && ((*p)[2] == endc || (*p)[2] == ':')) {
                    891:                (*p)+=2;
                    892:                return dummy_arg;
                    893:        } else
                    894:                return NULL;
1.7       espie     895: }
                    896:
                    897:
                    898: static char *
1.13      espie     899: do_shcmd(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7       espie     900: {
1.22      espie     901:        char *err;
                    902:        char *t;
1.7       espie     903:
1.22      espie     904:        t = Cmd_Exec(s, &err);
                    905:        if (err)
                    906:                Error(err, s);
                    907:        return t;
1.7       espie     908: }
                    909:
                    910: static void *
1.13      espie     911: get_stringarg(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
1.7       espie     912: {
1.22      espie     913:        const char *cp;
                    914:        char *s;
1.7       espie     915:
1.22      espie     916:        for (cp = *p + 1; *cp != ':' && *cp != endc; cp++) {
                    917:                if (*cp == '\\') {
                    918:                        if (cp[1] == ':' || cp[1] == endc || cp[1] == '\\')
                    919:                                cp++;
                    920:                } else if (*cp == '\0')
                    921:                        return NULL;
                    922:        }
                    923:        s = escape_dupi(*p+1, cp, ":)}");
                    924:        *p = cp;
                    925:        return s;
1.7       espie     926: }
                    927:
                    928: static void
1.13      espie     929: free_stringarg(void *arg)
1.7       espie     930: {
1.22      espie     931:        free(arg);
1.7       espie     932: }
                    933:
                    934: static char *
1.13      espie     935: do_upper(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7       espie     936: {
1.22      espie     937:        size_t len, i;
                    938:        char *t;
1.7       espie     939:
1.22      espie     940:        len = strlen(s);
                    941:        t = emalloc(len+1);
                    942:        for (i = 0; i < len; i++)
1.35      espie     943:                t[i] = TOUPPER(s[i]);
1.22      espie     944:        t[len] = '\0';
                    945:        return t;
1.7       espie     946: }
                    947:
                    948: static char *
1.13      espie     949: do_lower(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
1.7       espie     950: {
1.22      espie     951:        size_t  len, i;
                    952:        char    *t;
1.7       espie     953:
1.22      espie     954:        len = strlen(s);
                    955:        t = emalloc(len+1);
                    956:        for (i = 0; i < len; i++)
1.35      espie     957:                t[i] = TOLOWER(s[i]);
1.22      espie     958:        t[len] = '\0';
                    959:        return t;
1.1       espie     960: }
                    961:
1.10      espie     962: static void *
1.13      espie     963: get_patternarg(const char **p, SymTable *ctxt, bool err, int endc)
1.10      espie     964: {
1.22      espie     965:        return common_get_patternarg(p, ctxt, err, endc, false);
1.10      espie     966: }
                    967:
1.7       espie     968: /* Extract anchors */
                    969: static void *
1.13      espie     970: get_spatternarg(const char **p, SymTable *ctxt, bool err, int endc)
1.7       espie     971: {
1.48      tb        972:        return common_get_patternarg(p, ctxt, err, endc, true);
1.7       espie     973: }
                    974:
                    975: static void *
1.23      espie     976: common_get_patternarg(const char **p, SymTable *ctxt, bool err, int endc,
1.13      espie     977:     bool dosubst)
1.7       espie     978: {
1.22      espie     979:        VarPattern *pattern;
                    980:        char delim;
                    981:        const char *s;
                    982:
1.42      deraadt   983:        pattern = emalloc(sizeof(VarPattern));
1.22      espie     984:        pattern->flags = 0;
                    985:        s = *p;
1.1       espie     986:
1.22      espie     987:        delim = s[1];
                    988:        if (delim == '\0')
                    989:                return NULL;
                    990:        s += 2;
1.1       espie     991:
1.22      espie     992:        pattern->rhs = NULL;
1.23      espie     993:        pattern->lhs = VarGetPattern(ctxt, err, &s, delim, delim,
1.22      espie     994:            &pattern->leftLen, NULL);
                    995:        pattern->lbuffer = pattern->lhs;
                    996:        if (pattern->lhs != NULL) {
1.48      tb        997:                if (dosubst && pattern->leftLen > 0) {
                    998:                        if (pattern->lhs[pattern->leftLen-1] == '$') {
                    999:                                    pattern->leftLen--;
                   1000:                                    pattern->flags |= VAR_MATCH_END;
                   1001:                        }
                   1002:                        if (pattern->lhs[0] == '^') {
                   1003:                                    pattern->lhs++;
                   1004:                                    pattern->leftLen--;
                   1005:                                    pattern->flags |= VAR_MATCH_START;
                   1006:                        }
                   1007:                }
1.22      espie    1008:                pattern->rhs = VarGetPattern(ctxt, err, &s, delim, delim,
                   1009:                    &pattern->rightLen, dosubst ? pattern: NULL);
                   1010:                if (pattern->rhs != NULL) {
                   1011:                        /* Check for global substitution. If 'g' after the
                   1012:                         * final delimiter, substitution is global and is
                   1013:                         * marked that way.  */
                   1014:                        for (;; s++) {
                   1015:                                switch (*s) {
                   1016:                                case 'g':
                   1017:                                        pattern->flags |= VAR_SUB_GLOBAL;
                   1018:                                        continue;
                   1019:                                case '1':
                   1020:                                        pattern->flags |= VAR_SUB_ONE;
                   1021:                                        continue;
                   1022:                                }
                   1023:                                break;
                   1024:                        }
                   1025:                        if (*s == endc || *s == ':') {
                   1026:                                *p = s;
                   1027:                                return pattern;
                   1028:                        }
1.1       espie    1029:                }
1.7       espie    1030:        }
1.22      espie    1031:        free_patternarg(pattern);
                   1032:        return NULL;
1.7       espie    1033: }
                   1034:
                   1035: static void
1.13      espie    1036: free_patternarg(void *p)
1.7       espie    1037: {
1.40      espie    1038:        VarPattern *vp = p;
1.8       espie    1039:
1.22      espie    1040:        free(vp->lbuffer);
                   1041:        free(vp->rhs);
                   1042:        free(vp);
1.1       espie    1043: }
                   1044:
                   1045: static char *
1.13      espie    1046: do_regex(const char *s, const struct Name *n UNUSED, void *arg)
1.7       espie    1047: {
1.22      espie    1048:        VarREPattern p2;
1.40      espie    1049:        VarPattern *p = arg;
1.22      espie    1050:        int error;
                   1051:        char *result;
                   1052:
                   1053:        error = regcomp(&p2.re, p->lhs, REG_EXTENDED);
                   1054:        if (error) {
                   1055:                VarREError(error, &p2.re, "RE substitution error");
                   1056:                return var_Error;
                   1057:        }
                   1058:        p2.nsub = p2.re.re_nsub + 1;
                   1059:        p2.replace = p->rhs;
                   1060:        p2.flags = p->flags;
                   1061:        if (p2.nsub < 1)
                   1062:                p2.nsub = 1;
                   1063:        if (p2.nsub > 10)
                   1064:                p2.nsub = 10;
1.38      espie    1065:        p2.matches = ereallocarray(NULL, p2.nsub, sizeof(regmatch_t));
1.22      espie    1066:        result = VarModify((char *)s, VarRESubstitute, &p2);
                   1067:        regfree(&p2.re);
                   1068:        free(p2.matches);
                   1069:        return result;
1.7       espie    1070: }
                   1071:
                   1072: char *
1.23      espie    1073: VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
1.18      espie    1074:     bool err, bool *freePtr, const char **pscan, int paren)
1.1       espie    1075: {
1.22      espie    1076:        const char *tstr;
                   1077:        char endc = paren == '(' ? ')' : '}';
                   1078:        const char *start = *pscan;
                   1079:
                   1080:        tstr = start;
                   1081:        /*
                   1082:         * Now we need to apply any modifiers the user wants applied.
                   1083:         * These are:
                   1084:         *                :M<pattern>   words which match the given <pattern>.
                   1085:         *                              <pattern> is of the standard file
                   1086:         *                              wildcarding form.
                   1087:         *                :S<d><pat1><d><pat2><d>[g]
                   1088:         *                              Substitute <pat2> for <pat1> in the
                   1089:         *                              value
                   1090:         *                :C<d><pat1><d><pat2><d>[g]
                   1091:         *                              Substitute <pat2> for regex <pat1> in
                   1092:         *                              the value
                   1093:         *                :H            Substitute the head of each word
                   1094:         *                :T            Substitute the tail of each word
                   1095:         *                :E            Substitute the extension (minus '.') of
                   1096:         *                              each word
                   1097:         *                :R            Substitute the root of each word
                   1098:         *                              (pathname minus the suffix).
                   1099:         *                :lhs=rhs      Like :S, but the rhs goes to the end of
                   1100:         *                              the invocation.
                   1101:         */
1.24      espie    1102:
1.22      espie    1103:        while (*tstr != endc && *tstr != '\0') {
                   1104:                struct modifier *mod;
                   1105:                void *arg;
                   1106:                char *newStr;
                   1107:
                   1108:                tstr++;
1.44      tb       1109:                if (DEBUG(VAR)) {
                   1110:                        if (str != NULL)
                   1111:                                printf("Applying :%c to \"%s\"\n", *tstr, str);
                   1112:                        else
                   1113:                                printf("Applying :%c\n", *tstr);
                   1114:                }
1.22      espie    1115:
1.34      deraadt  1116:                mod = choose_mod[(unsigned char)*tstr];
1.22      espie    1117:                arg = NULL;
                   1118:
1.49    ! espie    1119:                if (mod != NULL)
1.22      espie    1120:                        arg = mod->getarg(&tstr, ctxt, err, endc);
1.49    ! espie    1121:                if (arg == NULL) {
1.22      espie    1122:                        mod = &sysv_mod;
                   1123:                        arg = mod->getarg(&tstr, ctxt, err, endc);
                   1124:                }
                   1125:                if (arg != NULL) {
1.49    ! espie    1126:                        if (str != NULL) {
1.22      espie    1127:                                if (mod->word_apply != NULL) {
1.23      espie    1128:                                        newStr = VarModify(str,
1.22      espie    1129:                                            mod->word_apply, arg);
1.49    ! espie    1130:                                        assert(mod->apply == NULL);
1.22      espie    1131:                                } else
                   1132:                                        newStr = mod->apply(str, name, arg);
                   1133:                                if (*freePtr)
                   1134:                                        free(str);
                   1135:                                str = newStr;
                   1136:                                if (str != var_Error)
                   1137:                                        *freePtr = true;
                   1138:                                else
                   1139:                                        *freePtr = false;
                   1140:                        }
                   1141:                        if (mod->freearg != NULL)
                   1142:                                mod->freearg(arg);
                   1143:                } else {
1.31      espie    1144:                        Error("Bad modifier: %s", tstr);
1.22      espie    1145:                        /* Try skipping to end of var... */
1.47      bluhm    1146:                        while (*tstr != endc && *tstr != '\0')
1.22      espie    1147:                                tstr++;
                   1148:                        if (str != NULL && *freePtr)
                   1149:                                free(str);
                   1150:                        str = var_Error;
                   1151:                        *freePtr = false;
                   1152:                        break;
                   1153:                }
1.44      tb       1154:                if (DEBUG(VAR) && str != NULL)
1.22      espie    1155:                        printf("Result is \"%s\"\n", str);
1.7       espie    1156:        }
1.22      espie    1157:        if (*tstr == '\0')
1.33      espie    1158:                Parse_Error(PARSE_FATAL, "Unclosed variable specification");
1.22      espie    1159:        else
1.7       espie    1160:                tstr++;
                   1161:
1.22      espie    1162:        *pscan = tstr;
                   1163:        return str;
1.1       espie    1164: }
1.7       espie    1165:
1.1       espie    1166: char *
1.13      espie    1167: Var_GetHead(char *s)
1.1       espie    1168: {
1.22      espie    1169:        return VarModify(s, VarHead, NULL);
1.1       espie    1170: }
                   1171:
                   1172: char *
1.13      espie    1173: Var_GetTail(char *s)
1.1       espie    1174: {
1.22      espie    1175:        return VarModify(s, VarTail, NULL);
1.1       espie    1176: }