diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 1997-04-01 07:28:29 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 1997-04-01 07:28:29 +0000 |
commit | 2ea210d86004d0167acb9219e03c6f617b3aa9c3 (patch) | |
tree | 36f6ba50c02f895f144101d86c23c124a0e69eec /usr.bin/make/var.c | |
parent | 578efbfa2de99f72e0c84130ddba49afb782a27e (diff) |
Sync with NetBSD (mostly by christos initial substitution/regexp from Der Mouse)
- fix the variable substitution code in make [PR/2748]
1. change s/a/b/ so that it substitutes the first occurance of the
pattern on each word, not only the first word.
2. add flag '1' to the variable substitution so that the substitutions
get performed only once.
***THIS IS AN INCOMPATIBLE CHANGE!***
Unfortunately there was no way to make things consistent without
modifying the current behavior. Fortunately none of our Makefiles
depended on this.
OLD:
VAR = aa1 aa2 aa3 aa4
S/a/b/ = ba1 aa2 aa3 aa4
S/a/b/g = bb1 bb2 bb3 bb4
NEW:
VAR = aa1 aa2 aa3 aa4
S/a/b/ = ba1 ba2 ba3 ba4
S/a/b/1 = ba1 aa2 aa3 aa4
S/a/b/g = bb1 bb2 bb3 bb4
S/a/b/1g = bb1 aa2 aa3 aa4
- add regexp variable substitution via 'C/foo/bar/' [PR/2752]
- add variable quoting via the ${VAR:Q} modifier. This is useful when running
recursive invocations of make(1):
make VAR=${VAR:Q}
will always work... (This may prove useful in the kernel builds...) [PR/2981]
- BSD did not traditionally have <sys/cdefs.h>; use BSD4_4 instead and include
<sys/param.h> to grab it.
- Don't compile the regex code if MAKE_BOOTSTRAP (from gwr)
- Use explicit .c.o rule in Makefile.boot so that the bootstrap process works.
- Use only integral types in procedure arguments. [buf.c buf.h]
- Include <stdlib.h> to get getenv() prototype on SVR4
- if __STDC__ -> ifdef __STDC__ to appease SVR4
- Define const and volatile for non __STDC__
- Implement snprintf() and vsnprintf() for non BSD4_4 systems.
- Make $MACHINE_ARCH settable from the environment.
- Fix .USE directive problems: (reported by cgd)
1. ${.*} variables did not get expanded in dependencies.
2. expanded ${.*} variables in .USE dependencies can cause tree
restructuring; handle it.
3. in compat mode, expand .USE before evaluating the list of targets,
instead of doing .USE expansions on demand, because they can cause
tree restructuring.
- Add a .MADE directive to indicated that the children of a target are
up-to-date, even when they are not. This is to simulate our current
make install behavior with proper dependencies.
- Fix problems in the RE substitution error handling.
- Locate all the children of a node marked as MADE.
- Do not compile-in ${MACHINE} (as per NetBSD PR#3386)
- Disable globbing for targets/dependencies when POSIX is defined.
- Fix globbing so that patterns that don't have a matching number of [] or {}
don't get expanded. (before the [ case got expanded to nothing!) This is
disabled.
- Make sure that the children of nodes that are marked .MADE, are marked
UPTODATE and their timestamps are consistent.
- Don't disable wildcards completely; they are used by other Makefiles.
Diffstat (limited to 'usr.bin/make/var.c')
-rw-r--r-- | usr.bin/make/var.c | 570 |
1 files changed, 423 insertions, 147 deletions
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 642c07c0be2..fdd8051ce6c 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -1,5 +1,5 @@ -/* $OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $ */ -/* $NetBSD: var.c,v 1.15 1996/11/06 17:59:29 christos Exp $ */ +/* $OpenBSD: var.c,v 1.6 1997/04/01 07:28:28 millert Exp $ */ +/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -43,7 +43,7 @@ #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -static char rcsid[] = "$OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $"; +static char rcsid[] = "$OpenBSD: var.c,v 1.6 1997/04/01 07:28:28 millert Exp $"; #endif #endif /* not lint */ @@ -90,6 +90,10 @@ static char rcsid[] = "$OpenBSD: var.c,v 1.5 1996/11/30 21:09:07 millert Exp $"; */ #include <ctype.h> +#ifndef MAKE_BOOTSTRAP +#include <regex.h> +#endif +#include <stdlib.h> #include "make.h" #include "buf.h" @@ -145,17 +149,31 @@ typedef struct Var { * modified variables */ } Var; +/* Var*Pattern flags */ +#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ +#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ +#define VAR_SUB_MATCHED 0x04 /* There was a match */ +#define VAR_MATCH_START 0x08 /* Match at start of word */ +#define VAR_MATCH_END 0x10 /* Match at end of word */ + typedef struct { char *lhs; /* String to match */ int leftLen; /* Length of string */ char *rhs; /* Replacement string (w/ &'s removed) */ int rightLen; /* Length of replacement */ int flags; -#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ -#define VAR_MATCH_START 2 /* Match at start of word */ -#define VAR_MATCH_END 4 /* Match at end of word */ } VarPattern; +#ifndef MAKE_BOOTSTRAP +typedef struct { + regex_t re; + int nsub; + regmatch_t *matches; + char *replace; + int flags; +} VarREPattern; +#endif + static int VarCmp __P((ClientData, ClientData)); static Var *VarFind __P((char *, GNode *, int)); static void VarAdd __P((char *, char *, GNode *)); @@ -169,7 +187,14 @@ static Boolean VarMatch __P((char *, Boolean, Buffer, ClientData)); static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData)); #endif static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData)); +#ifndef MAKE_BOOTSTRAP +static void VarREError __P((int, regex_t *, const char *)); +static Boolean VarRESubstitute __P((char *, Boolean, Buffer, ClientData)); +#endif static Boolean VarSubstitute __P((char *, Boolean, Buffer, ClientData)); +static char *VarGetPattern __P((GNode *, int, char **, int, int *, int *, + VarPattern *)); +static char *VarQuote __P((char *)); static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer, ClientData), ClientData)); @@ -891,9 +916,10 @@ VarSubstitute (word, addSpace, buf, patternp) VarPattern *pattern = (VarPattern *) patternp; wordLen = strlen(word); - if (1) { /* substitute in each word of the variable */ + if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != + (VAR_SUB_ONE|VAR_SUB_MATCHED)) { /* - * Break substitution down into simple anchored cases + * Still substituting -- break it down into simple anchored cases * and if none of them fits, perform the general substitution case. */ if ((pattern->flags & VAR_MATCH_START) && @@ -916,6 +942,7 @@ VarSubstitute (word, addSpace, buf, patternp) Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); } + pattern->flags |= VAR_SUB_MATCHED; } else if (pattern->flags & VAR_MATCH_END) { /* * Doesn't match to end -- copy word wholesale @@ -934,6 +961,7 @@ VarSubstitute (word, addSpace, buf, patternp) Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); Buf_AddBytes(buf, wordLen - pattern->leftLen, (Byte *)(word + pattern->leftLen)); + pattern->flags |= VAR_SUB_MATCHED; } } else if (pattern->flags & VAR_MATCH_START) { /* @@ -964,6 +992,7 @@ VarSubstitute (word, addSpace, buf, patternp) } Buf_AddBytes(buf, cp - word, (Byte *)word); Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); + pattern->flags |= VAR_SUB_MATCHED; } else { /* * Had to match at end and didn't. Copy entire word. @@ -1001,6 +1030,7 @@ VarSubstitute (word, addSpace, buf, patternp) if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ done = TRUE; } + pattern->flags |= VAR_SUB_MATCHED; } else { done = TRUE; } @@ -1018,10 +1048,6 @@ VarSubstitute (word, addSpace, buf, patternp) */ return ((Buf_Size(buf) != origSize) || addSpace); } - /* - * Common code for anchored substitutions: - * addSpace was set TRUE if characters were added to the buffer. - */ return (addSpace); } nosub: @@ -1032,6 +1058,158 @@ VarSubstitute (word, addSpace, buf, patternp) return(TRUE); } +#ifndef MAKE_BOOTSTRAP +/*- + *----------------------------------------------------------------------- + * VarREError -- + * Print the error caused by a regcomp or regexec call. + * + * Results: + * None. + * + * Side Effects: + * An error gets printed. + * + *----------------------------------------------------------------------- + */ +static void +VarREError(err, pat, str) + int err; + regex_t *pat; + const char *str; +{ + char *errbuf; + int errlen; + + errlen = regerror(err, pat, 0, 0); + errbuf = emalloc(errlen); + regerror(err, pat, errbuf, errlen); + Error("%s: %s", str, errbuf); + free(errbuf); +} + +/*- + *----------------------------------------------------------------------- + * VarRESubstitute -- + * Perform a regex substitution on the given word, placing the + * result in the passed buffer. + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRESubstitute(word, addSpace, buf, patternp) + char *word; + Boolean addSpace; + Buffer buf; + ClientData patternp; +{ + VarREPattern *pat; + int xrv; + char *wp; + char *rp; + int added; + +#define MAYBE_ADD_SPACE() \ + if (addSpace && !added) \ + Buf_AddByte(buf, ' '); \ + added = 1 + + added = 0; + wp = word; + pat = patternp; + + if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == + (VAR_SUB_ONE|VAR_SUB_MATCHED)) + xrv = REG_NOMATCH; + else { + tryagain: + xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, 0); + } + + switch (xrv) { + case 0: + pat->flags |= VAR_SUB_MATCHED; + if (pat->matches[0].rm_so > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, pat->matches[0].rm_so, wp); + } + + for (rp = pat->replace; *rp; rp++) { + if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf,rp[1]); + rp++; + } + else if ((*rp == '&') || ((*rp == '\\') && isdigit(rp[1]))) { + int n; + char *subbuf; + char zsub; + int sublen; + char errstr[3]; + + if (*rp == '&') { + n = 0; + errstr[0] = '&'; + errstr[1] = '\0'; + } else { + n = rp[1] - '0'; + errstr[0] = '\\'; + errstr[1] = rp[1]; + errstr[2] = '\0'; + rp++; + } + + if (n > pat->nsub) { + Error("No subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else if ((pat->matches[n].rm_so == -1) && + (pat->matches[n].rm_eo == -1)) { + Error("No match for subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else { + subbuf = wp + pat->matches[n].rm_so; + sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; + } + + if (sublen > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, sublen, subbuf); + } + } else { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *rp); + } + } + wp += pat->matches[0].rm_eo; + if (pat->flags & VAR_SUB_GLOBAL) + goto tryagain; + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, strlen(wp), wp); + } + break; + default: + VarREError(xrv, &pat->re, "Unexpected regex error"); + /* fall through */ + case REG_NOMATCH: + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf,strlen(wp),wp); + } + break; + } + return(addSpace||added); +} +#endif + /*- *----------------------------------------------------------------------- * VarModify -- @@ -1076,6 +1254,141 @@ VarModify (str, modProc, datum) /*- *----------------------------------------------------------------------- + * VarGetPattern -- + * Pass through the tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution). + * Return the expanded string or NULL if the delimiter was missing + * If pattern is specified, handle escaped ampersants, and replace + * unescaped ampersands with the lhs of the pattern. + * + * Results: + * A string of all the words modified appropriately. + * If length is specified, return the string length of the buffer + * If flags is specified and the last character of the pattern is a + * $ set the VAR_MATCH_END bit of flags. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +VarGetPattern(ctxt, err, tstr, delim, flags, length, pattern) + GNode *ctxt; + int err; + char **tstr; + int delim; + int *flags; + int *length; + VarPattern *pattern; +{ + char *cp; + Buffer buf = Buf_Init(0); + int junk; + if (length == NULL) + length = &junk; + +#define IS_A_MATCH(cp, delim) \ + ((cp[0] == '\\') && ((cp[1] == delim) || \ + (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) + + /* + * Skim through until the matching delimiter is found; + * pick up variable substitutions on the way. Also allow + * backslashes to quote the delimiter, $, and \, but don't + * touch other backslashes. + */ + for (cp = *tstr; *cp && (*cp != delim); cp++) { + if (IS_A_MATCH(cp, delim)) { + Buf_AddByte(buf, (Byte) cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] == delim) { + if (flags == NULL) + Buf_AddByte(buf, (Byte) *cp); + else + /* + * Unescaped $ at end of pattern => anchor + * pattern at end. + */ + *flags |= VAR_MATCH_END; + } + else { + char *cp2; + int len; + Boolean freeIt; + + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); + Buf_AddBytes(buf, strlen(cp2), (Byte *) cp2); + if (freeIt) + free(cp2); + cp += len - 1; + } + } + else if (pattern && *cp == '&') + Buf_AddBytes(buf, pattern->leftLen, (Byte *)pattern->lhs); + else + Buf_AddByte(buf, (Byte) *cp); + } + + Buf_AddByte(buf, (Byte) '\0'); + + if (*cp != delim) { + *tstr = cp; + *length = 0; + return NULL; + } + else { + *tstr = ++cp; + cp = (char *) Buf_GetAll(buf, length); + *length -= 1; /* Don't count the NULL */ + Buf_Destroy(buf, FALSE); + return cp; + } +} + +/*- + *----------------------------------------------------------------------- + * VarQuote -- + * Quote shell meta-characters in the string + * + * Results: + * The quoted string + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarQuote(str) + char *str; +{ + + Buffer buf; + /* This should cover most shells :-( */ + static char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + + buf = Buf_Init (MAKE_BSIZE); + for (; *str; str++) { + if (strchr(meta, *str) != NULL) + Buf_AddByte(buf, (Byte)'\\'); + Buf_AddByte(buf, (Byte)*str); + } + Buf_AddByte(buf, (Byte) '\0'); + str = (char *)Buf_GetAll (buf, (int *)NULL); + Buf_Destroy (buf, FALSE); + return str; +} + +/*- + *----------------------------------------------------------------------- * Var_Parse -- * Given the start of a variable invocation, extract the variable * name and find its value, then modify it according to the @@ -1104,7 +1417,7 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) { register char *tstr; /* Pointer into str */ Var *v; /* Variable in invocation */ - register char *cp; /* Secondary pointer into str (place marker + char *cp; /* Secondary pointer into str (place marker * for tstr) */ Boolean haveModifier;/* TRUE if have modifiers for the variable */ register char endc; /* Ending character when variable in parens @@ -1114,6 +1427,7 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) int cnt; /* Used to count brace pairs when variable in * in parens or braces */ char *start; + char delim; Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The @@ -1348,6 +1662,8 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) * wildcarding form. * :S<d><pat1><d><pat2><d>[g] * Substitute <pat2> for <pat1> in the value + * :C<d><pat1><d><pat2><d>[g] + * Substitute <pat2> for regex <pat1> in the value * :H Substitute the head of each word * :T Substitute the tail of each word * :E Substitute the extension (minus '.') of @@ -1426,12 +1742,11 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) case 'S': { VarPattern pattern; - register char delim; - Buffer buf; /* Buffer for patterns */ pattern.flags = 0; delim = tstr[1]; tstr += 2; + /* * If pattern begins with '^', it is anchored to the * start of the word -- skip over it and flag pattern. @@ -1441,159 +1756,112 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) tstr += 1; } - buf = Buf_Init(0); + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim, + &pattern.flags, &pattern.leftLen, NULL)) == NULL) + goto cleanup; + + if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim, + NULL, &pattern.rightLen, &pattern)) == NULL) + goto cleanup; /* - * Pass through the lhs looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution). - * The result is left in the Buffer buf. + * Check for global substitution. If 'g' after the final + * delimiter, substitution is global and is marked that + * way. */ - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '$') || - (cp[1] == '\\'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] != delim) { - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - if (freeIt) { - free(cp2); - } - cp += len - 1; - } else { - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - pattern.flags |= VAR_MATCH_END; - } - } else { - Buf_AddByte(buf, (Byte)*cp); + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; } + break; } - Buf_AddByte(buf, (Byte)'\0'); + termc = *cp; + newStr = VarModify(str, VarSubstitute, + (ClientData)&pattern); /* - * If lhs didn't end with the delimiter, complain and - * return NULL + * Free the two strings. */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); - } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } + free(pattern.lhs); + free(pattern.rhs); + break; + } +#ifndef MAKE_BOOTSTRAP + case 'C': + { + VarREPattern pattern; + char *re; + int error; - /* - * Fetch pattern and destroy buffer, but preserve the data - * in it, since that's our lhs. Note that Buf_GetAll - * will return the actual number of bytes, which includes - * the null byte, so we have to decrement the length by - * one. - */ - pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen); - pattern.leftLen--; - Buf_Destroy(buf, FALSE); + pattern.flags = 0; + delim = tstr[1]; + tstr += 2; - /* - * Now comes the replacement string. Three things need to - * be done here: 1) need to compress escaped delimiters and - * ampersands and 2) need to replace unescaped ampersands - * with the l.h.s. (since this isn't regexp, we can do - * it right here) and 3) expand any variable substitutions. - */ - buf = Buf_Init(0); - - tstr = cp + 1; - for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { - if ((*cp == '\\') && - ((cp[1] == delim) || - (cp[1] == '&') || - (cp[1] == '\\') || - (cp[1] == '$'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if ((*cp == '$') && (cp[1] != delim)) { - char *cp2; - int len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); - cp += len - 1; - if (freeIt) { - free(cp2); - } - } else if (*cp == '&') { - Buf_AddBytes(buf, pattern.leftLen, - (Byte *)pattern.lhs); - } else { - Buf_AddByte(buf, (Byte)*cp); - } - } + cp = tstr; - Buf_AddByte(buf, (Byte)'\0'); + if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, + NULL, NULL)) == NULL) + goto cleanup; - /* - * If didn't end in delimiter character, complain - */ - if (*cp != delim) { - *lengthPtr = cp - start + 1; - if (*freePtr) { - free(str); + if ((pattern.replace = VarGetPattern(ctxt, err, &cp, + delim, NULL, NULL, NULL)) == NULL) { + free(re); + goto cleanup; + } + + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; } - Buf_Destroy(buf, TRUE); - Error("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); + break; } - pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); - pattern.rightLen--; - Buf_Destroy(buf, FALSE); + termc = *cp; - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - cp++; - if (*cp == 'g') { - pattern.flags |= VAR_SUB_GLOBAL; - cp++; + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *lengthPtr = cp - start + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + return (var_Error); } - termc = *cp; - newStr = VarModify(str, VarSubstitute, - (ClientData)&pattern); - /* - * Free the two strings. - */ - free(pattern.lhs); - free(pattern.rhs); + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = emalloc(pattern.nsub * + sizeof(regmatch_t)); + newStr = VarModify(str, VarRESubstitute, + (ClientData) &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); break; } +#endif + case 'Q': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarQuote (str); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ case 'T': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarTail, (ClientData)0); @@ -1784,6 +2052,14 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr) } } return (str); + +cleanup: + *lengthPtr = cp - start + 1; + if (*freePtr) + free(str); + Error("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); } /*- |