version 1.43, 2000/07/17 23:09:06 |
version 1.44, 2000/07/17 23:26:50 |
|
|
* 2...?). |
* 2...?). |
* A Boolean in *freePtr telling whether the returned string should |
* A Boolean in *freePtr telling whether the returned string should |
* be freed by the caller. |
* be freed by the caller. |
* |
|
* Side Effects: |
|
* None. |
|
* |
|
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
char * |
char * |
Var_Parse(str, ctxt, err, lengthPtr, freePtr) |
Var_Parse(str, ctxt, err, lengthPtr, freePtr) |
char *str; /* The string to parse */ |
char *str; /* The string to parse */ |
SymTable *ctxt; /* The context for the variable */ |
SymTable *ctxt; /* The context for the variable */ |
Boolean err; /* TRUE if undefined variables are an error */ |
Boolean err; /* TRUE if undefined variables are an error */ |
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 */ |
{ |
{ |
char *tstr; /* Pointer into str */ |
char *tstr; /* Pointer into str */ |
Var *v; /* Variable in invocation */ |
Var *v; /* Variable in invocation */ |
Boolean haveModifier;/* TRUE if have modifiers for the variable */ |
char endc; /* Ending character when variable in parens |
char endc; /* Ending character when variable in parens |
|
* or braces */ |
* or braces */ |
char *start; |
char *start; |
Boolean dynamic; /* TRUE if the variable is local and we're |
char *val; /* Variable value */ |
* expanding it in a non-local context. This |
u_int32_t k; |
* is done to support dynamic sources. The |
int idx; |
* result is just the invocation, unaltered */ |
|
|
|
*freePtr = FALSE; |
*freePtr = FALSE; |
dynamic = FALSE; |
start = str++; |
start = str; |
|
|
|
if (str[1] != '(' && str[1] != '{') { |
val = NULL; |
/* |
|
* 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. |
|
*/ |
|
v = VarFind_interval(str+1, str+2, ctxt, FIND_ENV | FIND_MINE); |
|
if (v == NULL) { |
|
*lengthPtr = 2; |
|
|
|
if (ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL) { |
if (*str != '(' && *str != '{') { |
/* |
tstr = str + 1; |
* If substituting a local variable in a non-local context, |
*lengthPtr = 2; |
* assume it's for dynamic source stuff. We have to handle |
endc = '\0'; |
* 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. |
|
*/ |
|
switch (str[1]) { |
|
case '@': |
|
return "$(.TARGET)"; |
|
case '%': |
|
return "$(.ARCHIVE)"; |
|
case '*': |
|
return "$(.PREFIX)"; |
|
case '!': |
|
return "$(.MEMBER)"; |
|
} |
|
} |
|
/* Error. */ |
|
return err ? var_Error : varNoError; |
|
} else { |
|
haveModifier = FALSE; |
|
tstr = &str[1]; |
|
endc = str[1]; |
|
} |
|
} else { |
} else { |
endc = str[1] == '(' ? ')' : '}'; |
endc = *str == '(' ? ')' : '}'; |
|
str++; |
|
|
/* Skip to the end character or a colon, whichever comes first. */ |
/* Find eventual modifiers in the variable */ |
for (tstr = str + 2; *tstr != '\0' && *tstr != endc && *tstr != ':';) |
for (tstr = str; *tstr != ':'; tstr++) |
tstr++; |
if (*tstr == '\0' || *tstr == endc) { |
if (*tstr == ':') |
endc = '\0'; |
haveModifier = TRUE; |
break; |
else if (*tstr != '\0') |
|
haveModifier = FALSE; |
|
else { |
|
/* |
|
* If we never did find the end character, return NULL |
|
* right now, setting the length to be the distance to |
|
* the end of the string, since that's what make does. |
|
*/ |
|
*lengthPtr = tstr - str; |
|
return var_Error; |
|
} |
|
|
|
v = VarFind_interval(str + 2, tstr, ctxt, FIND_ENV | FIND_MINE); |
|
if (v == NULL && ctxt != CTXT_CMD && ctxt != CTXT_GLOBAL && |
|
ctxt != NULL && |
|
(tstr-str) == 4 && (str[3] == 'F' || str[3] == '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. |
|
*/ |
|
switch (str[2]) { |
|
case '@': |
|
case '%': |
|
case '*': |
|
case '!': |
|
case '>': |
|
case '<': |
|
{ |
|
char *val; |
|
|
|
/* Well, it's local -- go look for it. */ |
|
v = VarFind_interval(str+2, str+3, ctxt, 0); |
|
|
|
if (v != NULL) { |
|
/* No need for nested expansion or anything, as we're |
|
* the only one who sets these things and we sure don't |
|
* but nested invocations in them... */ |
|
val = VarValue(v); |
|
|
|
if (str[3] == 'D') |
|
val = Var_GetHead(val); |
|
else |
|
val = Var_GetTail(val); |
|
/* Resulting string is dynamically allocated, so |
|
* tell caller to free it. */ |
|
*freePtr = TRUE; |
|
*lengthPtr = tstr-start+1; |
|
*tstr = endc; |
|
return val; |
|
} |
|
break; |
|
} |
|
} |
} |
} |
*lengthPtr = tstr+1 - start; |
|
} |
|
|
if (v == NULL) { |
idx = quick_lookup(str, &tstr, &k); |
if ((tstr-str == 3 || |
v = varfind(str, tstr, ctxt, FIND_ENV | FIND_MINE, idx, k); |
(tstr-str == 4 && (str[3] == 'F' || |
if (v == NULL) { |
str[3] == 'D'))) && |
/* Find out about D and F forms of local variables. */ |
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
if (idx == -1 && tstr == str+2 && (str[1] == 'D' || str[1] == 'F')) { |
{ |
switch (*str) { |
/* If substituting a local variable in a non-local context, |
case '@': |
* assume it's for dynamic source stuff. We have to handle |
idx = TARGET_INDEX; |
* this specially and return the longhand for the variable |
break; |
* with the dollar sign escaped so it makes it back to the |
case '!': |
* caller. Only four of the local variables are treated |
idx = ARCHIVE_INDEX; |
* specially as they are the only four that will be set |
break; |
* when dynamic sources are expanded. */ |
case '*': |
switch (str[2]) { |
idx = PREFIX_INDEX; |
case '@': |
break; |
case '%': |
case '%': |
case '*': |
idx = MEMBER_INDEX; |
case '!': |
break; |
dynamic = TRUE; |
default: |
break; |
break; |
} |
|
} else if (tstr-str > 4 && str[2] == '.' && |
|
isupper((unsigned char) str[3]) && |
|
(ctxt == CTXT_CMD || ctxt == CTXT_GLOBAL || ctxt == NULL)) |
|
{ |
|
int len; |
|
|
|
len = (tstr-str) - 3; |
|
if ((strncmp(str+2, ".TARGET", len) == 0) || |
|
(strncmp(str+2, ".ARCHIVE", len) == 0) || |
|
(strncmp(str+2, ".PREFIX", len) == 0) || |
|
(strncmp(str+2, ".MEMBER", len) == 0)) { |
|
dynamic = TRUE; |
|
} |
|
} |
} |
|
/* This is a DF form, check if we can expand it now. */ |
if (!haveModifier) { |
if (idx != -1 && ctxt != NULL && ctxt != CTXT_GLOBAL) { |
/* No modifiers -- have specification length so we can return |
v = varfind(str, str+1, ctxt, 0, idx, 0); |
* now. */ |
/* No need for nested expansion or anything, as we're |
*lengthPtr = tstr - start + 1; |
* the only one who sets these things and we sure don't |
*tstr = endc; |
* do nested invocations in them... */ |
if (dynamic) { |
if (v != NULL) { |
char *n; |
val = VarValue(v); |
n = emalloc(*lengthPtr + 1); |
if (str[1] == 'D') |
strncpy(n, start, *lengthPtr); |
val = Var_GetHead(val); |
n[*lengthPtr] = '\0'; |
else |
|
val = Var_GetTail(val); |
*freePtr = TRUE; |
*freePtr = TRUE; |
return n; |
} |
} else |
|
return err ? var_Error : varNoError; |
|
} else { |
|
/* Still need to get to the end of the variable specification, |
|
* so kludge up a Var structure for the modifications */ |
|
v = new_var(str+1, NULL); /* junk has name, for error reports */ |
|
v->flags = VAR_JUNK; |
|
} |
} |
} |
} |
} |
} else { |
|
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. */ |
|
val = VarValue(v); |
|
if (strchr(val, '$') != NULL) { |
|
val = Var_Subst(val, ctxt, err); |
|
*freePtr = TRUE; |
|
} |
|
|
if (v->flags & VAR_IN_USE) |
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 = VarValue(v); |
|
if (strchr(str, '$') != NULL) { |
|
str = Var_Subst(str, ctxt, err); |
|
*freePtr = TRUE; |
|
} |
} |
|
if (endc != '\0') |
v->flags &= ~VAR_IN_USE; |
val = VarModifiers_Apply(val, ctxt, err, freePtr, tstr+1, endc, |
|
|
*lengthPtr = tstr - start + 1; |
|
if (str != NULL && haveModifier) |
|
str = VarModifiers_Apply(str, ctxt, err, freePtr, tstr+1, endc, |
|
lengthPtr); |
lengthPtr); |
|
if (val == NULL) { |
if (v->flags & VAR_JUNK) { |
/* Dynamic source that can't be expanded for now: copy the var |
/* Perform any free'ing needed and set *freePtr to FALSE so the caller |
* specification instead. */ |
* doesn't try to free a static pointer. */ |
if (idx != -1 && (ctxt == NULL || ctxt == CTXT_GLOBAL)) { |
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; |
*freePtr = TRUE; |
} else { |
val = interval_dup(start, start+ *lengthPtr); |
str = err ? var_Error : varNoError; |
} else |
} |
val = err ? var_Error : varNoError; |
} |
} |
return str; |
|
|
return val; |
} |
} |
|
|
/*- |
/*- |