version 1.5, 1996/11/30 21:09:07 |
version 1.6, 1997/04/01 07:28:28 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* $NetBSD: var.c,v 1.15 1996/11/06 17:59:29 christos Exp $ */ |
/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1988, 1989, 1990, 1993 |
* Copyright (c) 1988, 1989, 1990, 1993 |
|
|
*/ |
*/ |
|
|
#include <ctype.h> |
#include <ctype.h> |
|
#ifndef MAKE_BOOTSTRAP |
|
#include <regex.h> |
|
#endif |
|
#include <stdlib.h> |
#include "make.h" |
#include "make.h" |
#include "buf.h" |
#include "buf.h" |
|
|
|
|
* modified variables */ |
* modified variables */ |
} Var; |
} 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 { |
typedef struct { |
char *lhs; /* String to match */ |
char *lhs; /* String to match */ |
int leftLen; /* Length of string */ |
int leftLen; /* Length of string */ |
char *rhs; /* Replacement string (w/ &'s removed) */ |
char *rhs; /* Replacement string (w/ &'s removed) */ |
int rightLen; /* Length of replacement */ |
int rightLen; /* Length of replacement */ |
int flags; |
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; |
} 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 int VarCmp __P((ClientData, ClientData)); |
static Var *VarFind __P((char *, GNode *, int)); |
static Var *VarFind __P((char *, GNode *, int)); |
static void VarAdd __P((char *, char *, GNode *)); |
static void VarAdd __P((char *, char *, GNode *)); |
|
|
static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData)); |
static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, ClientData)); |
#endif |
#endif |
static Boolean VarNoMatch __P((char *, Boolean, Buffer, ClientData)); |
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 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, |
static char *VarModify __P((char *, Boolean (*)(char *, Boolean, Buffer, |
ClientData), |
ClientData), |
ClientData)); |
ClientData)); |
|
|
VarPattern *pattern = (VarPattern *) patternp; |
VarPattern *pattern = (VarPattern *) patternp; |
|
|
wordLen = strlen(word); |
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. |
* and if none of them fits, perform the general substitution case. |
*/ |
*/ |
if ((pattern->flags & VAR_MATCH_START) && |
if ((pattern->flags & VAR_MATCH_START) && |
|
|
Buf_AddBytes(buf, pattern->rightLen, |
Buf_AddBytes(buf, pattern->rightLen, |
(Byte *)pattern->rhs); |
(Byte *)pattern->rhs); |
} |
} |
|
pattern->flags |= VAR_SUB_MATCHED; |
} else if (pattern->flags & VAR_MATCH_END) { |
} else if (pattern->flags & VAR_MATCH_END) { |
/* |
/* |
* Doesn't match to end -- copy word wholesale |
* Doesn't match to end -- copy word wholesale |
|
|
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
Buf_AddBytes(buf, wordLen - pattern->leftLen, |
Buf_AddBytes(buf, wordLen - pattern->leftLen, |
(Byte *)(word + pattern->leftLen)); |
(Byte *)(word + pattern->leftLen)); |
|
pattern->flags |= VAR_SUB_MATCHED; |
} |
} |
} else if (pattern->flags & VAR_MATCH_START) { |
} else if (pattern->flags & VAR_MATCH_START) { |
/* |
/* |
|
|
} |
} |
Buf_AddBytes(buf, cp - word, (Byte *)word); |
Buf_AddBytes(buf, cp - word, (Byte *)word); |
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
|
pattern->flags |= VAR_SUB_MATCHED; |
} else { |
} else { |
/* |
/* |
* Had to match at end and didn't. Copy entire word. |
* Had to match at end and didn't. Copy entire word. |
|
|
if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ |
if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ |
done = TRUE; |
done = TRUE; |
} |
} |
|
pattern->flags |= VAR_SUB_MATCHED; |
} else { |
} else { |
done = TRUE; |
done = TRUE; |
} |
} |
|
|
*/ |
*/ |
return ((Buf_Size(buf) != origSize) || addSpace); |
return ((Buf_Size(buf) != origSize) || addSpace); |
} |
} |
/* |
|
* Common code for anchored substitutions: |
|
* addSpace was set TRUE if characters were added to the buffer. |
|
*/ |
|
return (addSpace); |
return (addSpace); |
} |
} |
nosub: |
nosub: |
|
|
return(TRUE); |
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 -- |
* VarModify -- |
* Modify each of the words of the passed string using the given |
* Modify each of the words of the passed string using the given |
* function. Used to implement all modifiers. |
* function. Used to implement all modifiers. |
|
|
|
|
/*- |
/*- |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
|
* 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 -- |
* Var_Parse -- |
* Given the start of a variable invocation, extract the variable |
* Given the start of a variable invocation, extract the variable |
* name and find its value, then modify it according to the |
* name and find its value, then modify it according to the |
|
|
{ |
{ |
register char *tstr; /* Pointer into str */ |
register char *tstr; /* Pointer into str */ |
Var *v; /* Variable in invocation */ |
Var *v; /* Variable in invocation */ |
register char *cp; /* Secondary pointer into str (place marker |
char *cp; /* Secondary pointer into str (place marker |
* for tstr) */ |
* for tstr) */ |
Boolean haveModifier;/* TRUE if have modifiers for the variable */ |
Boolean haveModifier;/* TRUE if have modifiers for the variable */ |
register char endc; /* Ending character when variable in parens |
register char endc; /* Ending character when variable in parens |
|
|
int cnt; /* Used to count brace pairs when variable in |
int cnt; /* Used to count brace pairs when variable in |
* in parens or braces */ |
* in parens or braces */ |
char *start; |
char *start; |
|
char delim; |
Boolean dynamic; /* TRUE if the variable is local and we're |
Boolean dynamic; /* TRUE if the variable is local and we're |
* expanding it in a non-local context. This |
* expanding it in a non-local context. This |
* is done to support dynamic sources. The |
* is done to support dynamic sources. The |
|
|
* wildcarding form. |
* wildcarding form. |
* :S<d><pat1><d><pat2><d>[g] |
* :S<d><pat1><d><pat2><d>[g] |
* Substitute <pat2> for <pat1> in the value |
* 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 |
* :H Substitute the head of each word |
* :T Substitute the tail of each word |
* :T Substitute the tail of each word |
* :E Substitute the extension (minus '.') of |
* :E Substitute the extension (minus '.') of |
|
|
case 'S': |
case 'S': |
{ |
{ |
VarPattern pattern; |
VarPattern pattern; |
register char delim; |
|
Buffer buf; /* Buffer for patterns */ |
|
|
|
pattern.flags = 0; |
pattern.flags = 0; |
delim = tstr[1]; |
delim = tstr[1]; |
tstr += 2; |
tstr += 2; |
|
|
/* |
/* |
* If pattern begins with '^', it is anchored to the |
* If pattern begins with '^', it is anchored to the |
* start of the word -- skip over it and flag pattern. |
* start of the word -- skip over it and flag pattern. |
|
|
tstr += 1; |
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, |
* Check for global substitution. If 'g' after the final |
* '$'s and backslashes (place the escaped character in |
* delimiter, substitution is global and is marked that |
* uninterpreted) and 2) unescaped $'s that aren't before |
* way. |
* the delimiter (expand the variable substitution). |
|
* The result is left in the Buffer buf. |
|
*/ |
*/ |
for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { |
for (;; cp++) { |
if ((*cp == '\\') && |
switch (*cp) { |
((cp[1] == delim) || |
case 'g': |
(cp[1] == '$') || |
pattern.flags |= VAR_SUB_GLOBAL; |
(cp[1] == '\\'))) |
continue; |
{ |
case '1': |
Buf_AddByte(buf, (Byte)cp[1]); |
pattern.flags |= VAR_SUB_ONE; |
cp++; |
continue; |
} 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); |
|
} |
} |
|
break; |
} |
} |
|
|
Buf_AddByte(buf, (Byte)'\0'); |
termc = *cp; |
|
newStr = VarModify(str, VarSubstitute, |
|
(ClientData)&pattern); |
|
|
/* |
/* |
* If lhs didn't end with the delimiter, complain and |
* Free the two strings. |
* return NULL |
|
*/ |
*/ |
if (*cp != delim) { |
free(pattern.lhs); |
*lengthPtr = cp - start + 1; |
free(pattern.rhs); |
if (*freePtr) { |
break; |
free(str); |
} |
} |
#ifndef MAKE_BOOTSTRAP |
Buf_Destroy(buf, TRUE); |
case 'C': |
Error("Unclosed substitution for %s (%c missing)", |
{ |
v->name, delim); |
VarREPattern pattern; |
return (var_Error); |
char *re; |
} |
int error; |
|
|
/* |
pattern.flags = 0; |
* Fetch pattern and destroy buffer, but preserve the data |
delim = tstr[1]; |
* in it, since that's our lhs. Note that Buf_GetAll |
tstr += 2; |
* 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); |
|
|
|
/* |
cp = tstr; |
* 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; |
if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, |
for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { |
NULL, NULL)) == NULL) |
if ((*cp == '\\') && |
goto cleanup; |
((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); |
if ((pattern.replace = VarGetPattern(ctxt, err, &cp, |
Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); |
delim, NULL, NULL, NULL)) == NULL) { |
cp += len - 1; |
free(re); |
if (freeIt) { |
goto cleanup; |
free(cp2); |
} |
} |
|
} else if (*cp == '&') { |
for (;; cp++) { |
Buf_AddBytes(buf, pattern.leftLen, |
switch (*cp) { |
(Byte *)pattern.lhs); |
case 'g': |
} else { |
pattern.flags |= VAR_SUB_GLOBAL; |
Buf_AddByte(buf, (Byte)*cp); |
continue; |
|
case '1': |
|
pattern.flags |= VAR_SUB_ONE; |
|
continue; |
} |
} |
|
break; |
} |
} |
|
|
Buf_AddByte(buf, (Byte)'\0'); |
termc = *cp; |
|
|
/* |
error = regcomp(&pattern.re, re, REG_EXTENDED); |
* If didn't end in delimiter character, complain |
free(re); |
*/ |
if (error) { |
if (*cp != delim) { |
|
*lengthPtr = cp - start + 1; |
*lengthPtr = cp - start + 1; |
if (*freePtr) { |
VarREError(error, &pattern.re, "RE substitution error"); |
free(str); |
free(pattern.replace); |
} |
|
Buf_Destroy(buf, TRUE); |
|
Error("Unclosed substitution for %s (%c missing)", |
|
v->name, delim); |
|
return (var_Error); |
return (var_Error); |
} |
} |
|
|
pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); |
pattern.nsub = pattern.re.re_nsub + 1; |
pattern.rightLen--; |
if (pattern.nsub < 1) |
Buf_Destroy(buf, FALSE); |
pattern.nsub = 1; |
|
if (pattern.nsub > 10) |
/* |
pattern.nsub = 10; |
* Check for global substitution. If 'g' after the final |
pattern.matches = emalloc(pattern.nsub * |
* delimiter, substitution is global and is marked that |
sizeof(regmatch_t)); |
* way. |
newStr = VarModify(str, VarRESubstitute, |
*/ |
(ClientData) &pattern); |
cp++; |
regfree(&pattern.re); |
if (*cp == 'g') { |
free(pattern.replace); |
pattern.flags |= VAR_SUB_GLOBAL; |
free(pattern.matches); |
cp++; |
|
} |
|
|
|
termc = *cp; |
|
newStr = VarModify(str, VarSubstitute, |
|
(ClientData)&pattern); |
|
/* |
|
* Free the two strings. |
|
*/ |
|
free(pattern.lhs); |
|
free(pattern.rhs); |
|
break; |
break; |
} |
} |
|
#endif |
|
case 'Q': |
|
if (tstr[1] == endc || tstr[1] == ':') { |
|
newStr = VarQuote (str); |
|
cp = tstr + 1; |
|
termc = *cp; |
|
break; |
|
} |
|
/*FALLTHRU*/ |
case 'T': |
case 'T': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify (str, VarTail, (ClientData)0); |
newStr = VarModify (str, VarTail, (ClientData)0); |
|
|
} |
} |
} |
} |
return (str); |
return (str); |
|
|
|
cleanup: |
|
*lengthPtr = cp - start + 1; |
|
if (*freePtr) |
|
free(str); |
|
Error("Unclosed substitution for %s (%c missing)", |
|
v->name, delim); |
|
return (var_Error); |
} |
} |
|
|
/*- |
/*- |