version 1.41, 2000/07/17 23:01:20 |
version 1.42, 2000/07/17 23:06:32 |
|
|
VarPattern *)); |
VarPattern *)); |
static char *VarQuote __P((const char *)); |
static char *VarQuote __P((const char *)); |
static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *)); |
static char *VarModify __P((const char *, Boolean (*)(const char *, Boolean, Buffer, void *), void *)); |
|
char *VarModifiers_Apply __P((char *, SymTable *, Boolean, |
|
Boolean *, char *, char, size_t *)); |
static void VarPrintVar __P((void *)); |
static void VarPrintVar __P((void *)); |
static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *)); |
static Boolean VarUppercase __P((const char *, Boolean, Buffer, void *)); |
static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *)); |
static Boolean VarLowercase __P((const char *, Boolean, Buffer, void *)); |
|
|
|
|
if ((flags & FIND_ENV)) { |
if ((flags & FIND_ENV)) { |
char *env; |
char *env; |
|
char *n; |
|
|
v = getvar(VAR_ENV, name, end, k); |
v = getvar(VAR_ENV, name, end, k); |
if (v != NULL) |
if (v != NULL) |
return v; |
return v; |
|
|
if ((env = getenv(name)) != NULL) |
/* getenv requires a null-terminated name */ |
|
n = interval_dup(name, end); |
|
env = getenv(n); |
|
free(n); |
|
if (env != NULL) |
return VarAdd(name, env, VAR_ENV); |
return VarAdd(name, env, VAR_ENV); |
} |
} |
|
|
|
|
size_t *lengthPtr; /* OUT: The length of the specification */ |
size_t *lengthPtr; /* OUT: The length of the specification */ |
Boolean *freePtr; /* OUT: TRUE if caller should free result */ |
Boolean *freePtr; /* OUT: TRUE if caller should free result */ |
{ |
{ |
register char *tstr; /* Pointer into str */ |
char *tstr; /* Pointer into str */ |
Var *v; /* Variable in invocation */ |
Var *v; /* Variable in invocation */ |
char *cp; /* Secondary pointer into str (place marker |
|
* 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 |
char endc; /* Ending character when variable in parens |
* or braces */ |
* or braces */ |
register char startc=0; /* Starting character when variable in parens |
|
* or braces */ |
|
int cnt; /* Used to count brace pairs when variable in |
|
* 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 |
|
|
*/ |
*/ |
switch (str[1]) { |
switch (str[1]) { |
case '@': |
case '@': |
return("$(.TARGET)"); |
return "$(.TARGET)"; |
case '%': |
case '%': |
return("$(.ARCHIVE)"); |
return "$(.ARCHIVE)"; |
case '*': |
case '*': |
return("$(.PREFIX)"); |
return "$(.PREFIX)"; |
case '!': |
case '!': |
return("$(.MEMBER)"); |
return "$(.MEMBER)"; |
} |
} |
} |
} |
/* |
/* Error. */ |
* Error |
return err ? var_Error : varNoError; |
*/ |
|
return (err ? var_Error : varNoError); |
|
} else { |
} else { |
haveModifier = FALSE; |
haveModifier = FALSE; |
tstr = &str[1]; |
tstr = &str[1]; |
endc = str[1]; |
endc = str[1]; |
} |
} |
} else { |
} else { |
startc = str[1]; |
endc = str[1] == '(' ? ')' : '}'; |
endc = startc == '(' ? ')' : '}'; |
|
|
|
/* |
/* Skip to the end character or a colon, whichever comes first. */ |
* Skip to the end character or a colon, whichever comes first. |
for (tstr = str + 2; *tstr != '\0' && *tstr != endc && *tstr != ':';) |
*/ |
tstr++; |
for (tstr = str + 2; |
if (*tstr == ':') |
*tstr != '\0' && *tstr != endc && *tstr != ':'; |
|
tstr++) |
|
{ |
|
continue; |
|
} |
|
if (*tstr == ':') { |
|
haveModifier = TRUE; |
haveModifier = TRUE; |
} else if (*tstr != '\0') { |
else if (*tstr != '\0') |
haveModifier = FALSE; |
haveModifier = FALSE; |
} else { |
else { |
/* |
/* |
* If we never did find the end character, return NULL |
* If we never did find the end character, return NULL |
* right now, setting the length to be the distance to |
* right now, setting the length to be the distance to |
* the end of the string, since that's what make does. |
* the end of the string, since that's what make does. |
*/ |
*/ |
*lengthPtr = tstr - str; |
*lengthPtr = tstr - str; |
return (var_Error); |
return var_Error; |
} |
} |
*tstr = '\0'; |
|
|
|
v = VarFind_interval(str + 2, tstr, ctxt, FIND_ENV | FIND_MINE); |
v = VarFind_interval(str + 2, tstr, ctxt, FIND_ENV | FIND_MINE); |
if (v == NULL && ctxt != CTXT_CMD && ctxt != CTXT_GLOBAL && |
if (v == NULL && ctxt != CTXT_CMD && ctxt != CTXT_GLOBAL && |
|
|
* Check for bogus D and F forms of local variables since we're |
* Check for bogus D and F forms of local variables since we're |
* in a local context and the name is the right length. |
* in a local context and the name is the right length. |
*/ |
*/ |
switch(str[2]) { |
switch (str[2]) { |
case '@': |
case '@': |
case '%': |
case '%': |
case '*': |
case '*': |
|
|
v = VarFind_interval(str+2, str+3, ctxt, 0); |
v = VarFind_interval(str+2, str+3, ctxt, 0); |
|
|
if (v != NULL) { |
if (v != NULL) { |
/* |
/* No need for nested expansion or anything, as we're |
* No need for nested expansion or anything, as we're |
|
* the only one who sets these things and we sure don't |
* the only one who sets these things and we sure don't |
* but nested invocations in them... |
* but nested invocations in them... */ |
*/ |
|
val = VarValue(v); |
val = VarValue(v); |
|
|
if (str[3] == 'D') { |
if (str[3] == 'D') |
val = VarModify(val, VarHead, NULL); |
val = Var_GetHead(val); |
} else { |
else |
val = VarModify(val, VarTail, NULL); |
val = Var_GetTail(val); |
} |
/* Resulting string is dynamically allocated, so |
/* |
* tell caller to free it. */ |
* Resulting string is dynamically allocated, so |
|
* tell caller to free it. |
|
*/ |
|
*freePtr = TRUE; |
*freePtr = TRUE; |
*lengthPtr = tstr-start+1; |
*lengthPtr = tstr-start+1; |
*tstr = endc; |
*tstr = endc; |
return(val); |
return val; |
} |
} |
break; |
break; |
} |
} |
|
|
|
|
if (v == NULL) { |
if (v == NULL) { |
if ((tstr-str == 3 || |
if ((tstr-str == 3 || |
((tstr-str == 4 && (str[3] == 'F' || |
(tstr-str == 4 && (str[3] == 'F' || |
str[3] == 'D')))) && |
str[3] == 'D'))) && |
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
{ |
{ |
/* |
/* If substituting a local variable in a non-local context, |
* If substituting a local variable in a non-local context, |
|
* assume it's for dynamic source stuff. We have to handle |
* assume it's for dynamic source stuff. We have to handle |
* this specially and return the longhand for the variable |
* this specially and return the longhand for the variable |
* with the dollar sign escaped so it makes it back to the |
* with the dollar sign escaped so it makes it back to the |
* caller. Only four of the local variables are treated |
* caller. Only four of the local variables are treated |
* specially as they are the only four that will be set |
* specially as they are the only four that will be set |
* when dynamic sources are expanded. |
* when dynamic sources are expanded. */ |
*/ |
|
switch (str[2]) { |
switch (str[2]) { |
case '@': |
case '@': |
case '%': |
case '%': |
|
|
dynamic = TRUE; |
dynamic = TRUE; |
break; |
break; |
} |
} |
} else if (((tstr-str) > 4) && (str[2] == '.') && |
} else if (tstr-str > 4 && str[2] == '.' && |
isupper((unsigned char) str[3]) && |
isupper((unsigned char) str[3]) && |
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
{ |
{ |
|
|
if ((strncmp(str+2, ".TARGET", len) == 0) || |
if ((strncmp(str+2, ".TARGET", len) == 0) || |
(strncmp(str+2, ".ARCHIVE", len) == 0) || |
(strncmp(str+2, ".ARCHIVE", len) == 0) || |
(strncmp(str+2, ".PREFIX", len) == 0) || |
(strncmp(str+2, ".PREFIX", len) == 0) || |
(strncmp(str+2, ".MEMBER", len) == 0)) |
(strncmp(str+2, ".MEMBER", len) == 0)) { |
{ |
|
dynamic = TRUE; |
dynamic = TRUE; |
} |
} |
} |
} |
|
|
if (!haveModifier) { |
if (!haveModifier) { |
/* |
/* No modifiers -- have specification length so we can return |
* No modifiers -- have specification length so we can return |
* now. */ |
* now. |
|
*/ |
|
*lengthPtr = tstr - start + 1; |
*lengthPtr = tstr - start + 1; |
*tstr = endc; |
*tstr = endc; |
if (dynamic) { |
if (dynamic) { |
str = emalloc(*lengthPtr + 1); |
char *n; |
strncpy(str, start, *lengthPtr); |
n = emalloc(*lengthPtr + 1); |
str[*lengthPtr] = '\0'; |
strncpy(n, start, *lengthPtr); |
|
n[*lengthPtr] = '\0'; |
*freePtr = TRUE; |
*freePtr = TRUE; |
return(str); |
return n; |
} else { |
} else |
return (err ? var_Error : varNoError); |
return err ? var_Error : varNoError; |
} |
|
} else { |
} else { |
/* |
/* Still need to get to the end of the variable specification, |
* Still need to get to the end of the variable specification, |
* so kludge up a Var structure for the modifications */ |
* so kludge up a Var structure for the modifications |
|
*/ |
|
v = new_var(str+1, NULL); /* junk has name, for error reports */ |
v = new_var(str+1, NULL); /* junk has name, for error reports */ |
v->flags = VAR_JUNK; |
v->flags = VAR_JUNK; |
} |
} |
} |
} |
} |
} |
|
|
if (v->flags & VAR_IN_USE) { |
if (v->flags & VAR_IN_USE) |
Fatal("Variable %s is recursive.", v->name); |
Fatal("Variable %s is recursive.", v->name); |
/*NOTREACHED*/ |
/*NOTREACHED*/ |
} else { |
else |
v->flags |= VAR_IN_USE; |
v->flags |= VAR_IN_USE; |
} |
/* Before doing any modification, we have to make sure the value |
/* |
|
* Before doing any modification, we have to make sure the value |
|
* has been fully expanded. If it looks like recursion might be |
* has been fully expanded. If it looks like recursion might be |
* necessary (there's a dollar sign somewhere in the variable's value) |
* necessary (there's a dollar sign somewhere in the variable's value) |
* we just call Var_Subst to do any other substitutions that are |
* we just call Var_Subst to do any other substitutions that are |
* necessary. Note that the value returned by Var_Subst will have |
* necessary. Note that the value returned by Var_Subst will have |
* been dynamically-allocated, so it will need freeing when we |
* been dynamically-allocated, so it will need freeing when we |
* return. |
* return. */ |
*/ |
|
str = VarValue(v); |
str = VarValue(v); |
if (strchr (str, '$') != (char *)NULL) { |
if (strchr(str, '$') != NULL) { |
str = Var_Subst(str, ctxt, err); |
str = Var_Subst(str, ctxt, err); |
*freePtr = TRUE; |
*freePtr = TRUE; |
} |
} |
|
|
v->flags &= ~VAR_IN_USE; |
v->flags &= ~VAR_IN_USE; |
|
|
|
*lengthPtr = tstr - start + 1; |
|
if (str != NULL && haveModifier) |
|
str = VarModifiers_Apply(str, ctxt, err, freePtr, tstr+1, endc, |
|
lengthPtr); |
|
|
|
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); |
|
*freePtr = FALSE; |
|
Buf_Destroy(&(v->val)); |
|
free(v); |
|
if (dynamic) { |
|
str = emalloc(*lengthPtr + 1); |
|
strncpy(str, start, *lengthPtr); |
|
str[*lengthPtr] = '\0'; |
|
*freePtr = TRUE; |
|
} else { |
|
str = err ? var_Error : varNoError; |
|
} |
|
} |
|
return str; |
|
} |
|
|
|
char * |
|
VarModifiers_Apply(str, ctxt, err, freePtr, start, endc, lengthPtr) |
|
char *str; |
|
SymTable *ctxt; |
|
Boolean err; |
|
Boolean *freePtr; |
|
char *start; |
|
char endc; |
|
size_t *lengthPtr; |
|
{ |
|
char *tstr; |
|
char delim; |
|
char *cp; |
|
|
|
tstr = start; |
|
|
/* |
/* |
* Now we need to apply any modifiers the user wants applied. |
* Now we need to apply any modifiers the user wants applied. |
* These are: |
* These are: |
|
|
* :lhs=rhs Like :S, but the rhs goes to the end of |
* :lhs=rhs Like :S, but the rhs goes to the end of |
* the invocation. |
* the invocation. |
*/ |
*/ |
if ((str != (char *)NULL) && haveModifier) { |
while (*tstr != endc) { |
/* |
char *newStr; /* New value to return */ |
* Skip initial colon while putting it back. |
char termc; /* Character which terminated scan */ |
*/ |
|
*tstr++ = ':'; |
|
while (*tstr != endc) { |
|
char *newStr; /* New value to return */ |
|
char termc; /* Character which terminated scan */ |
|
|
|
if (DEBUG(VAR)) { |
if (DEBUG(VAR)) |
printf("Applying :%c to \"%s\"\n", *tstr, str); |
printf("Applying :%c to \"%s\"\n", *tstr, str); |
} |
switch (*tstr) { |
switch (*tstr) { |
case 'N': |
case 'N': |
case 'M': |
case 'M': |
{ |
{ |
for (cp = tstr + 1; |
for (cp = tstr + 1; |
*cp != '\0' && *cp != ':' && *cp != endc; |
*cp != '\0' && *cp != ':' && *cp != endc; |
cp++) { |
cp++) { |
if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ |
if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ |
cp++; |
cp++; |
|
} |
|
} |
} |
termc = *cp; |
|
*cp = '\0'; |
|
if (*tstr == 'M') |
|
newStr = VarModify(str, VarMatch, tstr+1); |
|
else |
|
newStr = VarModify(str, VarNoMatch, tstr+1); |
|
break; |
|
} |
} |
case 'S': |
termc = *cp; |
{ |
*cp = '\0'; |
VarPattern pattern; |
if (*tstr == 'M') |
|
newStr = VarModify(str, VarMatch, tstr+1); |
|
else |
|
newStr = VarModify(str, VarNoMatch, tstr+1); |
|
break; |
|
} |
|
case 'S': |
|
{ |
|
VarPattern pattern; |
|
|
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. |
if (*tstr == '^') { |
*/ |
pattern.flags |= VAR_MATCH_START; |
if (*tstr == '^') { |
tstr++; |
pattern.flags |= VAR_MATCH_START; |
} |
tstr += 1; |
|
} |
|
|
|
cp = tstr; |
cp = tstr; |
if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim, |
if ((pattern.lhs = VarGetPattern(ctxt, err, &cp, delim, |
&pattern.flags, &pattern.leftLen, NULL)) == NULL) |
&pattern.flags, &pattern.leftLen, NULL)) == NULL) |
goto cleanup; |
goto cleanup; |
|
|
if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim, |
if ((pattern.rhs = VarGetPattern(ctxt, err, &cp, delim, |
NULL, &pattern.rightLen, &pattern)) == NULL) |
NULL, &pattern.rightLen, &pattern)) == NULL) |
goto cleanup; |
goto cleanup; |
|
|
/* |
/* Check for global substitution. If 'g' after the final |
* Check for global substitution. If 'g' after the final |
* delimiter, substitution is global and is marked that |
* delimiter, substitution is global and is marked that |
* way. */ |
* way. |
for (;; cp++) { |
*/ |
switch (*cp) { |
for (;; cp++) { |
case 'g': |
switch (*cp) { |
pattern.flags |= VAR_SUB_GLOBAL; |
case 'g': |
continue; |
pattern.flags |= VAR_SUB_GLOBAL; |
case '1': |
continue; |
pattern.flags |= VAR_SUB_ONE; |
case '1': |
continue; |
pattern.flags |= VAR_SUB_ONE; |
|
continue; |
|
} |
|
break; |
|
} |
} |
|
|
termc = *cp; |
|
newStr = VarModify(str, VarSubstitute, &pattern); |
|
|
|
/* |
|
* Free the two strings. |
|
*/ |
|
free(pattern.lhs); |
|
free(pattern.rhs); |
|
break; |
break; |
} |
} |
#ifndef MAKE_BOOTSTRAP |
|
case 'C': |
|
{ |
|
VarREPattern pattern; |
|
char *re; |
|
int error; |
|
|
|
pattern.flags = 0; |
termc = *cp; |
delim = tstr[1]; |
newStr = VarModify(str, VarSubstitute, &pattern); |
tstr += 2; |
|
|
|
cp = tstr; |
/* Free the two strings. */ |
|
free(pattern.lhs); |
|
free(pattern.rhs); |
|
break; |
|
} |
|
#ifndef MAKE_BOOTSTRAP |
|
case 'C': |
|
{ |
|
VarREPattern pattern; |
|
char *re; |
|
int error; |
|
|
if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, |
pattern.flags = 0; |
NULL, NULL)) == NULL) |
delim = tstr[1]; |
goto cleanup; |
tstr += 2; |
|
|
if ((pattern.replace = VarGetPattern(ctxt, err, &cp, |
cp = tstr; |
delim, NULL, NULL, NULL)) == NULL) { |
|
free(re); |
|
goto cleanup; |
|
} |
|
|
|
for (;; cp++) { |
if ((re = VarGetPattern(ctxt, err, &cp, delim, NULL, |
switch (*cp) { |
NULL, NULL)) == NULL) |
case 'g': |
goto cleanup; |
pattern.flags |= VAR_SUB_GLOBAL; |
|
continue; |
|
case '1': |
|
pattern.flags |= VAR_SUB_ONE; |
|
continue; |
|
} |
|
break; |
|
} |
|
|
|
termc = *cp; |
if ((pattern.replace = VarGetPattern(ctxt, err, &cp, |
|
delim, NULL, NULL, NULL)) == NULL) { |
error = regcomp(&pattern.re, re, REG_EXTENDED); |
|
free(re); |
free(re); |
if (error) { |
goto cleanup; |
*lengthPtr = cp - start + 1; |
} |
VarREError(error, &pattern.re, "RE substitution error"); |
|
free(pattern.replace); |
for (;; cp++) { |
return (var_Error); |
switch (*cp) { |
|
case 'g': |
|
pattern.flags |= VAR_SUB_GLOBAL; |
|
continue; |
|
case '1': |
|
pattern.flags |= VAR_SUB_ONE; |
|
continue; |
} |
} |
|
break; |
|
} |
|
|
pattern.nsub = pattern.re.re_nsub + 1; |
termc = *cp; |
if (pattern.nsub < 1) |
|
pattern.nsub = 1; |
error = regcomp(&pattern.re, re, REG_EXTENDED); |
if (pattern.nsub > 10) |
free(re); |
pattern.nsub = 10; |
if (error) { |
pattern.matches = emalloc(pattern.nsub * |
*lengthPtr = cp - start + 1; |
sizeof(regmatch_t)); |
VarREError(error, &pattern.re, "RE substitution error"); |
newStr = VarModify(str, VarRESubstitute, &pattern); |
|
regfree(&pattern.re); |
|
free(pattern.replace); |
free(pattern.replace); |
free(pattern.matches); |
return var_Error; |
break; |
|
} |
} |
|
|
|
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; |
|
} |
#endif |
#endif |
case 'Q': |
case 'Q': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarQuote (str); |
newStr = VarQuote(str); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'T': |
case 'T': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarTail, NULL); |
newStr = VarModify(str, VarTail, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'H': |
case 'H': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarHead, NULL); |
newStr = VarModify(str, VarHead, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'E': |
case 'E': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarSuffix, NULL); |
newStr = VarModify(str, VarSuffix, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'R': |
case 'R': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarRoot, NULL); |
newStr = VarModify(str, VarRoot, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'U': |
case 'U': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarUppercase, NULL); |
newStr = VarModify(str, VarUppercase, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
case 'L': |
case 'L': |
if (tstr[1] == endc || tstr[1] == ':') { |
if (tstr[1] == endc || tstr[1] == ':') { |
newStr = VarModify(str, VarLowercase, NULL); |
newStr = VarModify(str, VarLowercase, NULL); |
cp = tstr + 1; |
cp = tstr + 1; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
#ifdef SUNSHCMD |
#ifdef SUNSHCMD |
case 's': |
case 's': |
if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { |
if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { |
char *err; |
char *err; |
newStr = Cmd_Exec (str, &err); |
newStr = Cmd_Exec(str, &err); |
if (err) |
if (err) |
Error (err, str); |
Error(err, str); |
cp = tstr + 2; |
cp = tstr + 2; |
termc = *cp; |
termc = *cp; |
break; |
break; |
} |
} |
/*FALLTHRU*/ |
/*FALLTHRU*/ |
#endif |
#endif |
default: |
default: |
{ |
{ |
#ifdef SYSVVARSUB |
#ifdef SYSVVARSUB |
/* |
/* This can either be a bogus modifier or a System-V |
* This can either be a bogus modifier or a System-V |
* substitution command. */ |
* substitution command. |
VarPattern pattern; |
*/ |
Boolean eqFound; |
VarPattern pattern; |
int cnt; /* Used to count brace pairs when |
Boolean eqFound; |
* variable in in parens or braces */ |
|
char startc; |
|
|
pattern.flags = 0; |
if (endc == ')') |
eqFound = FALSE; |
startc = '('; |
/* |
else |
* First we make a pass through the string trying |
startc = '{'; |
* to verify it is a SYSV-make-style translation: |
|
* it must be: <string1>=<string2>) |
pattern.flags = 0; |
*/ |
eqFound = FALSE; |
cp = tstr; |
/* 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 */ |
|
} |
|
else if (*cp == endc) |
|
cnt--; |
|
else if (*cp == startc) |
|
cnt++; |
|
if (cnt) |
|
cp++; |
|
} |
|
if (*cp == endc && eqFound) { |
|
|
|
/* Now we break this sucker into the lhs and |
|
* rhs. We must null terminate them of course. */ |
|
for (cp = tstr; *cp != '='; cp++) |
|
continue; |
|
pattern.lhs = tstr; |
|
pattern.leftLen = cp - tstr; |
|
*cp++ = '\0'; |
|
|
|
pattern.rhs = cp; |
cnt = 1; |
cnt = 1; |
while (*cp != '\0' && cnt) { |
while (cnt) { |
if (*cp == '=') { |
if (*cp == endc) |
eqFound = TRUE; |
|
/* continue looking for endc */ |
|
} |
|
else if (*cp == endc) |
|
cnt--; |
cnt--; |
else if (*cp == startc) |
else if (*cp == startc) |
cnt++; |
cnt++; |
if (cnt) |
if (cnt) |
cp++; |
cp++; |
} |
} |
if (*cp == endc && eqFound) { |
pattern.rightLen = cp - pattern.rhs; |
|
*cp = '\0'; |
|
|
/* |
/* SYSV modifications happen through the whole |
* Now we break this sucker into the lhs and |
* string. Note the pattern is anchored at the end. */ |
* rhs. We must null terminate them of course. |
newStr = VarModify(str, VarSYSVMatch, &pattern); |
*/ |
|
for (cp = tstr; *cp != '='; cp++) |
|
continue; |
|
pattern.lhs = tstr; |
|
pattern.leftLen = cp - tstr; |
|
*cp++ = '\0'; |
|
|
|
pattern.rhs = cp; |
/* Restore the nulled characters */ |
cnt = 1; |
pattern.lhs[pattern.leftLen] = '='; |
while (cnt) { |
pattern.rhs[pattern.rightLen] = endc; |
if (*cp == endc) |
termc = endc; |
cnt--; |
} else |
else if (*cp == startc) |
|
cnt++; |
|
if (cnt) |
|
cp++; |
|
} |
|
pattern.rightLen = cp - pattern.rhs; |
|
*cp = '\0'; |
|
|
|
/* |
|
* SYSV modifications happen through the whole |
|
* string. Note the pattern is anchored at the end. |
|
*/ |
|
newStr = VarModify(str, VarSYSVMatch, &pattern); |
|
|
|
/* |
|
* Restore the nulled characters |
|
*/ |
|
pattern.lhs[pattern.leftLen] = '='; |
|
pattern.rhs[pattern.rightLen] = endc; |
|
termc = endc; |
|
} else |
|
#endif |
#endif |
{ |
{ |
Error ("Unknown modifier '%c'\n", *tstr); |
Error ("Unknown modifier '%c'\n", *tstr); |
for (cp = tstr+1; |
for (cp = tstr+1; |
*cp != ':' && *cp != endc && *cp != '\0'; |
*cp != ':' && *cp != endc && *cp != '\0';) |
cp++) |
cp++; |
continue; |
termc = *cp; |
termc = *cp; |
newStr = var_Error; |
newStr = var_Error; |
|
} |
|
} |
} |
} |
} |
if (DEBUG(VAR)) { |
|
printf("Result is \"%s\"\n", newStr); |
|
} |
|
|
|
if (*freePtr) { |
|
free (str); |
|
} |
|
str = newStr; |
|
if (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; |
|
} |
} |
*lengthPtr = tstr - start + 1; |
if (DEBUG(VAR)) |
} else { |
printf("Result is \"%s\"\n", newStr); |
*lengthPtr = tstr - start + 1; |
|
*tstr = endc; |
|
} |
|
|
|
if (v->flags & VAR_JUNK) { |
if (*freePtr) |
/* |
|
* 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(str); |
} |
str = newStr; |
*freePtr = FALSE; |
if (str != var_Error) |
Buf_Destroy(&(v->val)); |
|
free(v); |
|
if (dynamic) { |
|
str = emalloc(*lengthPtr + 1); |
|
strncpy(str, start, *lengthPtr); |
|
str[*lengthPtr] = '\0'; |
|
*freePtr = TRUE; |
*freePtr = TRUE; |
} else { |
else |
str = err ? var_Error : varNoError; |
*freePtr = FALSE; |
} |
if (termc == '\0') |
|
Error("Unclosed variable specification"); |
|
else if (termc == ':') |
|
*cp++ = termc; |
|
else |
|
*cp = termc; |
|
tstr = cp; |
} |
} |
return (str); |
*lengthPtr += tstr - start+1; |
|
return str; |
|
|
cleanup: |
cleanup: |
*lengthPtr = cp - start + 1; |
*lengthPtr += cp - start +1; |
if (*freePtr) |
if (*freePtr) |
free(str); |
free(str); |
Error("Unclosed substitution for %s (%c missing)", |
Error("Unclosed substitution for (%c missing)", delim); |
v->name, delim); |
return var_Error; |
return (var_Error); |
|
} |
} |
|
|
/*- |
/*- |