version 1.4, 1996/09/02 16:04:22 |
version 1.5, 1996/11/30 21:09:07 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* $NetBSD: var.c,v 1.14 1996/08/13 16:42:25 christos Exp $ */ |
/* $NetBSD: var.c,v 1.15 1996/11/06 17:59:29 christos Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
* Copyright (c) 1988, 1989, 1990, 1993 |
* Copyright (c) 1988, 1989 by Adam de Boor |
* The Regents of the University of California. All rights reserved. |
* Copyright (c) 1989 by Berkeley Softworks |
* Copyright (c) 1989 by Berkeley Softworks |
* All rights reserved. |
* All rights reserved. |
* |
* |
|
|
|
|
#ifndef lint |
#ifndef lint |
#if 0 |
#if 0 |
static char sccsid[] = "@(#)var.c 5.7 (Berkeley) 6/1/90"; |
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; |
#else |
#else |
static char rcsid[] = "$OpenBSD$"; |
static char rcsid[] = "$OpenBSD$"; |
#endif |
#endif |
|
|
#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ |
#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ |
#define VAR_MATCH_START 2 /* Match at start of word */ |
#define VAR_MATCH_START 2 /* Match at start of word */ |
#define VAR_MATCH_END 4 /* Match at end of word */ |
#define VAR_MATCH_END 4 /* Match at end of word */ |
#define VAR_NO_SUB 8 /* Substitution is non-global and already done */ |
|
} VarPattern; |
} VarPattern; |
|
|
static int VarCmp __P((ClientData, ClientData)); |
static int VarCmp __P((ClientData, ClientData)); |
|
|
|
|
if ((env = getenv (name)) != NULL) { |
if ((env = getenv (name)) != NULL) { |
int len; |
int len; |
|
|
v = (Var *) emalloc(sizeof(Var)); |
v = (Var *) emalloc(sizeof(Var)); |
v->name = estrdup(name); |
v->name = estrdup(name); |
|
|
len = strlen(env); |
len = strlen(env); |
|
|
v->val = Buf_Init(len); |
v->val = Buf_Init(len); |
Buf_AddBytes(v->val, len, (Byte *)env); |
Buf_AddBytes(v->val, len, (Byte *)env); |
|
|
v->flags = VAR_FROM_ENV; |
v->flags = VAR_FROM_ENV; |
return (v); |
return (v); |
} else if (checkEnvFirst && (flags & FIND_GLOBAL) && |
} else if (checkEnvFirst && (flags & FIND_GLOBAL) && |
|
|
* VarMatch -- |
* VarMatch -- |
* Place the word in the buffer if it matches the given pattern. |
* Place the word in the buffer if it matches the given pattern. |
* Callback function for VarModify to implement the :M modifier. |
* Callback function for VarModify to implement the :M modifier. |
* |
* |
* Results: |
* Results: |
* TRUE if a space should be placed in the buffer before the next |
* TRUE if a space should be placed in the buffer before the next |
* word. |
* word. |
|
|
* Place the word in the buffer if it matches the given pattern. |
* Place the word in the buffer if it matches the given pattern. |
* Callback function for VarModify to implement the System V % |
* Callback function for VarModify to implement the System V % |
* modifiers. |
* modifiers. |
* |
* |
* Results: |
* Results: |
* TRUE if a space should be placed in the buffer before the next |
* TRUE if a space should be placed in the buffer before the next |
* word. |
* word. |
|
|
* VarNoMatch -- |
* VarNoMatch -- |
* Place the word in the buffer if it doesn't match the given pattern. |
* Place the word in the buffer if it doesn't match the given pattern. |
* Callback function for VarModify to implement the :N modifier. |
* Callback function for VarModify to implement the :N modifier. |
* |
* |
* Results: |
* Results: |
* TRUE if a space should be placed in the buffer before the next |
* TRUE if a space should be placed in the buffer before the next |
* word. |
* word. |
|
|
VarPattern *pattern = (VarPattern *) patternp; |
VarPattern *pattern = (VarPattern *) patternp; |
|
|
wordLen = strlen(word); |
wordLen = strlen(word); |
if ((pattern->flags & VAR_NO_SUB) == 0) { |
if (1) { /* substitute in each word of the variable */ |
/* |
/* |
* Still substituting -- break it down into simple anchored cases |
* Break substitution 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) && |
|
|
* Pattern is unanchored: search for the pattern in the word using |
* Pattern is unanchored: search for the pattern in the word using |
* String_FindSubstring, copying unmatched portions and the |
* String_FindSubstring, copying unmatched portions and the |
* right-hand-side for each match found, handling non-global |
* right-hand-side for each match found, handling non-global |
* subsititutions correctly, etc. When the loop is done, any |
* substitutions correctly, etc. When the loop is done, any |
* remaining part of the word (word and wordLen are adjusted |
* remaining part of the word (word and wordLen are adjusted |
* accordingly through the loop) is copied straight into the |
* accordingly through the loop) is copied straight into the |
* buffer. |
* buffer. |
|
|
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); |
wordLen -= (cp - word) + pattern->leftLen; |
wordLen -= (cp - word) + pattern->leftLen; |
word = cp + pattern->leftLen; |
word = cp + pattern->leftLen; |
if (wordLen == 0) { |
if (wordLen == 0 || (pattern->flags & VAR_SUB_GLOBAL) == 0){ |
done = TRUE; |
done = TRUE; |
} |
} |
if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { |
|
done = TRUE; |
|
pattern->flags |= VAR_NO_SUB; |
|
} |
|
} else { |
} else { |
done = TRUE; |
done = TRUE; |
} |
} |
|
|
return ((Buf_Size(buf) != origSize) || addSpace); |
return ((Buf_Size(buf) != origSize) || addSpace); |
} |
} |
/* |
/* |
* Common code for anchored substitutions: if performed a substitution |
* Common code for anchored substitutions: |
* and it's not supposed to be global, mark the pattern as requiring |
* addSpace was set TRUE if characters were added to the buffer. |
* no more substitutions. addSpace was set TRUE if characters were |
|
* added to the buffer. |
|
*/ |
*/ |
if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { |
|
pattern->flags |= VAR_NO_SUB; |
|
} |
|
return (addSpace); |
return (addSpace); |
} |
} |
nosub: |
nosub: |
|
|
|
|
for (i = 1; i < ac; i++) |
for (i = 1; i < ac; i++) |
addSpace = (*modProc)(av[i], addSpace, buf, datum); |
addSpace = (*modProc)(av[i], addSpace, buf, datum); |
|
|
Buf_AddByte (buf, '\0'); |
Buf_AddByte (buf, '\0'); |
str = (char *)Buf_GetAll (buf, (int *)NULL); |
str = (char *)Buf_GetAll (buf, (int *)NULL); |
Buf_Destroy (buf, FALSE); |
Buf_Destroy (buf, FALSE); |
|
|
* 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 |
* result is just the invocation, unaltered */ |
* result is just the invocation, unaltered */ |
|
|
*freePtr = FALSE; |
*freePtr = FALSE; |
dynamic = FALSE; |
dynamic = FALSE; |
start = str; |
start = str; |
|
|
if (str[1] != '(' && str[1] != '{') { |
if (str[1] != '(' && str[1] != '{') { |
/* |
/* |
* If it's not bounded by braces of some sort, life is much simpler. |
* If it's not bounded by braces of some sort, life is much simpler. |
|
|
v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); |
v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); |
if (v == (Var *)NIL) { |
if (v == (Var *)NIL) { |
*lengthPtr = 2; |
*lengthPtr = 2; |
|
|
if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { |
if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { |
/* |
/* |
* If substituting a local variable in a non-local context, |
* If substituting a local variable in a non-local context, |
|
|
return (var_Error); |
return (var_Error); |
} |
} |
*tstr = '\0'; |
*tstr = '\0'; |
|
|
v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); |
v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); |
if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && |
if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && |
((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) |
((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) |
|
|
vname[0] = str[2]; |
vname[0] = str[2]; |
vname[1] = '\0'; |
vname[1] = '\0'; |
v = VarFind(vname, ctxt, 0); |
v = VarFind(vname, ctxt, 0); |
|
|
if (v != (Var *)NIL) { |
if (v != (Var *)NIL) { |
/* |
/* |
* No need for nested expansion or anything, as we're |
* No need for nested expansion or anything, as we're |
|
|
* but nested invocations in them... |
* but nested invocations in them... |
*/ |
*/ |
val = (char *)Buf_GetAll(v->val, (int *)NULL); |
val = (char *)Buf_GetAll(v->val, (int *)NULL); |
|
|
if (str[3] == 'D') { |
if (str[3] == 'D') { |
val = VarModify(val, VarHead, (ClientData)0); |
val = VarModify(val, VarHead, (ClientData)0); |
} else { |
} else { |
|
|
} |
} |
} |
} |
} |
} |
|
|
if (v == (Var *)NIL) { |
if (v == (Var *)NIL) { |
if ((((tstr-str) == 3) || |
if ((((tstr-str) == 3) || |
((((tstr-str) == 4) && (str[3] == 'F' || |
((((tstr-str) == 4) && (str[3] == 'F' || |
|
|
((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) |
((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) |
{ |
{ |
int len; |
int len; |
|
|
len = (tstr-str) - 3; |
len = (tstr-str) - 3; |
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) || |
|
|
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 |
|
|
str = Var_Subst(NULL, str, ctxt, err); |
str = Var_Subst(NULL, str, ctxt, err); |
*freePtr = TRUE; |
*freePtr = TRUE; |
} |
} |
|
|
v->flags &= ~VAR_IN_USE; |
v->flags &= ~VAR_IN_USE; |
|
|
/* |
/* |
* 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: |
|
|
while (*tstr != endc) { |
while (*tstr != endc) { |
char *newStr; /* New value to return */ |
char *newStr; /* New value to return */ |
char termc; /* Character which terminated scan */ |
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); |
} |
} |
|
|
} |
} |
|
|
buf = Buf_Init(0); |
buf = Buf_Init(0); |
|
|
/* |
/* |
* Pass through the lhs looking for 1) escaped delimiters, |
* Pass through the lhs looking for 1) escaped delimiters, |
* '$'s and backslashes (place the escaped character in |
* '$'s and backslashes (place the escaped character in |
|
|
char *cp2; |
char *cp2; |
int len; |
int len; |
Boolean freeIt; |
Boolean freeIt; |
|
|
cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); |
cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); |
Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); |
Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); |
if (freeIt) { |
if (freeIt) { |
|
|
} |
} |
|
|
Buf_AddByte(buf, (Byte)'\0'); |
Buf_AddByte(buf, (Byte)'\0'); |
|
|
/* |
/* |
* If lhs didn't end with the delimiter, complain and |
* If lhs didn't end with the delimiter, complain and |
* return NULL |
* return NULL |
|
|
* it right here) and 3) expand any variable substitutions. |
* it right here) and 3) expand any variable substitutions. |
*/ |
*/ |
buf = Buf_Init(0); |
buf = Buf_Init(0); |
|
|
tstr = cp + 1; |
tstr = cp + 1; |
for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { |
for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { |
if ((*cp == '\\') && |
if ((*cp == '\\') && |
|
|
} |
} |
|
|
Buf_AddByte(buf, (Byte)'\0'); |
Buf_AddByte(buf, (Byte)'\0'); |
|
|
/* |
/* |
* If didn't end in delimiter character, complain |
* If didn't end in delimiter character, complain |
*/ |
*/ |
|
|
*/ |
*/ |
VarPattern pattern; |
VarPattern pattern; |
Boolean eqFound; |
Boolean eqFound; |
|
|
pattern.flags = 0; |
pattern.flags = 0; |
eqFound = FALSE; |
eqFound = FALSE; |
/* |
/* |
|
|
cp++; |
cp++; |
} |
} |
if (*cp == endc && eqFound) { |
if (*cp == endc && eqFound) { |
|
|
/* |
/* |
* Now we break this sucker into the lhs and |
* Now we break this sucker into the lhs and |
* rhs. We must null terminate them of course. |
* rhs. We must null terminate them of course. |
|
|
pattern.lhs = tstr; |
pattern.lhs = tstr; |
pattern.leftLen = cp - tstr; |
pattern.leftLen = cp - tstr; |
*cp++ = '\0'; |
*cp++ = '\0'; |
|
|
pattern.rhs = cp; |
pattern.rhs = cp; |
cnt = 1; |
cnt = 1; |
while (cnt) { |
while (cnt) { |
|
|
} |
} |
pattern.rightLen = cp - pattern.rhs; |
pattern.rightLen = cp - pattern.rhs; |
*cp = '\0'; |
*cp = '\0'; |
|
|
/* |
/* |
* SYSV modifications happen through the whole |
* SYSV modifications happen through the whole |
* string. Note the pattern is anchored at the end. |
* string. Note the pattern is anchored at the end. |
|
|
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; |
continue; |
termc = *cp; |
termc = *cp; |
newStr = var_Error; |
newStr = var_Error; |
|
|
if (DEBUG(VAR)) { |
if (DEBUG(VAR)) { |
printf("Result is \"%s\"\n", newStr); |
printf("Result is \"%s\"\n", newStr); |
} |
} |
|
|
if (*freePtr) { |
if (*freePtr) { |
free (str); |
free (str); |
} |
} |
|
|
*lengthPtr = tstr - start + 1; |
*lengthPtr = tstr - start + 1; |
*tstr = endc; |
*tstr = endc; |
} |
} |
|
|
if (v->flags & VAR_FROM_ENV) { |
if (v->flags & VAR_FROM_ENV) { |
Boolean destroy = FALSE; |
Boolean destroy = FALSE; |
|
|
if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { |
if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { |
destroy = TRUE; |
destroy = TRUE; |
} else { |
} else { |
|
|
/* |
/* |
* Scan up to the end of the variable name. |
* Scan up to the end of the variable name. |
*/ |
*/ |
for (p = &str[2]; *p && |
for (p = &str[2]; *p && |
*p != ':' && *p != ')' && *p != '}'; p++) |
*p != ':' && *p != ')' && *p != '}'; p++) |
if (*p == '$') |
if (*p == '$') |
break; |
break; |
/* |
/* |
* A variable inside the variable. We cannot expand |
* A variable inside the variable. We cannot expand |
|
|
str = p; |
str = p; |
continue; |
continue; |
} |
} |
|
|
if (strncmp(var, str + 2, p - str - 2) != 0 || |
if (strncmp(var, str + 2, p - str - 2) != 0 || |
var[p - str - 2] != '\0') { |
var[p - str - 2] != '\0') { |
/* |
/* |
* Not the variable we want to expand, scan |
* Not the variable we want to expand, scan |
* until the next variable |
* until the next variable |
*/ |
*/ |
for (;*p != '$' && *p != '\0'; p++) |
for (;*p != '$' && *p != '\0'; p++) |
continue; |
continue; |
Buf_AddBytes(buf, p - str, (Byte *) str); |
Buf_AddBytes(buf, p - str, (Byte *) str); |
str = p; |
str = p; |
|
|
if (!expand) |
if (!expand) |
continue; |
continue; |
} |
} |
|
|
val = Var_Parse (str, ctxt, undefErr, &length, &doFree); |
val = Var_Parse (str, ctxt, undefErr, &length, &doFree); |
|
|
/* |
/* |
|
|
* advance the string pointer. |
* advance the string pointer. |
*/ |
*/ |
str += length; |
str += length; |
|
|
/* |
/* |
* Copy all the characters from the variable value straight |
* Copy all the characters from the variable value straight |
* into the new string. |
* into the new string. |
|
|
} |
} |
} |
} |
} |
} |
|
|
Buf_AddByte (buf, '\0'); |
Buf_AddByte (buf, '\0'); |
str = (char *)Buf_GetAll (buf, (int *)NULL); |
str = (char *)Buf_GetAll (buf, (int *)NULL); |
Buf_Destroy (buf, FALSE); |
Buf_Destroy (buf, FALSE); |
|
|
* None |
* None |
* |
* |
* Side Effects: |
* Side Effects: |
* The VAR_CMD and VAR_GLOBAL contexts are created |
* The VAR_CMD and VAR_GLOBAL contexts are created |
*----------------------------------------------------------------------- |
*----------------------------------------------------------------------- |
*/ |
*/ |
void |
void |
|
|
{ |
{ |
Lst_Destroy(allVars, VarDelete); |
Lst_Destroy(allVars, VarDelete); |
} |
} |
|
|
|
|
/****************** PRINT DEBUGGING INFO *****************/ |
/****************** PRINT DEBUGGING INFO *****************/ |
static int |
static int |