diff options
author | harti <harti@FreeBSD.org> | 2005-02-26 08:54:40 +0000 |
---|---|---|
committer | harti <harti@FreeBSD.org> | 2005-02-26 08:54:40 +0000 |
commit | 682993532924ac122793be798c023c620aa9bad1 (patch) | |
tree | 3961139fc2f36a9bf1883902eee8d33547e229ea /usr.bin | |
parent | d20416c2fe1b4b1c6590af7ee771ded74184f943 (diff) | |
download | FreeBSD-src-682993532924ac122793be798c023c620aa9bad1.zip FreeBSD-src-682993532924ac122793be798c023c620aa9bad1.tar.gz |
Major clean up and split the Var_Parse monster function into three
functions: one for the single letter variables, one for the others
and one that does the recursive expansion.
Patches: 7.68-7.79
Submitted by: Max Okumoto <okumoto@ucsd.edu>
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/make/var.c | 1390 |
1 files changed, 714 insertions, 676 deletions
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index c47ebd1..e3ff098 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -844,62 +844,72 @@ VarREError(int err, regex_t *pat, const char *str) free(errbuf); } -/*- - *----------------------------------------------------------------------- - * Var_Parse -- - * Given the start of a variable invocation, extract the variable - * name and find its value, then modify it according to the - * specification. - * - * Results: - * The (possibly-modified) value of the variable or var_Error if the - * specification is invalid. The length of the specification is - * placed in *lengthPtr (for invalid specifications, this is just - * 2 to skip the '$' and the following letter, or 1 if '$' was the - * last character in the string). - * A Boolean in *freePtr telling whether the returned string should - * be freed by the caller. - * - * Side Effects: - * None. - * - * Assumption: - * It is assumed that Var_Parse() is called with str[0] == '$'. - * - *----------------------------------------------------------------------- +/* + * Make sure this variable is fully expanded. */ -char * -Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, - Boolean *freePtr) +static char * +VarExpand(Var *v, GNode *ctxt, Boolean err) { - char *tstr; /* Pointer into str */ - Var *v; /* Variable in invocation */ - Boolean haveModifier; /* TRUE if have modifiers for the variable */ - char endc; /* Ending character when variable in parens - * or braces */ - char startc; /* Starting character when variable in parens - * or braces */ - char *start; - 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 - * result is just the invocation, unaltered */ + char *value; + char *result; - if (str[1] == '\0') { - /* - * Error, there is only a dollar sign in the input string. - */ - *freePtr = FALSE; - *lengthPtr = 1; - return (err ? var_Error : varNoError); - } + if (v->flags & VAR_IN_USE) { + Fatal("Variable %s is recursive.", v->name); + /* NOTREACHED */ + } + + v->flags |= VAR_IN_USE; - if (str[1] == OPEN_PAREN || str[1] == OPEN_BRACE) { /* - * Check if brackets contain a variable name. + * Before doing any modification, we have to make sure the + * value has been fully expanded. If it looks like recursion + * might be necessary (there's a dollar sign somewhere in the + * variable's value) we just call Var_Subst to do any other + * substitutions that are necessary. Note that the value + * returned by Var_Subst will have been + * dynamically-allocated, so it will need freeing when we + * return. */ - int vlen; /* length of variable name, after embedded variable + value = (char *)Buf_GetAll(v->val, (size_t *)NULL); + if (strchr(value, '$') == NULL) { + result = strdup(value); + } else { + Buffer *buf; + + buf = Var_Subst(NULL, value, ctxt, err); + result = Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + } + + v->flags &= ~VAR_IN_USE; + + return (result); +} + +/* + * Check if brackets contain a variable name. + */ +static char * +VarParseLong(char foo[], GNode *ctxt, Boolean err, size_t *lengthPtr, + Boolean *freePtr) +{ + const char *input = foo; + char *rw_str = foo; + + Var *v; /* Variable in invocation */ + const char *vname; + size_t vlen; /* length of variable name, after embedded variable * expansion */ + Boolean haveModifier; /* TRUE if have modifiers for the variable */ + char endc; /* Ending character when variable in parens + * or braces */ + char startc; /* Starting character when variable in parens + * or braces */ + char *tstr; /* Pointer into str */ + 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 + * result is just the invocation, unaltered */ /* build up expanded variable name in this buffer */ Buffer *buf = Buf_Init(MAKE_BSIZE); @@ -908,9 +918,9 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, * Skip to the end character or a colon, whichever comes first, * replacing embedded variables as we go. */ - startc = str[1]; + startc = input[1]; endc = (startc == OPEN_PAREN) ? CLOSE_PAREN : CLOSE_BRACE; - tstr = str + 2; + tstr = rw_str + 2; while (*tstr != '\0' && *tstr != endc && *tstr != ':') { if (*tstr == '$') { @@ -941,39 +951,37 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, * the end of the string, since that's what make does. */ *freePtr = FALSE; - *lengthPtr = tstr - str; + *lengthPtr = tstr - input; return (var_Error); } haveModifier = (*tstr == ':'); *tstr = '\0'; /* modify input string */ - start = str; - Buf_AddByte(buf, (Byte)'\0'); - str = Buf_GetAll(buf, (size_t *)NULL); /* REPLACE str */ - vlen = strlen(str); + vname = Buf_GetAll(buf, (size_t *)NULL); /* REPLACE str */ + vlen = strlen(vname); - v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + v = VarFind(vname, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && - (vlen == 2) && (str[1] == 'F' || str[1] == 'D')) + (vlen == 2) && (vname[1] == 'F' || vname[1] == 'D')) { /* * Check for bogus D and F forms of local variables since we're * in a local context and the name is the right length. */ - if (strchr("!%*<>@", str[0]) != NULL) { - char vname[2]; + if (strchr("!%*<>@", vname[0]) != NULL) { + char name[2]; char *val; /* * Well, it's local -- go look for it. */ - vname[0] = str[0]; - vname[1] = '\0'; + name[0] = vname[0]; + name[1] = '\0'; - v = VarFind(vname, ctxt, 0); + v = VarFind(name, ctxt, 0); if (v != NULL && !haveModifier) { /* * No need for nested expansion or anything, as we're @@ -982,7 +990,7 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, */ val = (char *)Buf_GetAll(v->val, (size_t *)NULL); - if (str[1] == 'D') { + if (vname[1] == 'D') { val = VarModify(val, VarHead, (void *)NULL); } else { val = VarModify(val, VarTail, (void *)NULL); @@ -992,7 +1000,7 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, * tell caller to free it. */ *freePtr = TRUE; - *lengthPtr = tstr - start + 1; + *lengthPtr = tstr - input + 1; *tstr = endc; Buf_Destroy(buf, TRUE); return (val); @@ -1002,7 +1010,8 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, if (v == NULL) { if (((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) && - ((vlen == 1) || ((vlen == 2) && (str[1] == 'F' || str[1] == 'D')))) + ((vlen == 1) || + ((vlen == 2) && (vname[1] == 'F' || vname[1] == 'D')))) { /* * If substituting a local variable in a non-local context, @@ -1013,23 +1022,23 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, * specially as they are the only four that will be set * when dynamic sources are expanded. */ - if (strchr("!%*@", str[0]) != NULL) { + if (strchr("!%*@", vname[0]) != NULL) { dynamic = TRUE; } else { dynamic = FALSE; } } else if (((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) && (vlen > 2) && - (str[0] == '.') && - isupper((unsigned char)str[1])) + (vname[0] == '.') && + isupper((unsigned char)vname[1])) { int len; len = vlen - 1; - if ((strncmp(str, ".TARGET", len) == 0) || - (strncmp(str, ".ARCHIVE", len) == 0) || - (strncmp(str, ".PREFIX", len) == 0) || - (strncmp(str, ".MEMBER", len) == 0)) + if ((strncmp(vname, ".TARGET", len) == 0) || + (strncmp(vname, ".ARCHIVE", len) == 0) || + (strncmp(vname, ".PREFIX", len) == 0) || + (strncmp(vname, ".MEMBER", len) == 0)) { dynamic = TRUE; } else { @@ -1044,7 +1053,8 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, * Still need to get to the end of the variable specification, * so kludge up a Var structure for the modifications */ - v = VarCreate(str, NULL, VAR_JUNK); + v = VarCreate(vname, NULL, VAR_JUNK); + } else { /* * No modifiers -- have specification length so we can return @@ -1052,10 +1062,10 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, */ if (dynamic) { char *result; - size_t rlen = tstr - start + 1; + size_t rlen = tstr - input + 1; result = emalloc(rlen + 1); - strncpy(result, start, rlen); + strncpy(result, input, rlen); result[rlen] = '\0'; *freePtr = TRUE; @@ -1066,7 +1076,7 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, return (result); } else { *freePtr = FALSE; - *lengthPtr = tstr - start + 1; + *lengthPtr = tstr - input + 1; *tstr = endc; Buf_Destroy(buf, TRUE); @@ -1079,680 +1089,708 @@ Var_Parse(char *str, GNode *ctxt, Boolean err, size_t *lengthPtr, *freePtr = FALSE; Buf_Destroy(buf, TRUE); - } else { - /* - * If it's not bounded by braces of some sort, life is much simpler. - * We just need to check for the first character and return the - * value if it exists. - */ - char name[2]; - - name[0] = str[1]; - name[1] = '\0'; - - v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - if (v == NULL) { - if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set - * when dynamic sources are expanded. - */ - /* XXX: It looks like $% and $! are reversed here */ - *freePtr = FALSE; - *lengthPtr = 2; - switch (str[1]) { - case '@': - return ("$(.TARGET)"); - case '%': - return ("$(.ARCHIVE)"); - case '*': - return ("$(.PREFIX)"); - case '!': - return ("$(.MEMBER)"); - default: - return (err ? var_Error : varNoError); - } - } else { - *freePtr = FALSE; - *lengthPtr = 2; - return (err ? var_Error : varNoError); - } - } else { - dynamic = FALSE; - start = str; - *freePtr = FALSE; - *lengthPtr = 2; - haveModifier = FALSE; - startc = 0; - endc = str[1]; - tstr = &str[1]; - } - } + rw_str = VarExpand(v, ctxt, err); + *freePtr = TRUE; /* - * Make sure this variable is fully expanded. - * XXX This section really should be its own function. + * Now we need to apply any modifiers the user wants applied. + * These are: + * :M<pattern> + * words which match the given <pattern>. + * <pattern> is of the standard file + * 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 + * each word + * :R Substitute the root of each word + * (pathname minus the suffix). + * :lhs=rhs + * Like :S, but the rhs goes to the end of + * the invocation. + * :U Converts variable to upper-case. + * :L Converts variable to lower-case. */ - { - if (v->flags & VAR_IN_USE) { - Fatal("Variable %s is recursive.", v->name); - /* NOTREACHED */ - } else { - v->flags |= VAR_IN_USE; - } - - /* - * Before doing any modification, we have to make sure the - * value has been fully expanded. If it looks like recursion - * might be necessary (there's a dollar sign somewhere in the - * variable's value) we just call Var_Subst to do any other - * substitutions that are necessary. Note that the value - * returned by Var_Subst will have been - * dynamically-allocated, so it will need freeing when we - * return. - */ - str = (char *)Buf_GetAll(v->val, (size_t *)NULL); - if (strchr(str, '$') != NULL) { - Buffer *buf; + if (haveModifier) { + char *cp; + /* + * Skip initial colon while putting it back. + */ + *tstr++ = ':'; + while (*tstr != endc) { + char *newStr; /* New value to return */ + char termc; /* Character which terminated scan */ + + DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *tstr, rw_str)); + switch (*tstr) { + case 'N': + case 'M': + { + char *pattern; + char *cp2; + Boolean copy; - buf = Var_Subst(NULL, str, ctxt, err); - str = Buf_GetAll(buf, NULL); - Buf_Destroy(buf, FALSE); + copy = FALSE; + for (cp = tstr + 1; + *cp != '\0' && *cp != ':' && *cp != endc; + cp++) + { + if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)) { + copy = TRUE; + cp++; + } + } + termc = *cp; + *cp = '\0'; + if (copy) { + /* + * Need to compress the \:'s out of the pattern, so + * allocate enough room to hold the uncompressed + * pattern (note that cp started at tstr+1, so + * cp - tstr takes the null byte into account) and + * compress the pattern into the space. + */ + pattern = emalloc(cp - tstr); + for (cp2 = pattern, cp = tstr + 1; + *cp != '\0'; + cp++, cp2++) + { + if ((*cp == '\\') && + (cp[1] == ':' || cp[1] == endc)) { + cp++; + } + *cp2 = *cp; + } + *cp2 = '\0'; + } else { + pattern = &tstr[1]; + } + if (*tstr == 'M' || *tstr == 'm') { + newStr = VarModify(rw_str, VarMatch, pattern); + } else { + newStr = VarModify(rw_str, VarNoMatch, pattern); + } + if (copy) { + free(pattern); + } + break; + } + case 'S': + { + VarPattern pattern; + char del; + Buffer *buf; /* Buffer for patterns */ - *freePtr = TRUE; - } - v->flags &= ~VAR_IN_USE; - } + pattern.flags = 0; + del = tstr[1]; + tstr += 2; - /* - * Now we need to apply any modifiers the user wants applied. - * These are: - * :M<pattern> words which match the given <pattern>. - * <pattern> is of the standard file - * 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 - * each word - * :R Substitute the root of each word - * (pathname minus the suffix). - * :lhs=rhs Like :S, but the rhs goes to the end of - * the invocation. - * :U Converts variable to upper-case. - * :L Converts variable to lower-case. - */ - if ((str != NULL) && haveModifier) { - char *cp; - /* - * Skip initial colon while putting it back. - */ - *tstr++ = ':'; - while (*tstr != endc) { - char *newStr; /* New value to return */ - char termc; /* Character which terminated scan */ - - DEBUGF(VAR, ("Applying :%c to \"%s\"\n", *tstr, str)); - switch (*tstr) { - case 'N': - case 'M': - { - char *pattern; - char *cp2; - Boolean copy; - - copy = FALSE; - for (cp = tstr + 1; - *cp != '\0' && *cp != ':' && *cp != endc; - cp++) - { - if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)) { - copy = TRUE; - cp++; + /* + * If pattern begins with '^', it is anchored to the + * start of the word -- skip over it and flag pattern. + */ + if (*tstr == '^') { + pattern.flags |= VAR_MATCH_START; + tstr += 1; } - } - termc = *cp; - *cp = '\0'; - if (copy) { + + buf = Buf_Init(0); + /* - * Need to compress the \:'s out of the pattern, so - * allocate enough room to hold the uncompressed - * pattern (note that cp started at tstr+1, so - * cp - tstr takes the null byte into account) and - * compress the pattern into the space. + * 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. */ - pattern = emalloc(cp - tstr); - for (cp2 = pattern, cp = tstr + 1; - *cp != '\0'; - cp++, cp2++) - { + for (cp = tstr; *cp != '\0' && *cp != del; cp++) { if ((*cp == '\\') && - (cp[1] == ':' || cp[1] == endc)) { - cp++; + ((cp[1] == del) || + (cp[1] == '$') || + (cp[1] == '\\'))) + { + Buf_AddByte(buf, (Byte)cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] != del) { + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + char *cp2; + size_t len; + Boolean freeIt; + + cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); + Buf_Append(buf, 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); } - *cp2 = *cp; } - *cp2 = '\0'; - } else { - pattern = &tstr[1]; - } - if (*tstr == 'M' || *tstr == 'm') { - newStr = VarModify(str, VarMatch, pattern); - } else { - newStr = VarModify(str, VarNoMatch, pattern); - } - if (copy) { - free(pattern); - } - break; - } - case 'S': - { - VarPattern pattern; - char del; - Buffer *buf; /* Buffer for patterns */ - pattern.flags = 0; - del = tstr[1]; - tstr += 2; + Buf_AddByte(buf, (Byte)'\0'); - /* - * If pattern begins with '^', it is anchored to the - * start of the word -- skip over it and flag pattern. - */ - if (*tstr == '^') { - pattern.flags |= VAR_MATCH_START; - tstr += 1; - } + /* + * If lhs didn't end with the delimiter, complain and + * exit. + */ + if (*cp != del) { + Fatal("Unclosed substitution for %s (%c missing)", + v->name, del); + } + + /* + * 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); - buf = Buf_Init(0); + /* + * 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); - /* - * 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. - */ - for (cp = tstr; *cp != '\0' && *cp != del; cp++) { - if ((*cp == '\\') && - ((cp[1] == del) || - (cp[1] == '$') || - (cp[1] == '\\'))) - { - Buf_AddByte(buf, (Byte)cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] != del) { - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - char *cp2; + tstr = cp + 1; + for (cp = tstr; *cp != '\0' && *cp != del; cp++) { + if ((*cp == '\\') && + ((cp[1] == del) || + (cp[1] == '&') || + (cp[1] == '\\') || + (cp[1] == '$'))) + { + Buf_AddByte(buf, (Byte)cp[1]); + cp++; + } else if ((*cp == '$') && (cp[1] != del)) { + char *cp2; size_t len; - Boolean freeIt; + Boolean freeIt; cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); Buf_Append(buf, cp2); + cp += len - 1; if (freeIt) { free(cp2); } - cp += len - 1; + } else if (*cp == '&') { + Buf_AddBytes(buf, pattern.leftLen, + (Byte *)pattern.lhs); } else { - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - pattern.flags |= VAR_MATCH_END; + Buf_AddByte(buf, (Byte)*cp); } - } else { - Buf_AddByte(buf, (Byte)*cp); } - } - Buf_AddByte(buf, (Byte)'\0'); + Buf_AddByte(buf, (Byte)'\0'); - /* - * If lhs didn't end with the delimiter, complain and - * exit. - */ - if (*cp != del) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, del); - } + /* + * If didn't end in delimiter character, complain + */ + if (*cp != del) { + Fatal("Unclosed substitution for %s (%c missing)", + v->name, del); + } - /* - * 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.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); + pattern.rightLen--; + Buf_Destroy(buf, FALSE); - /* - * 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 != del; cp++) { - if ((*cp == '\\') && - ((cp[1] == del) || - (cp[1] == '&') || - (cp[1] == '\\') || - (cp[1] == '$'))) - { - Buf_AddByte(buf, (Byte)cp[1]); + /* + * 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++; - } else if ((*cp == '$') && (cp[1] != del)) { - char *cp2; - size_t len; - Boolean freeIt; - - cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); - Buf_Append(buf, 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); } - } - Buf_AddByte(buf, (Byte)'\0'); - - /* - * If didn't end in delimiter character, complain - */ - if (*cp != del) { - Fatal("Unclosed substitution for %s (%c missing)", - v->name, del); - } - - pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); - pattern.rightLen--; - Buf_Destroy(buf, FALSE); + /* + * Global substitution of the empty string causes an + * infinite number of matches, unless anchored by '^' + * (start of string) or '$' (end of string). Catch the + * infinite substitution here. + * Note that flags can only contain the 3 bits we're + * interested in so we don't have to mask unrelated + * bits. We can test for equality. + */ + if (!pattern.leftLen && pattern.flags == VAR_SUB_GLOBAL) + Fatal("Global substitution of the empty string"); - /* - * 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++; + termc = *cp; + newStr = VarModify(rw_str, VarSubstitute, &pattern); + /* + * Free the two strings. + */ + free(pattern.lhs); + free(pattern.rhs); + break; } + case 'C': + { + int delim; + VarREPattern pattern; + char *re; + int error; - /* - * Global substitution of the empty string causes an - * infinite number of matches, unless anchored by '^' - * (start of string) or '$' (end of string). Catch the - * infinite substitution here. - * Note that flags can only contain the 3 bits we're - * interested in so we don't have to mask unrelated - * bits. We can test for equality. - */ - if (!pattern.leftLen && pattern.flags == VAR_SUB_GLOBAL) - Fatal("Global substitution of the empty string"); + pattern.flags = 0; + delim = tstr[1]; + tstr += 2; - termc = *cp; - newStr = VarModify(str, VarSubstitute, &pattern); - /* - * Free the two strings. - */ - free(pattern.lhs); - free(pattern.rhs); - break; - } - case 'C': - { - int delim; - VarREPattern pattern; - char *re; - int error; - - pattern.flags = 0; - delim = tstr[1]; - tstr += 2; - - cp = tstr; - - if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, - NULL, NULL)) == NULL) { - /* was: goto cleanup */ - *lengthPtr = cp - start + 1; - if (*freePtr) - free(str); - if (delim != '\0') - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } + cp = tstr; - if ((pattern.replace = VarGetPattern(ctxt, err, &cp, - delim, NULL, NULL, NULL)) == NULL){ - free(re); + if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, + NULL, NULL)) == NULL) { + /* was: goto cleanup */ + *lengthPtr = cp - input + 1; + if (*freePtr) + free(rw_str); + if (delim != '\0') + Fatal("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); + } - /* was: goto cleanup */ - *lengthPtr = cp - start + 1; - if (*freePtr) - free(str); - if (delim != '\0') - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } + if ((pattern.replace = VarGetPattern(ctxt, err, &cp, + delim, NULL, NULL, NULL)) == NULL){ + free(re); + + /* was: goto cleanup */ + *lengthPtr = cp - input + 1; + if (*freePtr) + free(rw_str); + if (delim != '\0') + Fatal("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); + } - for (;; cp++) { - switch (*cp) { - case 'g': - pattern.flags |= VAR_SUB_GLOBAL; - continue; - case '1': - pattern.flags |= VAR_SUB_ONE; - continue; - default: + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + default: + break; + } break; } - break; - } - - termc = *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); - } - - 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, &pattern); - regfree(&pattern.re); - free(pattern.replace); - free(pattern.matches); - break; - } - case 'L': - if (tstr[1] == endc || tstr[1] == ':') { - Buffer *buf; - buf = Buf_Init(MAKE_BSIZE); - for (cp = str; *cp ; cp++) - Buf_AddByte(buf, (Byte)tolower(*cp)); - Buf_AddByte(buf, (Byte)'\0'); - newStr = (char *)Buf_GetAll(buf, (size_t *)NULL); - Buf_Destroy(buf, FALSE); - - cp = tstr + 1; - termc = *cp; - break; - } - /* FALLTHROUGH */ - case 'O': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarSortWords(str, SortIncreasing); - cp = tstr + 1; - termc = *cp; - break; - } - /* FALLTHROUGH */ - case 'Q': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = Var_Quote(str); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'T': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(str, VarTail, (void *)NULL); - cp = tstr + 1; termc = *cp; - break; - } - /*FALLTHRU*/ - case 'U': - if (tstr[1] == endc || tstr[1] == ':') { - Buffer *buf; - buf = Buf_Init(MAKE_BSIZE); - for (cp = str; *cp ; cp++) - Buf_AddByte(buf, (Byte)toupper(*cp)); - Buf_AddByte(buf, (Byte)'\0'); - newStr = (char *)Buf_GetAll(buf, (size_t *)NULL); - Buf_Destroy(buf, FALSE); - - cp = tstr + 1; - termc = *cp; - break; - } - /* FALLTHROUGH */ - case 'H': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(str, VarHead, (void *)NULL); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'E': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(str, VarSuffix, (void *)NULL); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ - case 'R': - if (tstr[1] == endc || tstr[1] == ':') { - newStr = VarModify(str, VarRoot, (void *)NULL); - cp = tstr + 1; - termc = *cp; - break; - } - /*FALLTHRU*/ -#ifdef SUNSHCMD - case 's': - if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { - const char *error; - Buffer *buf; - - buf = Cmd_Exec(str, &error); - newStr = Buf_GetAll(buf, NULL); - Buf_Destroy(buf, FALSE); + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *lengthPtr = cp - input + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + return (var_Error); + } - if (error) - Error(error, str); - cp = tstr + 2; - termc = *cp; + 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(rw_str, VarRESubstitute, &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); break; } - /*FALLTHRU*/ -#endif - default: - { -#ifdef SYSVVARSUB - /* - * This can either be a bogus modifier or a System-V - * substitution command. - */ - VarPattern pattern; - Boolean eqFound; - int cnt; - - pattern.flags = 0; - eqFound = FALSE; - /* - * First we make a pass through the string trying - * to verify it is a SYSV-make-style translation: - * it must be: <string1>=<string2>) - */ - cp = tstr; - cnt = 1; - while (*cp != '\0' && cnt) { - if (*cp == '=') { - eqFound = TRUE; - /* continue looking for endc */ + case 'L': + if (tstr[1] == endc || tstr[1] == ':') { + Buffer *buf; + buf = Buf_Init(MAKE_BSIZE); + for (cp = rw_str; *cp ; cp++) + Buf_AddByte(buf, (Byte)tolower(*cp)); + + Buf_AddByte(buf, (Byte)'\0'); + newStr = (char *)Buf_GetAll(buf, (size_t *)NULL); + Buf_Destroy(buf, FALSE); + + cp = tstr + 1; + termc = *cp; + break; } - else if (*cp == endc) - cnt--; - else if (*cp == startc) - cnt++; - if (cnt) - cp++; - } - if (*cp == endc && eqFound) { - int delim; + /* FALLTHROUGH */ + case 'O': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarSortWords(rw_str, SortIncreasing); + cp = tstr + 1; + termc = *cp; + break; + } + /* FALLTHROUGH */ + case 'Q': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = Var_Quote(rw_str); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ + case 'T': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(rw_str, VarTail, (void *)NULL); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ + case 'U': + if (tstr[1] == endc || tstr[1] == ':') { + Buffer *buf; + buf = Buf_Init(MAKE_BSIZE); + for (cp = rw_str; *cp ; cp++) + Buf_AddByte(buf, (Byte)toupper(*cp)); + + Buf_AddByte(buf, (Byte)'\0'); + newStr = (char *)Buf_GetAll(buf, (size_t *)NULL); + Buf_Destroy(buf, FALSE); + + cp = tstr + 1; + termc = *cp; + break; + } + /* FALLTHROUGH */ + case 'H': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(rw_str, VarHead, (void *)NULL); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ + case 'E': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(rw_str, VarSuffix, (void *)NULL); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ + case 'R': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(rw_str, VarRoot, (void *)NULL); + cp = tstr + 1; + termc = *cp; + break; + } + /*FALLTHRU*/ + #ifdef SUNSHCMD + case 's': + if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { + const char *error; + Buffer *buf; + + buf = Cmd_Exec(rw_str, &error); + newStr = Buf_GetAll(buf, NULL); + Buf_Destroy(buf, FALSE); + + if (error) + Error(error, rw_str); + cp = tstr + 2; + termc = *cp; + break; + } + /*FALLTHRU*/ + #endif + default: + { + #ifdef SYSVVARSUB + /* + * This can either be a bogus modifier or a System-V + * substitution command. + */ + VarPattern pattern; + Boolean eqFound; + int cnt; + pattern.flags = 0; + eqFound = FALSE; /* - * Now we break this sucker into the lhs and - * rhs. We must null terminate them of course. + * First we make a pass through the string trying + * to verify it is a SYSV-make-style translation: + * it must be: <string1>=<string2>) */ cp = tstr; - - delim = '='; - if ((pattern.lhs = VarGetPattern(ctxt, - err, &cp, delim, &pattern.flags, &pattern.leftLen, - NULL)) == NULL) { - /* was: goto cleanup */ - *lengthPtr = cp - start + 1; - if (*freePtr) - free(str); - if (delim != '\0') - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); + cnt = 1; + while (*cp != '\0' && cnt) { + if (*cp == '=') { + eqFound = TRUE; + /* continue looking for endc */ + } + else if (*cp == endc) + cnt--; + else if (*cp == startc) + cnt++; + if (cnt) + cp++; } + if (*cp == endc && eqFound) { + int delim; - delim = endc; - if ((pattern.rhs = VarGetPattern(ctxt, - err, &cp, delim, NULL, &pattern.rightLen, - &pattern)) == NULL) { - /* was: goto cleanup */ - *lengthPtr = cp - start + 1; - if (*freePtr) - free(str); - if (delim != '\0') - Fatal("Unclosed substitution for %s (%c missing)", - v->name, delim); - return (var_Error); - } + /* + * Now we break this sucker into the lhs and + * rhs. We must null terminate them of course. + */ + cp = tstr; + + delim = '='; + if ((pattern.lhs = VarGetPattern(ctxt, + err, &cp, delim, &pattern.flags, &pattern.leftLen, + NULL)) == NULL) { + /* was: goto cleanup */ + *lengthPtr = cp - input + 1; + if (*freePtr) + free(rw_str); + if (delim != '\0') + Fatal("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); + } - /* - * SYSV modifications happen through the whole - * string. Note the pattern is anchored at the end. - */ - termc = *--cp; - delim = '\0'; - newStr = VarModify(str, VarSYSVMatch, &pattern); + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, + err, &cp, delim, NULL, &pattern.rightLen, + &pattern)) == NULL) { + /* was: goto cleanup */ + *lengthPtr = cp - input + 1; + if (*freePtr) + free(rw_str); + if (delim != '\0') + Fatal("Unclosed substitution for %s (%c missing)", + v->name, delim); + return (var_Error); + } - free(pattern.lhs); - free(pattern.rhs); + /* + * SYSV modifications happen through the whole + * string. Note the pattern is anchored at the end. + */ + termc = *--cp; + delim = '\0'; + newStr = VarModify(rw_str, VarSYSVMatch, &pattern); - termc = endc; - } else -#endif - { - Error("Unknown modifier '%c'\n", *tstr); - for (cp = tstr+1; - *cp != ':' && *cp != endc && *cp != '\0'; - cp++) - continue; - termc = *cp; - newStr = var_Error; + free(pattern.lhs); + free(pattern.rhs); + + termc = endc; + } else + #endif + { + Error("Unknown modifier '%c'\n", *tstr); + for (cp = tstr+1; + *cp != ':' && *cp != endc && *cp != '\0'; + cp++) + continue; + termc = *cp; + newStr = var_Error; + } } } + DEBUGF(VAR, ("Result is \"%s\"\n", newStr)); + + if (*freePtr) { + free(rw_str); + } + rw_str = newStr; + if (rw_str != var_Error) { + *freePtr = TRUE; + } else { + *freePtr = FALSE; + } + if (termc == '\0') { + Error("Unclosed variable specification for %s", v->name); + } else if (termc == ':') { + *cp++ = termc; + } else { + *cp = termc; + } + tstr = cp; } - DEBUGF(VAR, ("Result is \"%s\"\n", newStr)); + *lengthPtr = tstr - input + 1; + } else { + *lengthPtr = tstr - input + 1; + *tstr = endc; + } + if (v->flags & VAR_FROM_ENV) { + if (rw_str == (char *)Buf_GetAll(v->val, (size_t *)NULL)) { + /* + * Returning the value unmodified, so tell the caller to free + * the thing. + */ + *freePtr = TRUE; + VarDestroy(v, FALSE); + return (rw_str); + } else { + VarDestroy(v, TRUE); + return (rw_str); + } + } else if (v->flags & VAR_JUNK) { + /* + * Perform any free'ing needed and set *freePtr to FALSE so the caller + * doesn't try to free a static pointer. + */ if (*freePtr) { - free(str); + free(rw_str); } - str = newStr; - if (str != var_Error) { + if (dynamic) { + *freePtr = FALSE; + VarDestroy(v, TRUE); + rw_str = emalloc(*lengthPtr + 1); + strncpy(rw_str, input, *lengthPtr); + rw_str[*lengthPtr] = '\0'; *freePtr = TRUE; + return (rw_str); } else { *freePtr = FALSE; + VarDestroy(v, TRUE); + return (err ? var_Error : varNoError); } - if (termc == '\0') { - Error("Unclosed variable specification for %s", v->name); - } else if (termc == ':') { - *cp++ = termc; - } else { - *cp = termc; - } - tstr = cp; - } - *lengthPtr = tstr - start + 1; - } else { - *lengthPtr = tstr - start + 1; - *tstr = endc; - } - - if (v->flags & VAR_FROM_ENV) { - Boolean destroy = FALSE; - - if (str != (char *)Buf_GetAll(v->val, (size_t *)NULL)) { - destroy = TRUE; } else { - /* - * Returning the value unmodified, so tell the caller to free - * the thing. - */ - *freePtr = TRUE; + return (rw_str); } - VarDestroy(v, destroy); - } else if (v->flags & VAR_JUNK) { +} + +static char * +VarParseShort(const char input[], GNode *ctxt, Boolean err, size_t *lengthPtr, + Boolean *freePtr) +{ /* - * Perform any free'ing needed and set *freePtr to FALSE so the caller - * doesn't try to free a static pointer. + * If it's not bounded by braces of some sort, life is much simpler. + * We just need to check for the first character and return the value + * if it exists. */ - if (*freePtr) { - free(str); + Var *v; /* Variable in invocation */ + char name[2]; + + name[0] = input[1]; + name[1] = '\0'; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + if (v == NULL) { + if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { + /* + * If substituting a local variable in a non-local + * context, assume it's for dynamic source stuff. We + * have to handle this specially and return the + * longhand for the variable with the dollar sign + * escaped so it makes it back to the caller. Only + * four of the local variables are treated specially + * as they are the only four that will be set when + * dynamic sources are expanded. + */ + /* XXX: It looks like $% and $! are reversed here */ + *freePtr = FALSE; + *lengthPtr = 2; + switch (input[1]) { + case '@': + return ("$(.TARGET)"); + case '%': + return ("$(.ARCHIVE)"); + case '*': + return ("$(.PREFIX)"); + case '!': + return ("$(.MEMBER)"); + default: + return (err ? var_Error : varNoError); + } + } else { + *freePtr = FALSE; + *lengthPtr = 2; + return (err ? var_Error : varNoError); + } + } else { + char *result; + + result = VarExpand(v, ctxt, err); + + if (v->flags & VAR_FROM_ENV) { + VarDestroy(v, TRUE); + } + + *freePtr = TRUE; + *lengthPtr = 2; + return (result); } - *freePtr = FALSE; - VarDestroy(v, TRUE); - if (dynamic) { - str = emalloc(*lengthPtr + 1); - strncpy(str, start, *lengthPtr); - str[*lengthPtr] = '\0'; - *freePtr = TRUE; +} + +/*- + *----------------------------------------------------------------------- + * Var_Parse -- + * Given the start of a variable invocation, extract the variable + * name and find its value, then modify it according to the + * specification. + * + * Results: + * The (possibly-modified) value of the variable or var_Error if the + * specification is invalid. The length of the specification is + * placed in *lengthPtr (for invalid specifications, this is just + * 2 to skip the '$' and the following letter, or 1 if '$' was the + * last character in the string). + * A Boolean in *freePtr telling whether the returned string should + * be freed by the caller. + * + * Side Effects: + * None. + * + * Assumption: + * It is assumed that Var_Parse() is called with str[0] == '$'. + * + *----------------------------------------------------------------------- + */ +char * +Var_Parse(char *foo, GNode *ctxt, Boolean err, size_t *lengthPtr, + Boolean *freePtr) +{ + const char *input = foo; + + if (input[1] == '\0') { + /* Error, there is only a dollar sign in the input string. */ + *freePtr = FALSE; + *lengthPtr = 1; + return (err ? var_Error : varNoError); + + } else if (input[1] == OPEN_PAREN || input[1] == OPEN_BRACE) { + /* multi letter variable name */ + return (VarParseLong(foo, ctxt, err, lengthPtr, freePtr)); + } else { - str = err ? var_Error : varNoError; + /* single letter variable name */ + return (VarParseShort(input, ctxt, err, lengthPtr, freePtr)); } - } - return (str); } /*- |