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

1.6     ! espie       1: /*     $OpenBSD: varmodifiers.c,v 1.5 2000/09/14 13:35:38 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 *));
1.5       espie     121: static char *VarModify __P((char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *));
1.1       espie     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)
1.6     ! espie     141:     const char *word;          /* Word to Upper Case */
        !           142:     Boolean    addSpace;       /* True if need to add a space to the buffer
1.1       espie     143:                                 * before sticking in the head */
1.6     ! espie     144:     Buffer     buf;            /* Buffer in which to store it */
        !           145:     void       *dummy          UNUSED;
1.1       espie     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)
1.6     ! espie     172:     const char *word;          /* Word to Lower Case */
        !           173:     Boolean    addSpace;       /* True if need to add a space to the buffer
1.1       espie     174:                                 * before sticking in the head */
1.6     ! espie     175:     Buffer     buf;            /* Buffer in which to store it */
        !           176:     void       *dummy          UNUSED;
1.1       espie     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)
1.6     ! espie     204:     const char *word;          /* Word to trim */
        !           205:     Boolean    addSpace;       /* True if need to add a space to the buffer
1.1       espie     206:                                 * before sticking in the head */
1.6     ! espie     207:     Buffer     buf;            /* Buffer in which to store it */
        !           208:     void       *dummy          UNUSED;
1.1       espie     209: {
1.6     ! espie     210:     const char *slash;
1.1       espie     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:     }
1.6     ! espie     225:     return TRUE;
1.1       espie     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)
1.6     ! espie     245:     const char *word;          /* Word to trim */
        !           246:     Boolean    addSpace;       /* TRUE if need to stick a space in the
1.1       espie     247:                                 * buffer before adding the tail */
1.6     ! espie     248:     Buffer     buf;            /* Buffer in which to store it */
        !           249:     void       *dummy          UNUSED;
1.1       espie     250: {
1.6     ! espie     251:     const char *slash;
1.1       espie     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);
1.6     ! espie     260:     return TRUE;
1.1       espie     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)
1.6     ! espie     279:     const char *word;          /* Word to trim */
        !           280:     Boolean    addSpace;       /* TRUE if need to add a space before placing
1.1       espie     281:                                 * the suffix in the buffer */
1.6     ! espie     282:     Buffer     buf;            /* Buffer in which to store it */
        !           283:     void       *dummy          UNUSED;
1.1       espie     284: {
1.6     ! espie     285:     const char *dot;
1.1       espie     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:     }
1.6     ! espie     294:     return addSpace;
1.1       espie     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)
1.6     ! espie     314:     const char *word;          /* Word to trim */
        !           315:     Boolean    addSpace;       /* TRUE if need to add a space to the buffer
1.1       espie     316:                                 * before placing the root in it */
1.6     ! espie     317:     Buffer     buf;            /* Buffer in which to store it */
        !           318:     void       *dummy          UNUSED;
1.1       espie     319: {
1.6     ! espie     320:     const char *dot;
1.1       espie     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);
1.6     ! espie     330:     return TRUE;
1.1       espie     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 *
1.5       espie     772: VarModify(str, modProc, datum)
                    773:     char         *str;         /* String whose words should be trimmed */
                    774:                                /* Function to use to modify them */
1.1       espie     775:     Boolean              (*modProc) __P((const char *, Boolean, Buffer, void *));
1.5       espie     776:     void          *datum;      /* Datum to pass it */
1.1       espie     777: {
1.5       espie     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         *end;
                    783:     char         *word;
1.1       espie     784:
1.2       espie     785:     if (str == NULL)
                    786:        return NULL;
                    787:
1.1       espie     788:     Buf_Init(&buf, 0);
                    789:     addSpace = FALSE;
                    790:
1.5       espie     791:     end = str;
1.1       espie     792:
1.5       espie     793:     while ((word = iterate_words(&end)) != NULL) {
                    794:        char      termc;
1.1       espie     795:
1.5       espie     796:        termc = *end;
                    797:        *end = '\0';
                    798:        addSpace = (*modProc)(word, addSpace, &buf, datum);
                    799:        *end = termc;
                    800:     }
1.1       espie     801:     return Buf_Retrieve(&buf);
                    802: }
                    803:
                    804: /*-
                    805:  *-----------------------------------------------------------------------
                    806:  * VarGetPattern --
                    807:  *     Pass through the tstr looking for 1) escaped delimiters,
                    808:  *     '$'s and backslashes (place the escaped character in
                    809:  *     uninterpreted) and 2) unescaped $'s that aren't before
                    810:  *     the delimiter (expand the variable substitution).
                    811:  *     Return the expanded string or NULL if the delimiter was missing
                    812:  *     If pattern is specified, handle escaped ampersands, and replace
                    813:  *     unescaped ampersands with the lhs of the pattern.
                    814:  *
                    815:  * Results:
                    816:  *     A string of all the words modified appropriately.
                    817:  *     If length is specified, return the string length of the buffer
                    818:  *     If flags is specified and the last character of the pattern is a
                    819:  *     $ set the VAR_MATCH_END bit of flags.
                    820:  *
                    821:  * Side Effects:
                    822:  *     None.
                    823:  *-----------------------------------------------------------------------
                    824:  */
                    825: static char *
                    826: VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern)
                    827:     SymTable *ctxt;
                    828:     int err;
                    829:     char **tstr;
                    830:     int delim;
                    831:     int *flags;
                    832:     size_t *length;
                    833:     VarPattern *pattern;
                    834: {
                    835:     char *cp;
                    836:     BUFFER buf;
                    837:     size_t junk;
                    838:
                    839:     Buf_Init(&buf, 0);
                    840:     if (length == NULL)
                    841:        length = &junk;
                    842:
                    843: #define IS_A_MATCH(cp, delim) \
                    844:     ((cp[0] == '\\') && ((cp[1] == delim) ||  \
                    845:      (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&'))))
                    846:
                    847:     /*
                    848:      * Skim through until the matching delimiter is found;
                    849:      * pick up variable substitutions on the way. Also allow
                    850:      * backslashes to quote the delimiter, $, and \, but don't
                    851:      * touch other backslashes.
                    852:      */
                    853:     for (cp = *tstr; *cp && (*cp != delim); cp++) {
                    854:        if (IS_A_MATCH(cp, delim)) {
                    855:            Buf_AddChar(&buf, cp[1]);
                    856:            cp++;
                    857:        } else if (*cp == '$') {
                    858:            if (cp[1] == delim) {
                    859:                if (flags == NULL)
                    860:                    Buf_AddChar(&buf, *cp);
                    861:                else
                    862:                    /*
                    863:                     * Unescaped $ at end of pattern => anchor
                    864:                     * pattern at end.
                    865:                     */
                    866:                    *flags |= VAR_MATCH_END;
                    867:            }
                    868:            else {
                    869:                char   *cp2;
                    870:                size_t     len;
                    871:                Boolean freeIt;
                    872:
                    873:                /*
                    874:                 * If unescaped dollar sign not before the
                    875:                 * delimiter, assume it's a variable
                    876:                 * substitution and recurse.
                    877:                 */
                    878:                cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt);
                    879:                Buf_AddString(&buf, cp2);
                    880:                if (freeIt)
                    881:                    free(cp2);
                    882:                cp += len - 1;
                    883:            }
                    884:        }
                    885:        else if (pattern && *cp == '&')
                    886:            Buf_AddChars(&buf, pattern->leftLen, pattern->lhs);
                    887:        else
                    888:            Buf_AddChar(&buf, *cp);
                    889:     }
                    890:
                    891:     if (*cp != delim) {
                    892:        *tstr = cp;
                    893:        *length = 0;
                    894:        return NULL;
                    895:     }
                    896:     else {
                    897:        *tstr = ++cp;
                    898:        *length = Buf_Size(&buf);
                    899:        return Buf_Retrieve(&buf);
                    900:     }
                    901: }
                    902:
                    903: char *
                    904: VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr)
                    905:     char       *str;
                    906:     SymTable   *ctxt;
                    907:     Boolean    err;
                    908:     Boolean    *freePtr;
                    909:     char       *start;
                    910:     char       endc;
                    911:     size_t     *lengthPtr;
                    912: {
                    913:     char       *tstr;
                    914:     char       delim;
                    915:     char       *cp;
                    916:
                    917:     tstr = start;
                    918:
                    919:     /*
                    920:      * Now we need to apply any modifiers the user wants applied.
                    921:      * These are:
                    922:      *           :M<pattern>   words which match the given <pattern>.
                    923:      *                         <pattern> is of the standard file
                    924:      *                         wildcarding form.
                    925:      *           :S<d><pat1><d><pat2><d>[g]
                    926:      *                         Substitute <pat2> for <pat1> in the value
                    927:      *           :C<d><pat1><d><pat2><d>[g]
                    928:      *                         Substitute <pat2> for regex <pat1> in the value
                    929:      *           :H            Substitute the head of each word
                    930:      *           :T            Substitute the tail of each word
                    931:      *           :E            Substitute the extension (minus '.') of
                    932:      *                         each word
                    933:      *           :R            Substitute the root of each word
                    934:      *                         (pathname minus the suffix).
                    935:      *           :lhs=rhs      Like :S, but the rhs goes to the end of
                    936:      *                         the invocation.
                    937:      */
                    938:     while (*tstr != endc) {
                    939:        char    *newStr;    /* New value to return */
                    940:        char    termc;      /* Character which terminated scan */
                    941:
                    942:        if (DEBUG(VAR))
1.2       espie     943:            printf("Applying :%c to \"%s\"\n", *tstr, str ? str : "");
1.1       espie     944:        switch (*tstr) {
                    945:            case 'N':
                    946:            case 'M':
                    947:            {
                    948:                for (cp = tstr + 1;
                    949:                     *cp != '\0' && *cp != ':' && *cp != endc;
                    950:                     cp++) {
                    951:                    if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){
                    952:                        cp++;
                    953:                    }
                    954:                }
                    955:                termc = *cp;
                    956:                *cp = '\0';
                    957:                if (*tstr == 'M')
                    958:                    newStr = VarModify(str, VarMatch, tstr+1);
                    959:                else
                    960:                    newStr = VarModify(str, VarNoMatch, tstr+1);
                    961:                break;
                    962:            }
                    963:            case 'S':
                    964:            {
                    965:                VarPattern          pattern;
                    966:
                    967:                pattern.flags = 0;
                    968:                delim = tstr[1];
                    969:                tstr += 2;
                    970:
                    971:                /* If pattern begins with '^', it is anchored to the
                    972:                 * start of the word -- skip over it and flag pattern.  */
                    973:                if (*tstr == '^') {
                    974:                    pattern.flags |= VAR_MATCH_START;
                    975:                    tstr++;
                    976:                }
                    977:
                    978:                cp = tstr;
                    979:                if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim,
                    980:                    &pattern.flags, &pattern.leftLen, NULL)) == NULL)
                    981:                    goto cleanup;
                    982:
                    983:                if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim,
                    984:                    NULL, &pattern.rightLen, &pattern)) == NULL)
                    985:                    goto cleanup;
                    986:
                    987:                /* Check for global substitution. If 'g' after the final
                    988:                 * delimiter, substitution is global and is marked that
                    989:                 * way.  */
                    990:                for (;; cp++) {
                    991:                    switch (*cp) {
                    992:                    case 'g':
                    993:                        pattern.flags |= VAR_SUB_GLOBAL;
                    994:                        continue;
                    995:                    case '1':
                    996:                        pattern.flags |= VAR_SUB_ONE;
                    997:                        continue;
                    998:                    }
                    999:                    break;
                   1000:                }
                   1001:
                   1002:                termc = *cp;
                   1003:                newStr = VarModify(str, VarSubstitute, &pattern);
                   1004:
                   1005:                /* Free the two strings.  */
                   1006:                free(pattern.lhs);
                   1007:                free(pattern.rhs);
                   1008:                break;
                   1009:            }
                   1010: #ifndef MAKE_BOOTSTRAP
                   1011:            case 'C':
                   1012:            {
                   1013:                VarREPattern    pattern;
                   1014:                char           *re;
                   1015:                int             error;
                   1016:
                   1017:                pattern.flags = 0;
                   1018:                delim = tstr[1];
                   1019:                tstr += 2;
                   1020:
                   1021:                cp = tstr;
                   1022:
                   1023:                if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL,
                   1024:                    NULL, NULL)) == NULL)
                   1025:                    goto cleanup;
                   1026:
                   1027:                if ((pattern.replace = VarGetPattern(ctxt, err, &cp,
                   1028:                    delim, NULL, NULL, NULL)) == NULL) {
                   1029:                    free(re);
                   1030:                    goto cleanup;
                   1031:                }
                   1032:
                   1033:                for (;; cp++) {
                   1034:                    switch (*cp) {
                   1035:                    case 'g':
                   1036:                        pattern.flags |= VAR_SUB_GLOBAL;
                   1037:                        continue;
                   1038:                    case '1':
                   1039:                        pattern.flags |= VAR_SUB_ONE;
                   1040:                        continue;
                   1041:                    }
                   1042:                    break;
                   1043:                }
                   1044:
                   1045:                termc = *cp;
                   1046:
                   1047:                error = regcomp(&pattern.re, re, REG_EXTENDED);
                   1048:                free(re);
                   1049:                if (error) {
                   1050:                    *lengthPtr = cp - start + 1;
                   1051:                    VarREError(error, &pattern.re, "RE substitution error");
                   1052:                    free(pattern.replace);
                   1053:                    return var_Error;
                   1054:                }
                   1055:
                   1056:                pattern.nsub = pattern.re.re_nsub + 1;
                   1057:                if (pattern.nsub < 1)
                   1058:                    pattern.nsub = 1;
                   1059:                if (pattern.nsub > 10)
                   1060:                    pattern.nsub = 10;
                   1061:                pattern.matches = emalloc(pattern.nsub *
                   1062:                                          sizeof(regmatch_t));
                   1063:                newStr = VarModify(str, VarRESubstitute, &pattern);
                   1064:                regfree(&pattern.re);
                   1065:                free(pattern.replace);
                   1066:                free(pattern.matches);
                   1067:                break;
                   1068:            }
                   1069: #endif
                   1070:            case 'Q':
                   1071:                if (tstr[1] == endc || tstr[1] == ':') {
                   1072:                    newStr = VarQuote(str);
                   1073:                    cp = tstr + 1;
                   1074:                    termc = *cp;
                   1075:                    break;
                   1076:                }
1.3       espie    1077:                /* FALLTHROUGH */
1.1       espie    1078:            case 'T':
                   1079:                if (tstr[1] == endc || tstr[1] == ':') {
                   1080:                    newStr = VarModify(str, VarTail, NULL);
                   1081:                    cp = tstr + 1;
                   1082:                    termc = *cp;
                   1083:                    break;
                   1084:                }
1.3       espie    1085:                /* FALLTHROUGH */
1.1       espie    1086:            case 'H':
                   1087:                if (tstr[1] == endc || tstr[1] == ':') {
                   1088:                    newStr = VarModify(str, VarHead, NULL);
                   1089:                    cp = tstr + 1;
                   1090:                    termc = *cp;
                   1091:                    break;
                   1092:                }
1.3       espie    1093:                /* FALLTHROUGH */
1.1       espie    1094:            case 'E':
                   1095:                if (tstr[1] == endc || tstr[1] == ':') {
                   1096:                    newStr = VarModify(str, VarSuffix, NULL);
                   1097:                    cp = tstr + 1;
                   1098:                    termc = *cp;
                   1099:                    break;
                   1100:                }
1.3       espie    1101:                /* FALLTHROUGH */
1.1       espie    1102:            case 'R':
                   1103:                if (tstr[1] == endc || tstr[1] == ':') {
                   1104:                    newStr = VarModify(str, VarRoot, NULL);
                   1105:                    cp = tstr + 1;
                   1106:                    termc = *cp;
                   1107:                    break;
                   1108:                }
1.3       espie    1109:                /* FALLTHROUGH */
1.1       espie    1110:            case 'U':
                   1111:                if (tstr[1] == endc || tstr[1] == ':') {
                   1112:                    newStr = VarModify(str, VarUppercase, NULL);
                   1113:                    cp = tstr + 1;
                   1114:                    termc = *cp;
                   1115:                    break;
                   1116:                }
1.3       espie    1117:                /* FALLTHROUGH */
1.1       espie    1118:            case 'L':
                   1119:                if (tstr[1] == endc || tstr[1] == ':') {
                   1120:                    newStr = VarModify(str, VarLowercase, NULL);
                   1121:                    cp = tstr + 1;
                   1122:                    termc = *cp;
                   1123:                    break;
                   1124:                }
1.3       espie    1125:                /* FALLTHROUGH */
1.1       espie    1126: #ifdef SUNSHCMD
                   1127:            case 's':
                   1128:                if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) {
1.4       espie    1129:                    char *err = 0;
1.2       espie    1130:                    newStr = str ? Cmd_Exec(str, &err) : NULL;
1.1       espie    1131:                    if (err)
                   1132:                        Error(err, str);
                   1133:                    cp = tstr + 2;
                   1134:                    termc = *cp;
                   1135:                    break;
                   1136:                }
1.3       espie    1137:                /* FALLTHROUGH */
1.1       espie    1138: #endif
                   1139:            default:
                   1140:            {
                   1141: #ifdef SYSVVARSUB
                   1142:                /* This can either be a bogus modifier or a System-V
                   1143:                 * substitution command.  */
                   1144:                VarPattern      pattern;
                   1145:                Boolean         eqFound;
                   1146:                int             cnt;    /* Used to count brace pairs when
                   1147:                                         * variable in in parens or braces */
                   1148:                char            startc;
                   1149:
                   1150:                if (endc == ')')
                   1151:                    startc = '(';
                   1152:                else
                   1153:                    startc = '{';
                   1154:
                   1155:                pattern.flags = 0;
                   1156:                eqFound = FALSE;
                   1157:                /* First we make a pass through the string trying
                   1158:                 * to verify it is a SYSV-make-style translation:
                   1159:                 * it must be: <string1>=<string2>) */
                   1160:                cp = tstr;
                   1161:                cnt = 1;
                   1162:                while (*cp != '\0' && cnt) {
                   1163:                    if (*cp == '=') {
                   1164:                        eqFound = TRUE;
                   1165:                        /* continue looking for endc */
                   1166:                    }
                   1167:                    else if (*cp == endc)
                   1168:                        cnt--;
                   1169:                    else if (*cp == startc)
                   1170:                        cnt++;
                   1171:                    if (cnt)
                   1172:                        cp++;
                   1173:                }
                   1174:                if (*cp == endc && eqFound) {
                   1175:
                   1176:                    /* Now we break this sucker into the lhs and
                   1177:                     * rhs. We must null terminate them of course.  */
                   1178:                    for (cp = tstr; *cp != '='; cp++)
                   1179:                        continue;
                   1180:                    pattern.lhs = tstr;
                   1181:                    pattern.leftLen = cp - tstr;
                   1182:                    *cp++ = '\0';
                   1183:
                   1184:                    pattern.rhs = cp;
                   1185:                    cnt = 1;
                   1186:                    while (cnt) {
                   1187:                        if (*cp == endc)
                   1188:                            cnt--;
                   1189:                        else if (*cp == startc)
                   1190:                            cnt++;
                   1191:                        if (cnt)
                   1192:                            cp++;
                   1193:                    }
                   1194:                    pattern.rightLen = cp - pattern.rhs;
                   1195:                    *cp = '\0';
                   1196:
                   1197:                    /* SYSV modifications happen through the whole
                   1198:                     * string. Note the pattern is anchored at the end.  */
                   1199:                    newStr = VarModify(str, VarSYSVMatch, &pattern);
                   1200:
                   1201:                    /* Restore the nulled characters */
                   1202:                    pattern.lhs[pattern.leftLen] = '=';
                   1203:                    pattern.rhs[pattern.rightLen] = endc;
                   1204:                    termc = endc;
                   1205:                } else
                   1206: #endif
                   1207:                {
                   1208:                    Error ("Unknown modifier '%c'\n", *tstr);
                   1209:                    for (cp = tstr+1;
                   1210:                         *cp != ':' && *cp != endc && *cp != '\0';)
                   1211:                         cp++;
                   1212:                    termc = *cp;
                   1213:                    newStr = var_Error;
                   1214:                }
                   1215:            }
                   1216:        }
                   1217:        if (DEBUG(VAR))
1.2       espie    1218:            printf("Result is \"%s\"\n", newStr != NULL ? newStr : "");
1.1       espie    1219:
                   1220:        if (*freePtr)
                   1221:            free(str);
                   1222:        str = newStr;
1.2       espie    1223:        if (str != var_Error && str != NULL)
1.1       espie    1224:            *freePtr = TRUE;
                   1225:        else
                   1226:            *freePtr = FALSE;
                   1227:        if (termc == '\0')
                   1228:            Error("Unclosed variable specification");
                   1229:        else if (termc == ':')
                   1230:            *cp++ = termc;
                   1231:        else
                   1232:            *cp = termc;
                   1233:        tstr = cp;
                   1234:     }
                   1235:     *lengthPtr += tstr - start+1;
                   1236:     return str;
                   1237:
                   1238: cleanup:
                   1239:     *lengthPtr += cp - start +1;
                   1240:     if (*freePtr)
                   1241:        free(str);
                   1242:     Error("Unclosed substitution for (%c missing)", delim);
                   1243:     return var_Error;
                   1244: }
                   1245:
                   1246: /*-
                   1247:  *-----------------------------------------------------------------------
                   1248:  * VarQuote --
                   1249:  *     Quote shell meta-characters in the string
                   1250:  *
                   1251:  * Results:
                   1252:  *     The quoted string
                   1253:  *
                   1254:  *-----------------------------------------------------------------------
                   1255:  */
                   1256: static char *
                   1257: VarQuote(str)
                   1258:        const char *str;
                   1259: {
                   1260:
                   1261:     BUFFER       buf;
                   1262:     /* This should cover most shells :-( */
                   1263:     static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
1.2       espie    1264:
                   1265:     if (str == NULL)
                   1266:        return NULL;
1.1       espie    1267:
                   1268:     Buf_Init(&buf, MAKE_BSIZE);
                   1269:     for (; *str; str++) {
                   1270:        if (strchr(meta, *str) != NULL)
                   1271:            Buf_AddChar(&buf, '\\');
                   1272:        Buf_AddChar(&buf, *str);
                   1273:     }
                   1274:     return Buf_Retrieve(&buf);
                   1275: }
                   1276: /*-
                   1277:  *-----------------------------------------------------------------------
                   1278:  * Var_GetHead --
                   1279:  *     Find the leading components of a (list of) filename(s).
                   1280:  *     XXX: VarHead does not replace foo by ., as (sun) System V make
                   1281:  *     does.
                   1282:  *
                   1283:  * Results:
                   1284:  *     The leading components.
                   1285:  *
                   1286:  * Side Effects:
                   1287:  *     None.
                   1288:  *
                   1289:  *-----------------------------------------------------------------------
                   1290:  */
                   1291: char *
                   1292: Var_GetHead(file)
                   1293:     char       *file;      /* Filename to manipulate */
                   1294: {
                   1295:     return VarModify(file, VarHead, NULL);
                   1296: }
                   1297:
                   1298: /*-
                   1299:  *-----------------------------------------------------------------------
                   1300:  * Var_GetTail --
                   1301:  *     Return the tail from each of a list of words. Used to set the
                   1302:  *     System V local variables.
                   1303:  *
                   1304:  * Results:
                   1305:  *     The resulting string.
                   1306:  *
                   1307:  * Side Effects:
                   1308:  *     None.
                   1309:  *
                   1310:  *-----------------------------------------------------------------------
                   1311:  */
                   1312: char *
                   1313: Var_GetTail(file)
                   1314:     char       *file;      /* Filename to modify */
                   1315: {
                   1316:     return VarModify(file, VarTail, NULL);
                   1317: }
                   1318: