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

1.2     ! espie       1: /*     $OpenBSD: varmodifiers.c,v 1.1 2000/07/17 23:09:06 espie Exp $  */
1.1       espie       2: /*     $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $        */
                      3:
                      4: /*
                      5:  * Copyright (c) 1999 Marc 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.
                     47:  * 3. All advertising materials mentioning features or use of this software
                     48:  *    must display the following acknowledgement:
                     49:  *     This product includes software developed by the University of
                     50:  *     California, Berkeley and its contributors.
                     51:  * 4. Neither the name of the University nor the names of its contributors
                     52:  *    may be used to endorse or promote products derived from this software
                     53:  *    without specific prior written permission.
                     54:  *
                     55:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     56:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     57:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     58:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     59:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     60:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     61:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     62:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     63:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     64:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     65:  * SUCH DAMAGE.
                     66:  */
                     67:
                     68: /* VarModifiers_Apply is mostly a constituent function of Var_Parse.  */
                     69:
                     70: #include    <ctype.h>
                     71: #ifndef MAKE_BOOTSTRAP
                     72: #include    <sys/types.h>
                     73: #include    <regex.h>
                     74: #endif
                     75: #include "make.h"
                     76: #include "buf.h"
                     77: #include "varmodifiers.h"
                     78:
                     79: /* Var*Pattern flags */
                     80: #define VAR_SUB_GLOBAL 0x01    /* Apply substitution globally */
                     81: #define VAR_SUB_ONE    0x02    /* Apply substitution to one word */
                     82: #define VAR_SUB_MATCHED        0x04    /* There was a match */
                     83: #define VAR_MATCH_START        0x08    /* Match at start of word */
                     84: #define VAR_MATCH_END  0x10    /* Match at end of word */
                     85:
                     86: typedef struct {
                     87:     char         *lhs;     /* String to match */
                     88:     size_t       leftLen;  /* Length of string */
                     89:     char         *rhs;     /* Replacement string (w/ &'s removed) */
                     90:     size_t       rightLen; /* Length of replacement */
                     91:     int                  flags;
                     92: } VarPattern;
                     93:
                     94: #ifndef MAKE_BOOTSTRAP
                     95: typedef struct {
                     96:     regex_t      re;
                     97:     int                  nsub;
                     98:     regmatch_t  *matches;
                     99:     char        *replace;
                    100:     int                  flags;
                    101: } VarREPattern;
                    102: #endif
                    103:
                    104: static Boolean VarHead __P((const char *, Boolean, Buffer, void *));
                    105: static Boolean VarTail __P((const char *, Boolean, Buffer, void *));
                    106: static Boolean VarSuffix __P((const char *, Boolean, Buffer, void *));
                    107: static Boolean VarRoot __P((const char *, Boolean, Buffer, void *));
                    108: static Boolean VarMatch __P((const char *, Boolean, Buffer, void *));
                    109: #ifdef SYSVVARSUB
                    110: static Boolean VarSYSVMatch __P((const char *, Boolean, Buffer, void *));
                    111: #endif
                    112: static Boolean VarNoMatch __P((const char *, Boolean, Buffer, void *));
                    113: #ifndef MAKE_BOOTSTRAP
                    114: static void VarREError __P((int, regex_t *, const char *));
                    115: static Boolean VarRESubstitute __P((const char *, Boolean, Buffer, void *));
                    116: #endif
                    117: static Boolean VarSubstitute __P((const char *, Boolean, Buffer, void *));
                    118: static char *VarGetPattern __P((SymTable *, int, char **, int, int *, size_t *,
                    119:                                VarPattern *));
                    120: static char *VarQuote __P((const char *));
                    121: static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));
                    122: static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *));
                    123: static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *));
                    124:
                    125: /*-
                    126:  *-----------------------------------------------------------------------
                    127:  * VarUppercase --
                    128:  *     Place the Upper cased word in the given buffer.
                    129:  *
                    130:  * Results:
                    131:  *     TRUE if characters were added to the buffer (a space needs to be
                    132:  *     added to the buffer before the next word).
                    133:  *
                    134:  * Side Effects:
                    135:  *     The word is added to the buffer.
                    136:  *
                    137:  *-----------------------------------------------------------------------
                    138:  */
                    139: static Boolean
                    140: VarUppercase(word, addSpace, buf, dummy)
                    141:     const char    *word;       /* Word to Upper Case */
                    142:     Boolean      addSpace;     /* True if need to add a space to the buffer
                    143:                                 * before sticking in the head */
                    144:     Buffer       buf;          /* Buffer in which to store it */
                    145:     void *dummy;
                    146: {
                    147:     size_t len = strlen(word);
                    148:
                    149:     if (addSpace)
                    150:        Buf_AddSpace(buf);
                    151:     while (len--)
                    152:        Buf_AddChar(buf, toupper(*word++));
                    153:     return TRUE;
                    154: }
                    155:
                    156: /*-
                    157:  *-----------------------------------------------------------------------
                    158:  * VarLowercase --
                    159:  *     Place the Lower cased word in the given buffer.
                    160:  *
                    161:  * Results:
                    162:  *     TRUE if characters were added to the buffer (a space needs to be
                    163:  *     added to the buffer before the next word).
                    164:  *
                    165:  * Side Effects:
                    166:  *     The word is added to the buffer.
                    167:  *
                    168:  *-----------------------------------------------------------------------
                    169:  */
                    170: static Boolean
                    171: VarLowercase(word, addSpace, buf, dummy)
                    172:     const char    *word;       /* Word to Lower Case */
                    173:     Boolean      addSpace;     /* True if need to add a space to the buffer
                    174:                                 * before sticking in the head */
                    175:     Buffer       buf;          /* Buffer in which to store it */
                    176:     void *dummy;
                    177: {
                    178:     size_t len = strlen(word);
                    179:
                    180:     if (addSpace)
                    181:        Buf_AddSpace(buf);
                    182:     while (len--)
                    183:        Buf_AddChar(buf, tolower(*word++));
                    184:     return TRUE;
                    185: }
                    186:
                    187: /*-
                    188:  *-----------------------------------------------------------------------
                    189:  * VarHead --
                    190:  *     Remove the tail of the given word and place the result in the given
                    191:  *     buffer.
                    192:  *
                    193:  * Results:
                    194:  *     TRUE if characters were added to the buffer (a space needs to be
                    195:  *     added to the buffer before the next word).
                    196:  *
                    197:  * Side Effects:
                    198:  *     The trimmed word is added to the buffer.
                    199:  *
                    200:  *-----------------------------------------------------------------------
                    201:  */
                    202: static Boolean
                    203: VarHead(word, addSpace, buf, dummy)
                    204:     const char    *word;       /* Word to trim */
                    205:     Boolean      addSpace;     /* True if need to add a space to the buffer
                    206:                                 * before sticking in the head */
                    207:     Buffer       buf;          /* Buffer in which to store it */
                    208:     void         *dummy;
                    209: {
                    210:     const char           *slash;
                    211:
                    212:     slash = strrchr(word, '/');
                    213:     if (slash != NULL) {
                    214:        if (addSpace)
                    215:            Buf_AddSpace(buf);
                    216:        Buf_AddInterval(buf, word, slash);
                    217:        return TRUE;
                    218:     } else {
                    219:        /* If no directory part, give . (q.v. the POSIX standard) */
                    220:        if (addSpace)
                    221:            Buf_AddString(buf, " .");
                    222:        else
                    223:            Buf_AddChar(buf, '.');
                    224:     }
                    225:     return(dummy ? TRUE : TRUE);
                    226: }
                    227:
                    228: /*-
                    229:  *-----------------------------------------------------------------------
                    230:  * VarTail --
                    231:  *     Remove the head of the given word and place the result in the given
                    232:  *     buffer.
                    233:  *
                    234:  * Results:
                    235:  *     TRUE if characters were added to the buffer (a space needs to be
                    236:  *     added to the buffer before the next word).
                    237:  *
                    238:  * Side Effects:
                    239:  *     The trimmed word is added to the buffer.
                    240:  *
                    241:  *-----------------------------------------------------------------------
                    242:  */
                    243: static Boolean
                    244: VarTail(word, addSpace, buf, dummy)
                    245:     const char    *word;       /* Word to trim */
                    246:     Boolean      addSpace;     /* TRUE if need to stick a space in the
                    247:                                 * buffer before adding the tail */
                    248:     Buffer       buf;          /* Buffer in which to store it */
                    249:     void         *dummy;
                    250: {
                    251:     const char *slash;
                    252:
                    253:     if (addSpace)
                    254:        Buf_AddSpace(buf);
                    255:     slash = strrchr(word, '/');
                    256:     if (slash != NULL)
                    257:        Buf_AddString(buf, slash+1);
                    258:     else
                    259:        Buf_AddString(buf, word);
                    260:     return (dummy ? TRUE : TRUE);
                    261: }
                    262:
                    263: /*-
                    264:  *-----------------------------------------------------------------------
                    265:  * VarSuffix --
                    266:  *     Place the suffix of the given word in the given buffer.
                    267:  *
                    268:  * Results:
                    269:  *     TRUE if characters were added to the buffer (a space needs to be
                    270:  *     added to the buffer before the next word).
                    271:  *
                    272:  * Side Effects:
                    273:  *     The suffix from the word is placed in the buffer.
                    274:  *
                    275:  *-----------------------------------------------------------------------
                    276:  */
                    277: static Boolean
                    278: VarSuffix(word, addSpace, buf, dummy)
                    279:     const char    *word;       /* Word to trim */
                    280:     Boolean      addSpace;     /* TRUE if need to add a space before placing
                    281:                                 * the suffix in the buffer */
                    282:     Buffer       buf;          /* Buffer in which to store it */
                    283:     void         *dummy;
                    284: {
                    285:     const char *dot;
                    286:
                    287:     dot = strrchr(word, '.');
                    288:     if (dot != NULL) {
                    289:        if (addSpace)
                    290:            Buf_AddSpace(buf);
                    291:        Buf_AddString(buf, dot+1);
                    292:        addSpace = TRUE;
                    293:     }
                    294:     return (dummy ? addSpace : addSpace);
                    295: }
                    296:
                    297: /*-
                    298:  *-----------------------------------------------------------------------
                    299:  * VarRoot --
                    300:  *     Remove the suffix of the given word and place the result in the
                    301:  *     buffer.
                    302:  *
                    303:  * Results:
                    304:  *     TRUE if characters were added to the buffer (a space needs to be
                    305:  *     added to the buffer before the next word).
                    306:  *
                    307:  * Side Effects:
                    308:  *     The trimmed word is added to the buffer.
                    309:  *
                    310:  *-----------------------------------------------------------------------
                    311:  */
                    312: static Boolean
                    313: VarRoot(word, addSpace, buf, dummy)
                    314:     const char           *word;        /* Word to trim */
                    315:     Boolean      addSpace;     /* TRUE if need to add a space to the buffer
                    316:                                 * before placing the root in it */
                    317:     Buffer       buf;          /* Buffer in which to store it */
                    318:     void         *dummy;
                    319: {
                    320:     const char *dot;
                    321:
                    322:     if (addSpace)
                    323:        Buf_AddSpace(buf);
                    324:
                    325:     dot = strrchr(word, '.');
                    326:     if (dot != NULL)
                    327:        Buf_AddInterval(buf, word, dot);
                    328:     else
                    329:        Buf_AddString(buf, word);
                    330:     return (dummy ? TRUE : TRUE);
                    331: }
                    332:
                    333: /*-
                    334:  *-----------------------------------------------------------------------
                    335:  * VarMatch --
                    336:  *     Place the word in the buffer if it matches the given pattern.
                    337:  *     Callback function for VarModify to implement the :M modifier.
                    338:  *
                    339:  * Results:
                    340:  *     TRUE if a space should be placed in the buffer before the next
                    341:  *     word.
                    342:  *
                    343:  * Side Effects:
                    344:  *     The word may be copied to the buffer.
                    345:  *
                    346:  *-----------------------------------------------------------------------
                    347:  */
                    348: static Boolean
                    349: VarMatch(word, addSpace, buf, pattern)
                    350:     const char    *word;       /* Word to examine */
                    351:     Boolean      addSpace;     /* TRUE if need to add a space to the
                    352:                                 * buffer before adding the word, if it
                    353:                                 * matches */
                    354:     Buffer       buf;          /* Buffer in which to store it */
                    355:     void         *pattern;     /* Pattern the word must match */
                    356: {
                    357:     if (Str_Match(word, (char *) pattern)) {
                    358:        if (addSpace)
                    359:            Buf_AddSpace(buf);
                    360:        addSpace = TRUE;
                    361:        Buf_AddString(buf, word);
                    362:     }
                    363:     return addSpace;
                    364: }
                    365:
                    366: #ifdef SYSVVARSUB
                    367: /*-
                    368:  *-----------------------------------------------------------------------
                    369:  * VarSYSVMatch --
                    370:  *     Place the word in the buffer if it matches the given pattern.
                    371:  *     Callback function for VarModify to implement the System V %
                    372:  *     modifiers.
                    373:  *
                    374:  * Results:
                    375:  *     TRUE if a space should be placed in the buffer before the next
                    376:  *     word.
                    377:  *
                    378:  * Side Effects:
                    379:  *     The word may be copied to the buffer.
                    380:  *
                    381:  *-----------------------------------------------------------------------
                    382:  */
                    383: static Boolean
                    384: VarSYSVMatch(word, addSpace, buf, patp)
                    385:     const char    *word;       /* Word to examine */
                    386:     Boolean      addSpace;     /* TRUE if need to add a space to the
                    387:                                 * buffer before adding the word, if it
                    388:                                 * matches */
                    389:     Buffer       buf;          /* Buffer in which to store it */
                    390:     void         *patp;        /* Pattern the word must match */
                    391: {
                    392:     size_t       len;
                    393:     const char    *ptr;
                    394:     VarPattern           *pat = (VarPattern *) patp;
                    395:
                    396:     if (*word) {
                    397:            if (addSpace)
                    398:                Buf_AddSpace(buf);
                    399:
                    400:            addSpace = TRUE;
                    401:
                    402:            if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
                    403:                Str_SYSVSubst(buf, pat->rhs, ptr, len);
                    404:            else
                    405:                Buf_AddString(buf, word);
                    406:     }
                    407:     return addSpace;
                    408: }
                    409: #endif
                    410:
                    411: /*-
                    412:  *-----------------------------------------------------------------------
                    413:  * VarNoMatch --
                    414:  *     Place the word in the buffer if it doesn't match the given pattern.
                    415:  *     Callback function for VarModify to implement the :N modifier.
                    416:  *
                    417:  * Results:
                    418:  *     TRUE if a space should be placed in the buffer before the next
                    419:  *     word.
                    420:  *
                    421:  * Side Effects:
                    422:  *     The word may be copied to the buffer.
                    423:  *
                    424:  *-----------------------------------------------------------------------
                    425:  */
                    426: static Boolean
                    427: VarNoMatch(word, addSpace, buf, pattern)
                    428:     const char    *word;       /* Word to examine */
                    429:     Boolean      addSpace;     /* TRUE if need to add a space to the
                    430:                                 * buffer before adding the word, if it
                    431:                                 * matches */
                    432:     Buffer       buf;          /* Buffer in which to store it */
                    433:     void         *pattern;     /* Pattern the word must match */
                    434: {
                    435:     if (!Str_Match(word, (char *) pattern)) {
                    436:        if (addSpace)
                    437:            Buf_AddSpace(buf);
                    438:        addSpace = TRUE;
                    439:        Buf_AddString(buf, word);
                    440:     }
                    441:     return(addSpace);
                    442: }
                    443:
                    444:
                    445: /*-
                    446:  *-----------------------------------------------------------------------
                    447:  * VarSubstitute --
                    448:  *     Perform a string-substitution on the given word, placing the
                    449:  *     result in the passed buffer.
                    450:  *
                    451:  * Results:
                    452:  *     TRUE if a space is needed before more characters are added.
                    453:  *
                    454:  * Side Effects:
                    455:  *     None.
                    456:  *
                    457:  *-----------------------------------------------------------------------
                    458:  */
                    459: static Boolean
                    460: VarSubstitute(word, addSpace, buf, patternp)
                    461:     const char                 *word;      /* Word to modify */
                    462:     Boolean            addSpace;   /* True if space should be added before
                    463:                                     * other characters */
                    464:     Buffer             buf;        /* Buffer for result */
                    465:     void               *patternp;  /* Pattern for substitution */
                    466: {
                    467:     size_t             wordLen;    /* Length of word */
                    468:     const char         *cp;                /* General pointer */
                    469:     VarPattern *pattern = (VarPattern *) patternp;
                    470:
                    471:     wordLen = strlen(word);
                    472:     if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) !=
                    473:        (VAR_SUB_ONE|VAR_SUB_MATCHED)) {
                    474:        /*
                    475:         * Still substituting -- break it down into simple anchored cases
                    476:         * and if none of them fits, perform the general substitution case.
                    477:         */
                    478:        if ((pattern->flags & VAR_MATCH_START) &&
                    479:            (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) {
                    480:                /*
                    481:                 * Anchored at start and beginning of word matches pattern
                    482:                 */
                    483:                if ((pattern->flags & VAR_MATCH_END) &&
                    484:                    (wordLen == pattern->leftLen)) {
                    485:                        /*
                    486:                         * Also anchored at end and matches to the end (word
                    487:                         * is same length as pattern) add space and rhs only
                    488:                         * if rhs is non-null.
                    489:                         */
                    490:                        if (pattern->rightLen != 0) {
                    491:                            if (addSpace)
                    492:                                Buf_AddSpace(buf);
                    493:                            addSpace = TRUE;
                    494:                            Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    495:                        }
                    496:                        pattern->flags |= VAR_SUB_MATCHED;
                    497:                } else if (pattern->flags & VAR_MATCH_END) {
                    498:                    /*
                    499:                     * Doesn't match to end -- copy word wholesale
                    500:                     */
                    501:                    goto nosub;
                    502:                } else {
                    503:                    /*
                    504:                     * Matches at start but need to copy in trailing characters
                    505:                     */
                    506:                    if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){
                    507:                        if (addSpace)
                    508:                            Buf_AddSpace(buf);
                    509:                        addSpace = TRUE;
                    510:                    }
                    511:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    512:                    Buf_AddChars(buf, wordLen - pattern->leftLen,
                    513:                                 word + pattern->leftLen);
                    514:                    pattern->flags |= VAR_SUB_MATCHED;
                    515:                }
                    516:        } else if (pattern->flags & VAR_MATCH_START) {
                    517:            /*
                    518:             * Had to match at start of word and didn't -- copy whole word.
                    519:             */
                    520:            goto nosub;
                    521:        } else if (pattern->flags & VAR_MATCH_END) {
                    522:            /*
                    523:             * Anchored at end, Find only place match could occur (leftLen
                    524:             * characters from the end of the word) and see if it does. Note
                    525:             * that because the $ will be left at the end of the lhs, we have
                    526:             * to use strncmp.
                    527:             */
                    528:            cp = word + (wordLen - pattern->leftLen);
                    529:            if ((cp >= word) &&
                    530:                (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) {
                    531:                /*
                    532:                 * Match found. If we will place characters in the buffer,
                    533:                 * add a space before hand as indicated by addSpace, then
                    534:                 * stuff in the initial, unmatched part of the word followed
                    535:                 * by the right-hand-side.
                    536:                 */
                    537:                if (((cp - word) + pattern->rightLen) != 0) {
                    538:                    if (addSpace)
                    539:                        Buf_AddSpace(buf);
                    540:                    addSpace = TRUE;
                    541:                }
                    542:                Buf_AddInterval(buf, word, cp);
                    543:                Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    544:                pattern->flags |= VAR_SUB_MATCHED;
                    545:            } else {
                    546:                /*
                    547:                 * Had to match at end and didn't. Copy entire word.
                    548:                 */
                    549:                goto nosub;
                    550:            }
                    551:        } else {
                    552:            /*
                    553:             * Pattern is unanchored: search for the pattern in the word using
                    554:             * String_FindSubstring, copying unmatched portions and the
                    555:             * right-hand-side for each match found, handling non-global
                    556:             * substitutions correctly, etc. When the loop is done, any
                    557:             * remaining part of the word (word and wordLen are adjusted
                    558:             * accordingly through the loop) is copied straight into the
                    559:             * buffer.
                    560:             * addSpace is set FALSE as soon as a space is added to the
                    561:             * buffer.
                    562:             */
                    563:            register Boolean done;
                    564:            size_t origSize;
                    565:
                    566:            done = FALSE;
                    567:            origSize = Buf_Size(buf);
                    568:            while (!done) {
                    569:                cp = strstr(word, pattern->lhs);
                    570:                if (cp != (char *)NULL) {
                    571:                    if (addSpace && (((cp - word) + pattern->rightLen) != 0)){
                    572:                        Buf_AddSpace(buf);
                    573:                        addSpace = FALSE;
                    574:                    }
                    575:                    Buf_AddInterval(buf, word, cp);
                    576:                    Buf_AddChars(buf, pattern->rightLen, pattern->rhs);
                    577:                    wordLen -= (cp - word) + pattern->leftLen;
                    578:                    word = cp + pattern->leftLen;
                    579:                    if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){
                    580:                        done = TRUE;
                    581:                    }
                    582:                    pattern->flags |= VAR_SUB_MATCHED;
                    583:                } else {
                    584:                    done = TRUE;
                    585:                }
                    586:            }
                    587:            if (wordLen != 0) {
                    588:                if (addSpace)
                    589:                    Buf_AddSpace(buf);
                    590:                Buf_AddChars(buf, wordLen, word);
                    591:            }
                    592:            /*
                    593:             * If added characters to the buffer, need to add a space
                    594:             * before we add any more. If we didn't add any, just return
                    595:             * the previous value of addSpace.
                    596:             */
                    597:            return (Buf_Size(buf) != origSize || addSpace);
                    598:        }
                    599:        return (addSpace);
                    600:     }
                    601:  nosub:
                    602:     if (addSpace)
                    603:        Buf_AddSpace(buf);
                    604:     Buf_AddChars(buf, wordLen, word);
                    605:     return(TRUE);
                    606: }
                    607:
                    608:
                    609: #ifndef MAKE_BOOTSTRAP
                    610: /*-
                    611:  *-----------------------------------------------------------------------
                    612:  * VarREError --
                    613:  *     Print the error caused by a regcomp or regexec call.
                    614:  *
                    615:  * Results:
                    616:  *     None.
                    617:  *
                    618:  * Side Effects:
                    619:  *     An error gets printed.
                    620:  *
                    621:  *-----------------------------------------------------------------------
                    622:  */
                    623: static void
                    624: VarREError(err, pat, str)
                    625:     int err;
                    626:     regex_t *pat;
                    627:     const char *str;
                    628: {
                    629:     char *errbuf;
                    630:     int errlen;
                    631:
                    632:     errlen = regerror(err, pat, 0, 0);
                    633:     errbuf = emalloc(errlen);
                    634:     regerror(err, pat, errbuf, errlen);
                    635:     Error("%s: %s", str, errbuf);
                    636:     free(errbuf);
                    637: }
                    638:
                    639: /*-
                    640:  *-----------------------------------------------------------------------
                    641:  * VarRESubstitute --
                    642:  *     Perform a regex substitution on the given word, placing the
                    643:  *     result in the passed buffer.
                    644:  *
                    645:  * Results:
                    646:  *     TRUE if a space is needed before more characters are added.
                    647:  *
                    648:  * Side Effects:
                    649:  *     None.
                    650:  *
                    651:  *-----------------------------------------------------------------------
                    652:  */
                    653: static Boolean
                    654: VarRESubstitute(word, addSpace, buf, patternp)
                    655:     const char *word;
                    656:     Boolean addSpace;
                    657:     Buffer buf;
                    658:     void *patternp;
                    659: {
                    660:     VarREPattern *pat;
                    661:     int xrv;
                    662:     const char *wp;
                    663:     char *rp;
                    664:     int added;
                    665:
                    666: #define MAYBE_ADD_SPACE()              \
                    667:        if (addSpace && !added)         \
                    668:            Buf_AddSpace(buf);          \
                    669:        added = 1
                    670:
                    671:     added = 0;
                    672:     wp = word;
                    673:     pat = patternp;
                    674:
                    675:     if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) ==
                    676:        (VAR_SUB_ONE|VAR_SUB_MATCHED))
                    677:        xrv = REG_NOMATCH;
                    678:     else {
                    679:     tryagain:
                    680:        xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0);
                    681:     }
                    682:
                    683:     switch (xrv) {
                    684:     case 0:
                    685:        pat->flags |= VAR_SUB_MATCHED;
                    686:        if (pat->matches[0].rm_so > 0) {
                    687:            MAYBE_ADD_SPACE();
                    688:            Buf_AddChars(buf, pat->matches[0].rm_so, wp);
                    689:        }
                    690:
                    691:        for (rp = pat->replace; *rp; rp++) {
                    692:            if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) {
                    693:                MAYBE_ADD_SPACE();
                    694:                Buf_AddChar(buf, rp[1]);
                    695:                rp++;
                    696:            }
                    697:            else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) {
                    698:                int n;
                    699:                const char *subbuf;
                    700:                int sublen;
                    701:                char errstr[3];
                    702:
                    703:                if (*rp == '&') {
                    704:                    n = 0;
                    705:                    errstr[0] = '&';
                    706:                    errstr[1] = '\0';
                    707:                } else {
                    708:                    n = rp[1] - '0';
                    709:                    errstr[0] = '\\';
                    710:                    errstr[1] = rp[1];
                    711:                    errstr[2] = '\0';
                    712:                    rp++;
                    713:                }
                    714:
                    715:                if (n > pat->nsub) {
                    716:                    Error("No subexpression %s", &errstr[0]);
                    717:                    subbuf = "";
                    718:                    sublen = 0;
                    719:                } else if ((pat->matches[n].rm_so == -1) &&
                    720:                           (pat->matches[n].rm_eo == -1)) {
                    721:                    Error("No match for subexpression %s", &errstr[0]);
                    722:                    subbuf = "";
                    723:                    sublen = 0;
                    724:                } else {
                    725:                    subbuf = wp + pat->matches[n].rm_so;
                    726:                    sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so;
                    727:                }
                    728:
                    729:                if (sublen > 0) {
                    730:                    MAYBE_ADD_SPACE();
                    731:                    Buf_AddChars(buf, sublen, subbuf);
                    732:                }
                    733:            } else {
                    734:                MAYBE_ADD_SPACE();
                    735:                Buf_AddChar(buf, *rp);
                    736:            }
                    737:        }
                    738:        wp += pat->matches[0].rm_eo;
                    739:        if (pat->flags & VAR_SUB_GLOBAL)
                    740:            goto tryagain;
                    741:        if (*wp) {
                    742:            MAYBE_ADD_SPACE();
                    743:            Buf_AddChars(buf, strlen(wp), wp);
                    744:        }
                    745:        break;
                    746:     default:
                    747:        VarREError(xrv, &pat->re, "Unexpected regex error");
                    748:        /* fall through */
                    749:     case REG_NOMATCH:
                    750:        if (*wp) {
                    751:            MAYBE_ADD_SPACE();
                    752:            Buf_AddChars(buf, strlen(wp), wp);
                    753:        }
                    754:        break;
                    755:     }
                    756:     return(addSpace||added);
                    757: }
                    758: #endif
                    759:
                    760: /*-
                    761:  *-----------------------------------------------------------------------
                    762:  * VarModify --
                    763:  *     Modify each of the words of the passed string using the given
                    764:  *     function. Used to implement all modifiers.
                    765:  *
                    766:  * Results:
                    767:  *     A string of all the words modified appropriately.
                    768:  *
                    769:  *-----------------------------------------------------------------------
                    770:  */
                    771: static char *
                    772: VarModify (str, modProc, datum)
                    773:     const char           *str;     /* String whose words should be trimmed */
                    774:                                    /* Function to use to modify them */
                    775:     Boolean              (*modProc) __P((const char *, Boolean, Buffer, void *));
                    776:     void          *datum;          /* Datum to pass it */
                    777: {
                    778:     BUFFER       buf;              /* Buffer for the new string */
                    779:     Boolean      addSpace;         /* TRUE if need to add a space to the
                    780:                                     * buffer before adding the trimmed
                    781:                                     * word */
                    782:     char         **av;             /* word list */
                    783:     char         *as;              /* word list memory */
                    784:     int ac, i;
                    785:
1.2     ! espie     786:     if (str == NULL)
        !           787:        return NULL;
        !           788:
1.1       espie     789:     Buf_Init(&buf, 0);
                    790:     addSpace = FALSE;
                    791:
                    792:     av = brk_string(str, &ac, FALSE, &as);
                    793:
                    794:     for (i = 0; i < ac; i++)
                    795:        addSpace = (*modProc)(av[i], addSpace, &buf, datum);
                    796:
                    797:     free(as);
                    798:     free(av);
                    799:     return Buf_Retrieve(&buf);
                    800: }
                    801:
                    802: /*-
                    803:  *-----------------------------------------------------------------------
                    804:  * VarGetPattern --
                    805:  *     Pass through the tstr looking for 1) escaped delimiters,
                    806:  *     '$'s and backslashes (place the escaped character in
                    807:  *     uninterpreted) and 2) unescaped $'s that aren't before
                    808:  *     the delimiter (expand the variable substitution).
                    809:  *     Return the expanded string or NULL if the delimiter was missing
                    810:  *     If pattern is specified, handle escaped ampersands, and replace
                    811:  *     unescaped ampersands with the lhs of the pattern.
                    812:  *
                    813:  * Results:
                    814:  *     A string of all the words modified appropriately.
                    815:  *     If length is specified, return the string length of the buffer
                    816:  *     If flags is specified and the last character of the pattern is a
                    817:  *     $ set the VAR_MATCH_END bit of flags.
                    818:  *
                    819:  * Side Effects:
                    820:  *     None.
                    821:  *-----------------------------------------------------------------------
                    822:  */
                    823: static char *
                    824: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
                    825:     SymTable *ctxt;
                    826:     int err;
                    827:     char **tstr;
                    828:     int delim;
                    829:     int *flags;
                    830:     size_t *length;
                    831:     VarPattern *pattern;
                    832: {
                    833:     char *cp;
                    834:     BUFFER buf;
                    835:     size_t junk;
                    836:
                    837:     Buf_Init(&buf, 0);
                    838:     if (length == NULL)
                    839:        length = &junk;
                    840:
                    841: #define IS_A_MATCH(cp, delim) \
                    842:     ((cp[0] == '\\') && ((cp[1] == delim) ||  \
                    843:      (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
                    844:
                    845:     /*
                    846:      * Skim through until the matching delimiter is found;
                    847:      * pick up variable substitutions on the way. Also allow
                    848:      * backslashes to quote the delimiter, $, and \, but don't
                    849:      * touch other backslashes.
                    850:      */
                    851:     for (cp = *tstr; *cp && (*cp != delim); cp++) {
                    852:        if (IS_A_MATCH(cp, delim)) {
                    853:            Buf_AddChar(&buf, cp[1]);
                    854:            cp++;
                    855:        } else if (*cp == '$') {
                    856:            if (cp[1] == delim) {
                    857:                if (flags == NULL)
                    858:                    Buf_AddChar(&buf, *cp);
                    859:                else
                    860:                    /*
                    861:                     * Unescaped $ at end of pattern => anchor
                    862:                     * pattern at end.
                    863:                     */
                    864:                    *flags |= VAR_MATCH_END;
                    865:            }
                    866:            else {
                    867:                char   *cp2;
                    868:                size_t     len;
                    869:                Boolean freeIt;
                    870:
                    871:                /*
                    872:                 * If unescaped dollar sign not before the
                    873:                 * delimiter, assume it's a variable
                    874:                 * substitution and recurse.
                    875:                 */
                    876:                cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
                    877:                Buf_AddString(&buf, cp2);
                    878:                if (freeIt)
                    879:                    free(cp2);
                    880:                cp += len - 1;
                    881:            }
                    882:        }
                    883:        else if (pattern && *cp == '&')
                    884:            Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
                    885:        else
                    886:            Buf_AddChar(&buf, *cp);
                    887:     }
                    888:
                    889:     if (*cp != delim) {
                    890:        *tstr = cp;
                    891:        *length = 0;
                    892:        return NULL;
                    893:     }
                    894:     else {
                    895:        *tstr = ++cp;
                    896:        *length = Buf_Size(&buf);
                    897:        return Buf_Retrieve(&buf);
                    898:     }
                    899: }
                    900:
                    901: char *
                    902: VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr)
                    903:     char       *str;
                    904:     SymTable   *ctxt;
                    905:     Boolean    err;
                    906:     Boolean    *freePtr;
                    907:     char       *start;
                    908:     char       endc;
                    909:     size_t     *lengthPtr;
                    910: {
                    911:     char       *tstr;
                    912:     char       delim;
                    913:     char       *cp;
                    914:
                    915:     tstr = start;
                    916:
                    917:     /*
                    918:      * Now we need to apply any modifiers the user wants applied.
                    919:      * These are:
                    920:      *           :M<pattern>   words which match the given <pattern>.
                    921:      *                         <pattern> is of the standard file
                    922:      *                         wildcarding form.
                    923:      *           :S<d><pat1><d><pat2><d>[g]
                    924:      *                         Substitute <pat2> for <pat1> in the value
                    925:      *           :C<d><pat1><d><pat2><d>[g]
                    926:      *                         Substitute <pat2> for regex <pat1> in the value
                    927:      *           :H            Substitute the head of each word
                    928:      *           :T            Substitute the tail of each word
                    929:      *           :E            Substitute the extension (minus '.') of
                    930:      *                         each word
                    931:      *           :R            Substitute the root of each word
                    932:      *                         (pathname minus the suffix).
                    933:      *           :lhs=rhs      Like :S, but the rhs goes to the end of
                    934:      *                         the invocation.
                    935:      */
                    936:     while (*tstr != endc) {
                    937:        char    *newStr;    /* New value to return */
                    938:        char    termc;      /* Character which terminated scan */
                    939:
                    940:        if (DEBUG(VAR))
1.2     ! espie     941:            printf("Applying :%c to \"%s\"\n", *tstr, str ? str : "");
1.1       espie     942:        switch (*tstr) {
                    943:            case 'N':
                    944:            case 'M':
                    945:            {
                    946:                for (cp = tstr + 1;
                    947:                     *cp != '\0' && *cp != ':' && *cp != endc;
                    948:                     cp++) {
                    949:                    if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
                    950:                        cp++;
                    951:                    }
                    952:                }
                    953:                termc = *cp;
                    954:                *cp = '\0';
                    955:                if (*tstr == 'M')
                    956:                    newStr = VarModify(str, VarMatch, tstr+1);
                    957:                else
                    958:                    newStr = VarModify(str, VarNoMatch, tstr+1);
                    959:                break;
                    960:            }
                    961:            case 'S':
                    962:            {
                    963:                VarPattern          pattern;
                    964:
                    965:                pattern.flags = 0;
                    966:                delim = tstr[1];
                    967:                tstr += 2;
                    968:
                    969:                /* If pattern begins with '^', it is anchored to the
                    970:                 * start of the word -- skip over it and flag pattern.  */
                    971:                if (*tstr == '^') {
                    972:                    pattern.flags |= VAR_MATCH_START;
                    973:                    tstr++;
                    974:                }
                    975:
                    976:                cp = tstr;
                    977:                if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
                    978:                    &pattern.flags, &pattern.leftLen, NULL)) == NULL)
                    979:                    goto cleanup;
                    980:
                    981:                if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
                    982:                    NULL, &pattern.rightLen, &pattern)) == NULL)
                    983:                    goto cleanup;
                    984:
                    985:                /* Check for global substitution. If 'g' after the final
                    986:                 * delimiter, substitution is global and is marked that
                    987:                 * way.  */
                    988:                for (;; cp++) {
                    989:                    switch (*cp) {
                    990:                    case 'g':
                    991:                        pattern.flags |= VAR_SUB_GLOBAL;
                    992:                        continue;
                    993:                    case '1':
                    994:                        pattern.flags |= VAR_SUB_ONE;
                    995:                        continue;
                    996:                    }
                    997:                    break;
                    998:                }
                    999:
                   1000:                termc = *cp;
                   1001:                newStr = VarModify(str, VarSubstitute, &pattern);
                   1002:
                   1003:                /* Free the two strings.  */
                   1004:                free(pattern.lhs);
                   1005:                free(pattern.rhs);
                   1006:                break;
                   1007:            }
                   1008: #ifndef MAKE_BOOTSTRAP
                   1009:            case 'C':
                   1010:            {
                   1011:                VarREPattern    pattern;
                   1012:                char           *re;
                   1013:                int             error;
                   1014:
                   1015:                pattern.flags = 0;
                   1016:                delim = tstr[1];
                   1017:                tstr += 2;
                   1018:
                   1019:                cp = tstr;
                   1020:
                   1021:                if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
                   1022:                    NULL, NULL)) == NULL)
                   1023:                    goto cleanup;
                   1024:
                   1025:                if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
                   1026:                    delim, NULL, NULL, NULL)) == NULL) {
                   1027:                    free(re);
                   1028:                    goto cleanup;
                   1029:                }
                   1030:
                   1031:                for (;; cp++) {
                   1032:                    switch (*cp) {
                   1033:                    case 'g':
                   1034:                        pattern.flags |= VAR_SUB_GLOBAL;
                   1035:                        continue;
                   1036:                    case '1':
                   1037:                        pattern.flags |= VAR_SUB_ONE;
                   1038:                        continue;
                   1039:                    }
                   1040:                    break;
                   1041:                }
                   1042:
                   1043:                termc = *cp;
                   1044:
                   1045:                error = regcomp(&pattern.re, re, REG_EXTENDED);
                   1046:                free(re);
                   1047:                if (error) {
                   1048:                    *lengthPtr = cp - start + 1;
                   1049:                    VarREError(error, &pattern.re, "RE substitution error");
                   1050:                    free(pattern.replace);
                   1051:                    return var_Error;
                   1052:                }
                   1053:
                   1054:                pattern.nsub = pattern.re.re_nsub + 1;
                   1055:                if (pattern.nsub < 1)
                   1056:                    pattern.nsub = 1;
                   1057:                if (pattern.nsub > 10)
                   1058:                    pattern.nsub = 10;
                   1059:                pattern.matches = emalloc(pattern.nsub *
                   1060:                                          sizeof(regmatch_t));
                   1061:                newStr = VarModify(str, VarRESubstitute, &pattern);
                   1062:                regfree(&pattern.re);
                   1063:                free(pattern.replace);
                   1064:                free(pattern.matches);
                   1065:                break;
                   1066:            }
                   1067: #endif
                   1068:            case 'Q':
                   1069:                if (tstr[1] == endc || tstr[1] == ':') {
                   1070:                    newStr = VarQuote(str);
                   1071:                    cp = tstr + 1;
                   1072:                    termc = *cp;
                   1073:                    break;
                   1074:                }
                   1075:                /*FALLTHRU*/
                   1076:            case 'T':
                   1077:                if (tstr[1] == endc || tstr[1] == ':') {
                   1078:                    newStr = VarModify(str, VarTail, NULL);
                   1079:                    cp = tstr + 1;
                   1080:                    termc = *cp;
                   1081:                    break;
                   1082:                }
                   1083:                /*FALLTHRU*/
                   1084:            case 'H':
                   1085:                if (tstr[1] == endc || tstr[1] == ':') {
                   1086:                    newStr = VarModify(str, VarHead, NULL);
                   1087:                    cp = tstr + 1;
                   1088:                    termc = *cp;
                   1089:                    break;
                   1090:                }
                   1091:                /*FALLTHRU*/
                   1092:            case 'E':
                   1093:                if (tstr[1] == endc || tstr[1] == ':') {
                   1094:                    newStr = VarModify(str, VarSuffix, NULL);
                   1095:                    cp = tstr + 1;
                   1096:                    termc = *cp;
                   1097:                    break;
                   1098:                }
                   1099:                /*FALLTHRU*/
                   1100:            case 'R':
                   1101:                if (tstr[1] == endc || tstr[1] == ':') {
                   1102:                    newStr = VarModify(str, VarRoot, NULL);
                   1103:                    cp = tstr + 1;
                   1104:                    termc = *cp;
                   1105:                    break;
                   1106:                }
                   1107:                /*FALLTHRU*/
                   1108:            case 'U':
                   1109:                if (tstr[1] == endc || tstr[1] == ':') {
                   1110:                    newStr = VarModify(str, VarUppercase, NULL);
                   1111:                    cp = tstr + 1;
                   1112:                    termc = *cp;
                   1113:                    break;
                   1114:                }
                   1115:                /*FALLTHRU*/
                   1116:            case 'L':
                   1117:                if (tstr[1] == endc || tstr[1] == ':') {
                   1118:                    newStr = VarModify(str, VarLowercase, NULL);
                   1119:                    cp = tstr + 1;
                   1120:                    termc = *cp;
                   1121:                    break;
                   1122:                }
                   1123:                /*FALLTHRU*/
                   1124: #ifdef SUNSHCMD
                   1125:            case 's':
                   1126:                if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
                   1127:                    char *err;
1.2     ! espie    1128:                    newStr = str ? Cmd_Exec(str, &err) : NULL;
1.1       espie    1129:                    if (err)
                   1130:                        Error(err, str);
                   1131:                    cp = tstr + 2;
                   1132:                    termc = *cp;
                   1133:                    break;
                   1134:                }
                   1135:                /*FALLTHRU*/
                   1136: #endif
                   1137:            default:
                   1138:            {
                   1139: #ifdef SYSVVARSUB
                   1140:                /* This can either be a bogus modifier or a System-V
                   1141:                 * substitution command.  */
                   1142:                VarPattern      pattern;
                   1143:                Boolean         eqFound;
                   1144:                int             cnt;    /* Used to count brace pairs when
                   1145:                                         * variable in in parens or braces */
                   1146:                char            startc;
                   1147:
                   1148:                if (endc == ')')
                   1149:                    startc = '(';
                   1150:                else
                   1151:                    startc = '{';
                   1152:
                   1153:                pattern.flags = 0;
                   1154:                eqFound = FALSE;
                   1155:                /* First we make a pass through the string trying
                   1156:                 * to verify it is a SYSV-make-style translation:
                   1157:                 * it must be: <string1>=<string2>) */
                   1158:                cp = tstr;
                   1159:                cnt = 1;
                   1160:                while (*cp != '\0' && cnt) {
                   1161:                    if (*cp == '=') {
                   1162:                        eqFound = TRUE;
                   1163:                        /* continue looking for endc */
                   1164:                    }
                   1165:                    else if (*cp == endc)
                   1166:                        cnt--;
                   1167:                    else if (*cp == startc)
                   1168:                        cnt++;
                   1169:                    if (cnt)
                   1170:                        cp++;
                   1171:                }
                   1172:                if (*cp == endc && eqFound) {
                   1173:
                   1174:                    /* Now we break this sucker into the lhs and
                   1175:                     * rhs. We must null terminate them of course.  */
                   1176:                    for (cp = tstr; *cp != '='; cp++)
                   1177:                        continue;
                   1178:                    pattern.lhs = tstr;
                   1179:                    pattern.leftLen = cp - tstr;
                   1180:                    *cp++ = '\0';
                   1181:
                   1182:                    pattern.rhs = cp;
                   1183:                    cnt = 1;
                   1184:                    while (cnt) {
                   1185:                        if (*cp == endc)
                   1186:                            cnt--;
                   1187:                        else if (*cp == startc)
                   1188:                            cnt++;
                   1189:                        if (cnt)
                   1190:                            cp++;
                   1191:                    }
                   1192:                    pattern.rightLen = cp - pattern.rhs;
                   1193:                    *cp = '\0';
                   1194:
                   1195:                    /* SYSV modifications happen through the whole
                   1196:                     * string. Note the pattern is anchored at the end.  */
                   1197:                    newStr = VarModify(str, VarSYSVMatch, &pattern);
                   1198:
                   1199:                    /* Restore the nulled characters */
                   1200:                    pattern.lhs[pattern.leftLen] = '=';
                   1201:                    pattern.rhs[pattern.rightLen] = endc;
                   1202:                    termc = endc;
                   1203:                } else
                   1204: #endif
                   1205:                {
                   1206:                    Error ("Unknown modifier '%c'\n", *tstr);
                   1207:                    for (cp = tstr+1;
                   1208:                         *cp != ':' && *cp != endc && *cp != '\0';)
                   1209:                         cp++;
                   1210:                    termc = *cp;
                   1211:                    newStr = var_Error;
                   1212:                }
                   1213:            }
                   1214:        }
                   1215:        if (DEBUG(VAR))
1.2     ! espie    1216:            printf("Result is \"%s\"\n", newStr != NULL ? newStr : "");
1.1       espie    1217:
                   1218:        if (*freePtr)
                   1219:            free(str);
                   1220:        str = newStr;
1.2     ! espie    1221:        if (str != var_Error && str != NULL)
1.1       espie    1222:            *freePtr = TRUE;
                   1223:        else
                   1224:            *freePtr = FALSE;
                   1225:        if (termc == '\0')
                   1226:            Error("Unclosed variable specification");
                   1227:        else if (termc == ':')
                   1228:            *cp++ = termc;
                   1229:        else
                   1230:            *cp = termc;
                   1231:        tstr = cp;
                   1232:     }
                   1233:     *lengthPtr += tstr - start+1;
                   1234:     return str;
                   1235:
                   1236: cleanup:
                   1237:     *lengthPtr += cp - start +1;
                   1238:     if (*freePtr)
                   1239:        free(str);
                   1240:     Error("Unclosed substitution for (%c missing)", delim);
                   1241:     return var_Error;
                   1242: }
                   1243:
                   1244: /*-
                   1245:  *-----------------------------------------------------------------------
                   1246:  * VarQuote --
                   1247:  *     Quote shell meta-characters in the string
                   1248:  *
                   1249:  * Results:
                   1250:  *     The quoted string
                   1251:  *
                   1252:  *-----------------------------------------------------------------------
                   1253:  */
                   1254: static char *
                   1255: VarQuote(str)
                   1256:        const char *str;
                   1257: {
                   1258:
                   1259:     BUFFER       buf;
                   1260:     /* This should cover most shells :-( */
                   1261:     static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.2     ! espie    1262:
        !          1263:     if (str == NULL)
        !          1264:        return NULL;
1.1       espie    1265:
                   1266:     Buf_Init(&buf, MAKE_BSIZE);
                   1267:     for (; *str; str++) {
                   1268:        if (strchr(meta, *str) != NULL)
                   1269:            Buf_AddChar(&buf, '\\');
                   1270:        Buf_AddChar(&buf, *str);
                   1271:     }
                   1272:     return Buf_Retrieve(&buf);
                   1273: }
                   1274: /*-
                   1275:  *-----------------------------------------------------------------------
                   1276:  * Var_GetHead --
                   1277:  *     Find the leading components of a (list of) filename(s).
                   1278:  *     XXX: VarHead does not replace foo by ., as (sun) System V make
                   1279:  *     does.
                   1280:  *
                   1281:  * Results:
                   1282:  *     The leading components.
                   1283:  *
                   1284:  * Side Effects:
                   1285:  *     None.
                   1286:  *
                   1287:  *-----------------------------------------------------------------------
                   1288:  */
                   1289: char *
                   1290: Var_GetHead(file)
                   1291:     char       *file;      /* Filename to manipulate */
                   1292: {
                   1293:     return VarModify(file, VarHead, NULL);
                   1294: }
                   1295:
                   1296: /*-
                   1297:  *-----------------------------------------------------------------------
                   1298:  * Var_GetTail --
                   1299:  *     Return the tail from each of a list of words. Used to set the
                   1300:  *     System V local variables.
                   1301:  *
                   1302:  * Results:
                   1303:  *     The resulting string.
                   1304:  *
                   1305:  * Side Effects:
                   1306:  *     None.
                   1307:  *
                   1308:  *-----------------------------------------------------------------------
                   1309:  */
                   1310: char *
                   1311: Var_GetTail(file)
                   1312:     char       *file;      /* Filename to modify */
                   1313: {
                   1314:     return VarModify(file, VarTail, NULL);
                   1315: }
                   1316: