From nsmart@www.in-design.com  Tue Jun  2 04:16:54 1998
Received: from www.in-design.com (www.in-design.com [206.210.93.16])
          by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id EAA05078
          for <FreeBSD-gnats-submit@freebsd.org>; Tue, 2 Jun 1998 04:16:51 -0700 (PDT)
          (envelope-from nsmart@www.in-design.com)
Received: (from nsmart@localhost)
	by www.in-design.com (8.8.7/8.8.5) id HAA05028;
	Tue, 2 Jun 1998 07:16:47 -0400 (EDT)
Message-Id: <199806021116.HAA05028@www.in-design.com>
Date: Tue, 2 Jun 1998 07:16:47 -0400 (EDT)
From: njs3@doc.ic.ac.uk
Reply-To: njs3@doc.ic.ac.uk
To: FreeBSD-gnats-submit@freebsd.org
Subject: [PATCH] make(1) does not handle embedded variable expansion
X-Send-Pr-Version: 3.2

>Number:         6828
>Category:       bin
>Synopsis:       [PATCH] make(1) does not handle embedded variable expansion
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Jun  2 04:20:01 PDT 1998
>Closed-Date:    Tue Jun 2 06:15:59 PDT 1998
>Last-Modified:  Tue Nov 27 19:25:37 PST 2001
>Originator:     nsmart
>Release:        FreeBSD 2.2.5-RELEASE i386
>Organization:
Hierarchial
>Environment:

>Description:

make(1) does not handle embedded variable expansion, e.g:

FOO	= bar
BAR	= FOO

all:
	echo ${${BAR}}

>How-To-Repeat:

>Fix:
	
Here is a patch based on one used by NetBSD.  I'd let it stew in -current
for a couple of days at least.  The NetBSD folks have lots of other nice
stuff in their make if anyone fancies merging it.  Don't import the change
they made to the behaviour of ${.PREFIX} though (contact me for details).

*** var.c	Mon Sep 29 00:00:00 1997
--- var.c	Sun May 31 18:49:48 1998
***************
*** 1114,1119 ****
--- 1117,1124 ----
  				 * expanding it in a non-local context. This
  				 * is done to support dynamic sources. The
  				 * result is just the invocation, unaltered */
+     int		vlen;		/* length of variable name, after embedded variable
+ 				 * expansion */
  
      *freePtr = FALSE;
      dynamic = FALSE;
***************
*** 1165,1187 ****
  	    endc = str[1];
  	}
      } else {
  	startc = str[1];
  	endc = startc == '(' ? ')' : '}';
  
  	/*
! 	 * Skip to the end character or a colon, whichever comes first.
  	 */
! 	for (tstr = str + 2;
! 	     *tstr != '\0' && *tstr != endc && *tstr != ':';
! 	     tstr++)
! 	{
! 	    continue;
! 	}
! 	if (*tstr == ':') {
! 	    haveModifier = TRUE;
! 	} 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
--- 1170,1203 ----
  	    endc = str[1];
  	}
      } else {
+ 	/* build up expanded variable name in this buffer */
+ 	Buffer	buf = Buf_Init(MAKE_BSIZE);
+ 
  	startc = str[1];
  	endc = startc == '(' ? ')' : '}';
  
  	/*
! 	 * Skip to the end character or a colon, whichever comes first,
! 	 * replacing embedded variables as we go.
  	 */
! 	for (tstr = str + 2; *tstr != '\0' && *tstr != endc && *tstr != ':'; tstr++)
! 		if (*tstr == '$') {
! 			int	rlen;
! 			Boolean	rfree;
! 			char*	rval = Var_Parse(tstr, ctxt, err, &rlen, &rfree);
!                 
! 			if (rval == var_Error) {
! 				Fatal("Error expanding embedded variable.");
! 			} else if (rval != NULL) {
! 				Buf_AddBytes(buf, strlen(rval), (Byte *) rval);
! 				if (rfree)
! 					free(rval);
! 			}
! 			tstr += rlen - 1;
! 		} else
! 			Buf_AddByte(buf, (Byte) *tstr);
! 	
! 	if (*tstr == '\0') {
  	    /*
  	     * If we never did find the end character, return NULL
  	     * right now, setting the length to be the distance to
***************
*** 1190,1206 ****
  	    *lengthPtr = tstr - str;
  	    return (var_Error);
  	}
  	*tstr = '\0';
  
! 	v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
  	if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
! 	    ((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 '*':
--- 1206,1228 ----
  	    *lengthPtr = tstr - str;
  	    return (var_Error);
  	}
+ 	
+ 	haveModifier = (*tstr == ':');
  	*tstr = '\0';
  
! 	Buf_AddByte(buf, (Byte) '\0');
! 	str = Buf_GetAll(buf, NULL);
! 	vlen = strlen(str);
! 
! 	v = VarFind (str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD);
  	if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
! 	    (vlen == 2) && (str[1] == 'F' || str[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.
  	     */
! 	    switch(str[0]) {
  		case '@':
  		case '%':
  		case '*':
***************
*** 1214,1220 ****
  		    /*
  		     * Well, it's local -- go look for it.
  		     */
! 		    vname[0] = str[2];
  		    vname[1] = '\0';
  		    v = VarFind(vname, ctxt, 0);
  
--- 1236,1242 ----
  		    /*
  		     * Well, it's local -- go look for it.
  		     */
! 		    vname[0] = str[0];
  		    vname[1] = '\0';
  		    v = VarFind(vname, ctxt, 0);
  
***************
*** 1222,1232 ****
  			/*
  			 * 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 = (char *)Buf_GetAll(v->val, (int *)NULL);
  
! 			if (str[3] == 'D') {
  			    val = VarModify(val, VarHead, (ClientData)0);
  			} else {
  			    val = VarModify(val, VarTail, (ClientData)0);
--- 1244,1254 ----
  			/*
  			 * No need for nested expansion or anything, as we're
  			 * the only one who sets these things and we sure don't
! 			 * put nested invocations in them...
  			 */
  			val = (char *)Buf_GetAll(v->val, (int *)NULL);
  
! 			if (str[1] == 'D') {
  			    val = VarModify(val, VarHead, (ClientData)0);
  			} else {
  			    val = VarModify(val, VarTail, (ClientData)0);
***************
*** 1238,1243 ****
--- 1260,1266 ----
  			*freePtr = TRUE;
  			*lengthPtr = tstr-start+1;
  			*tstr = endc;
+ 			Buf_Destroy(buf, TRUE);
  			return(val);
  		    }
  		    break;
***************
*** 1246,1254 ****
  	}
  
  	if (v == (Var *)NIL) {
! 	    if ((((tstr-str) == 3) ||
! 		 ((((tstr-str) == 4) && (str[3] == 'F' ||
! 					 str[3] == 'D')))) &&
  		((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  	    {
  		/*
--- 1269,1277 ----
  	}
  
  	if (v == (Var *)NIL) {
! 	    if (((vlen == 1) ||
! 		 (((vlen == 2) && (str[1] == 'F' ||
! 					 str[1] == 'D')))) &&
  		((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  	    {
  		/*
***************
*** 1260,1266 ****
  		 * specially as they are the only four that will be set
  		 * when dynamic sources are expanded.
  		 */
! 		switch (str[2]) {
  		    case '@':
  		    case '%':
  		    case '*':
--- 1283,1289 ----
  		 * specially as they are the only four that will be set
  		 * when dynamic sources are expanded.
  		 */
! 		switch (str[0]) {
  		    case '@':
  		    case '%':
  		    case '*':
***************
*** 1268,1284 ****
  			dynamic = TRUE;
  			break;
  		}
! 	    } else if (((tstr-str) > 4) && (str[2] == '.') &&
! 		       isupper((unsigned char) str[3]) &&
  		       ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  	    {
  		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;
  		}
--- 1291,1307 ----
  			dynamic = TRUE;
  			break;
  		}
! 	    } else if ((vlen > 2) && (str[0] == '.') &&
! 		       isupper((unsigned char) str[1]) &&
  		       ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)))
  	    {
  		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))
  		{
  		    dynamic = TRUE;
  		}
***************
*** 1296,1303 ****
--- 1319,1328 ----
  		    strncpy(str, start, *lengthPtr);
  		    str[*lengthPtr] = '\0';
  		    *freePtr = TRUE;
+ 		    Buf_Destroy(buf, TRUE);
  		    return(str);
  		} else {
+ 		    Buf_Destroy(buf, TRUE);
  		    return (err ? var_Error : varNoError);
  		}
  	    } else {
***************
*** 1311,1316 ****
--- 1336,1342 ----
  		v->flags = VAR_JUNK;
  	    }
  	}
+ 	Buf_Destroy(buf, TRUE);
      }
  
      if (v->flags & VAR_IN_USE) {
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->closed 
State-Changed-By: thepish 
State-Changed-When: Tue Jun 2 06:15:59 PDT 1998 
State-Changed-Why:  
Patch applied in current (originator suggests waiting before merge to releng) 
>Unformatted:
