Module Name:    src
Committed By:   kre
Date:           Sat Jun 17 07:22:12 UTC 2017

Modified Files:
        src/bin/sh: exec.c expand.c memalloc.c memalloc.h parser.c

Log Message:
Many internal memory management type fixes.

PR bin/52302   (core dump with interactive shell, here doc and error
on same line) is fixed.   (An old bug.)

echo "$( echo x; for a in $( seq 1000 ); do printf '%s\n'; done; echo y )"
consistently prints 1002 lines (x, 1000 empty ones, then y) as it should
(And you don't want to know what it did before, or why.) (Another old one.)

(Recently added) Problems with ~ expansion fixed (mem management related).

Proper fix for the cwrappers configure problem (which includes the quick
fix that was done earlier, but extends upon that to be correct). (This was
another newly added problem.)

And the really devious (and rare) old bug - if STACKSTRNUL() needs to
allocate a new buffer in which to store the \0, calculate the size of
the string space remaining correctly, unlike when SPUTC() grows the
buffer, there is no actual data being stored in the STACKSTRNUL()
case - the string space remaining was calculated as one byte too few.
That would be harmless, unless the next buffer also filled, in which
case it was assumed that it was really full, not one byte less, meaning
one junk char (a nul, or anything) was being copied into the next (even
bigger buffer) corrupting the data.

Consistent use of stalloc() to allocate a new block of (stack) memory,
and grabstackstr() to claim a block of (stack) memory that had already
been occupied but not claimed as in use.  Since grabstackstr is implemented
as just a call to stalloc() this is a no-op change in practice, but makes
it much easier to comprehend what is really happening.  Previous code
sometimes used stalloc() when the use case was really for grabstackstr().
Change grabstackstr() to actually use the arg passed to it, instead of
(not much better than) guessing how much space to claim,

More care when using unstalloc()/ungrabstackstr() to return space, and in
particular when the stack must be returned to its previous state, rather than
just returning no-longer needed space, neither of those work.  They also don't
work properly if there have been (really, even might have been) any stack mem
allocations since the last stalloc()/grabstackstr().   (If we know there
cannot have been then the alloc/release sequence is kind of pointless.)
To work correctly in general we must use setstackmark()/popstackmark() so
do that when needed.  Have those also save/restore the top of stack string
space remaining.

        [Aside: for those reading this, the "stack" mentioned is not
        in any way related to the thing used for maintaining the C
        function call state, ie: the "stack segment" of the program,
        but the shell's internal memory management strategy.]

More comments to better explain what is happening in some cases.
Also cleaned up some hopelessly broken DEBUG mode data that were
recently added (no effect on anyone but the poor semi-human attempting
to make sense of it...).

User visible changes:

Proper counting of line numbers when a here document is delimited
by a multi-line end-delimiter, as in

        cat << 'REALLY
        END'
        here doc line 1
        here doc line 2
        REALLY
        END

(which is an obscure case, but nothing says should not work.)  The \n
in the end-delimiter of the here doc (the last one) was not incrementing
the line number, which from that point on in the script would be 1 too
low (or more, for end-delimiters with more than one \n in them.)

With tilde expansion:
        unset HOME; echo ~
changed to return getpwuid(getuid())->pw_home instead of failing (returning ~)

POSIX says this is unspecified, which makes it difficult for a script to
compensate for being run without HOME set (as in env -i sh script), so
while not able to be used portably, this seems like a useful extension
(and is implemented the same way by some other shells).

Further, with
        HOME=; printf %s ~
we now write nothing (which is required by POSIX - which requires ~ to
expand to the value of $HOME if it is set) previously if $HOME (in this
case) or a user's directory in the passwd file (for ~user) were a null
STRING, We failed the ~ expansion and left behind '~' or '~user'.


To generate a diff of this commit:
cvs rdiff -u -r1.49 -r1.50 src/bin/sh/exec.c
cvs rdiff -u -r1.115 -r1.116 src/bin/sh/expand.c
cvs rdiff -u -r1.29 -r1.30 src/bin/sh/memalloc.c
cvs rdiff -u -r1.16 -r1.17 src/bin/sh/memalloc.h
cvs rdiff -u -r1.137 -r1.138 src/bin/sh/parser.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/sh/exec.c
diff -u src/bin/sh/exec.c:1.49 src/bin/sh/exec.c:1.50
--- src/bin/sh/exec.c:1.49	Wed Jun  7 05:08:32 2017
+++ src/bin/sh/exec.c	Sat Jun 17 07:22:12 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $	*/
+/*	$NetBSD: exec.c,v 1.50 2017/06/17 07:22:12 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)exec.c	8.4 (Berkeley) 6/8/95";
 #else
-__RCSID("$NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $");
+__RCSID("$NetBSD: exec.c,v 1.50 2017/06/17 07:22:12 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -334,11 +334,10 @@ padvance(const char **path, const char *
 		*path = p + 1;
 	else
 		*path = NULL;
-	return stalloc(len);
+	return grabstackstr(q + strlen(name) + 1);
 }
 
 
-
 /*** Command hashing code ***/
 
 
@@ -617,14 +616,17 @@ loop:
 		if (!S_ISREG(statb.st_mode))
 			goto loop;
 		if (pathopt) {		/* this is a %func directory */
+			char *endname;
+
 			if (act & DO_NOFUNC)
 				goto loop;
-			stalloc(strlen(fullname) + 1);
+			endname = fullname + strlen(fullname) + 1;
+			grabstackstr(endname);
 			readcmdfile(fullname);
 			if ((cmdp = cmdlookup(name, 0)) == NULL ||
 			    cmdp->cmdtype != CMDFUNCTION)
 				error("%s not defined in %s", name, fullname);
-			stunalloc(fullname);
+			ungrabstackstr(fullname, endname);
 			goto success;
 		}
 #ifdef notdef
@@ -644,7 +646,11 @@ loop:
 		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
 		INTOFF;
 		if (act & DO_ALTPATH) {
+		/*
+		 * this should be a grabstackstr() but is not needed:
+		 * fullname is no longer needed for anything
 			stalloc(strlen(fullname) + 1);
+		 */
 			cmdp = &loc_cmd;
 		} else
 			cmdp = cmdlookup(name, 1);

Index: src/bin/sh/expand.c
diff -u src/bin/sh/expand.c:1.115 src/bin/sh/expand.c:1.116
--- src/bin/sh/expand.c:1.115	Wed Jun  7 09:31:30 2017
+++ src/bin/sh/expand.c	Sat Jun 17 07:22:12 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: expand.c,v 1.115 2017/06/07 09:31:30 kre Exp $	*/
+/*	$NetBSD: expand.c,v 1.116 2017/06/17 07:22:12 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)expand.c	8.5 (Berkeley) 5/15/95";
 #else
-__RCSID("$NetBSD: expand.c,v 1.115 2017/06/07 09:31:30 kre Exp $");
+__RCSID("$NetBSD: expand.c,v 1.116 2017/06/17 07:22:12 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -225,9 +225,6 @@ argstr(const char *p, int flag)
 	int firsteq = 1;
 	const char *ifs = NULL;
 	int ifs_split = EXP_IFS_SPLIT;
-#ifdef DEBUG
-	const char *ed = expdest;
-#endif
 
 	if (flag & EXP_IFS_SPLIT)
 		ifs = ifsval();
@@ -239,16 +236,16 @@ argstr(const char *p, int flag)
 	for (;;) {
 		switch (c = *p++) {
 		case '\0':
+			STACKSTRNUL(expdest);
 			VTRACE(DBG_EXPAND, ("argstr returning at \"\" "
-			   "added \"%.*s\" to expdest (followed by: %2.2x)\n", 
-			   expdest-ed, ed, *expdest));
+			   "added \"%s\" to expdest\n", stackblock()));
 			return p - 1;
 		case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
 		case CTLENDARI: /* end of a $(( )) string */
+			STACKSTRNUL(expdest);
 			VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..."
-			    " after %2.2x; "
-			   "added \"%.*s\" to expdest (followed by: %2.2x)\n", 
-			    p, p[-1]&0xff, expdest-ed, ed, *expdest));
+			    " after %2.2X; added \"%s\" to expdest\n", 
+			    p, (c&0xff), stackblock()));
 			return p;
 		case CTLQUOTEMARK:
 			/* "$@" syntax adherence hack */
@@ -272,27 +269,43 @@ argstr(const char *p, int flag)
 				STPUTC(c, expdest);
 			c = *p++;
 			STPUTC(c, expdest);
+			if (c == '\n')		/* should not happen, but ... */
+				line_number++;
 			break;
-		case CTLVAR:
+		case CTLVAR: {
+#ifdef DEBUG
+			unsigned int pos = expdest - stackblock();
+#endif
 			p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
+			STACKSTRNUL(expdest);
 			VTRACE(DBG_EXPAND, ("argstr evalvar "
-			   "added \"%.*s\" to expdest (followed by: %2.2x)\n", 
-			   expdest-ed, ed, *expdest));
+			   "added \"%s\" to expdest\n", 
+			   stackblock() + pos));
 			break;
+		}
 		case CTLBACKQ:
-		case CTLBACKQ|CTLQUOTE:
+		case CTLBACKQ|CTLQUOTE: {
+#ifdef DEBUG
+			unsigned int pos = expdest - stackblock();
+#endif
 			expbackq(argbackq->n, c & CTLQUOTE, flag);
 			argbackq = argbackq->next;
-			VTRACE(DBG_EXPAND, ("argstr expbackq "
-			   "added \"%.*s\" to expdest (followed by: %2.2x)\n", 
-			   expdest-ed, ed, *expdest));
+			STACKSTRNUL(expdest);
+			VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" "
+			   "to expdest\n", stackblock() + pos));
 			break;
-		case CTLARI:
+		}
+		case CTLARI: {
+#ifdef DEBUG
+			unsigned int pos = expdest - stackblock();
+#endif
 			p = expari(p);
+			STACKSTRNUL(expdest);
 			VTRACE(DBG_EXPAND, ("argstr expari "
-			   "+ \"%.*s\" to dest (fwd by: %2.2x) p=\"%.5s...\"\n",
-			   expdest-ed, ed, *expdest, p));
+			   "+ \"%s\" to expdest p=\"%.5s...\"\n",
+			   stackblock() + pos, p));
 			break;
+		}
 		case ':':
 		case '=':
 			/*
@@ -333,20 +346,29 @@ exptilde(const char *p, int flag)
 	const char *home;
 	int quotes = flag & (EXP_GLOB | EXP_CASE);
 	char *user;
+	struct stackmark smark;
+#ifdef DEBUG
+	unsigned int offs = expdest - stackblock();
+#endif
+
+	setstackmark(&smark);
+	user = stackblock();		/* we will just borrow top of stack */
 
-	user = expdest;		/* we will just borrow top of stack */
 	while ((c = *++p) != '\0') {
 		switch(c) {
-		case CTLESC:
-		case CTLVAR:
-		case CTLBACKQ:
+		case CTLESC:		/* any of these occurring */
+		case CTLVAR:		/* means ~ expansion */
+		case CTLBACKQ:		/* does not happen at all */
 		case CTLBACKQ | CTLQUOTE:
-		case CTLARI:
+		case CTLARI:		/* just leave original unchanged */
 		case CTLENDARI:
 		case CTLQUOTEMARK:
+		case CTLENDVAR:
+		case '\n':
+			popstackmark(&smark);
 			return (startp);
 		case CTLNONL:
-			break;
+			continue;
 		case ':':
 			if (flag & EXP_VARTILDE)
 				goto done;
@@ -358,24 +380,46 @@ exptilde(const char *p, int flag)
 	}
  done:
 	STACKSTRNUL(user);
+	user = stackblock();		/* to start of collected username */
 
-	CTRACE(DBG_EXPAND, ("exptilde, found \"%s\" :", expdest));
-	if (*expdest == '\0')
+	CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user));
+	if (*user == '\0') {
 		home = lookupvar("HOME");
-	else if ((pw = getpwnam(expdest)) == NULL)
+		/*
+		 * if HOME is unset, results are unspecified...
+		 * we used to just leave the ~ unchanged, but
+		 * (some) other shells do ... and this seems more useful.
+		 */
+		if (home == NULL && (pw = getpwuid(getuid())) != NULL)
+			home = pw->pw_dir;
+	} else if ((pw = getpwnam(user)) == NULL) {
+		/*
+		 * If user does not exist, results are undefined.
+		 * so we can abort() here if we want, but let's not!
+		 */
 		home = NULL;
-	else
+	} else
 		home = pw->pw_dir;
-	STUNSTR(user - expdest);	/* return borrowed string space */
 
-	if (home == NULL || *home == '\0')
+	VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>"));
+	popstackmark(&smark);	/* now expdest is valid again */
+
+	/*
+	 * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial
+	 *		working directory from getpwnam() for ~user
+	 * Nothing there about "except if a null string".  So do what it wants.
+	 */
+	if (home == NULL /* || *home == '\0' */) {
+		CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp));
 		return startp;
-	while ((c = *home++) != '\0') {
+	} while ((c = *home++) != '\0') {
 		if (quotes && SQSYNTAX[(int)c] == CCTL)
 			STPUTC(CTLESC, expdest);
 		STPUTC(c, expdest);
 	}
-	CTRACE(DBG_EXPAND, ("returning \"%s\"\n", p));
+	CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n",
+	      expdest - stackblock() - offs, expdest - stackblock() - offs,
+	      stackblock() + offs, p));
 
 	return (p);
 }
@@ -431,8 +475,13 @@ removerecordregions(int endoff)
 
 
 /*
- * Expand arithmetic expression.  Backup to start of expression,
- * evaluate, place result in (backed up) result, adjust string position.
+ * Expand arithmetic expression.
+ *
+ * In this incarnation, we start at the beginning (yes, "Let's start at the
+ * very beginning.  A very good place to start.") and collect the expression
+ * until the end - which means expanding anything contained within.
+ *
+ * Fortunately, argstr() just happens to do that for us...
  */
 STATIC const char *
 expari(const char *p)
@@ -442,24 +491,11 @@ expari(const char *p)
 	int adjustment;
 	int begoff;
 	int quoted;
-#ifdef DEBUG
-	const char *ed = expdest;
-#endif
+	struct stackmark smark;
 
 	/*	ifsfree(); */
 
 	/*
-	 * This routine is slightly over-complicated for
-	 * efficiency.  First we make sure there is
-	 * enough space for the result, which may be bigger
-	 * than the expression if we add exponentation.  Next we
-	 * scan backwards looking for the start of arithmetic.  If the
-	 * next previous character is a CTLESC character, then we
-	 * have to rescan starting from the beginning since CTLESC
-	 * characters have to be processed left to right.
-	 */
-
-	/*
 	 * SPACE_NEEDED is enough for all possible digits (rounded up)
 	 * plus possible "-", and the terminating '\0', hence, plus 2
 	 *
@@ -469,57 +505,47 @@ expari(const char *p)
 	 * so this is safe, if occasionally slightly wasteful.
 	 */
 #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2))
-	/*
-	 * Only need check for SPACE_NEEDED-2 as we have already
-	 * consumed (but will overwrite) 2 chars, the CTLARI, and the
-	 * flags (quoting) byte (if those had not already been seen,
-	 * we would not be here.)
-	 */
+
 	quoted = *p++ == '"';
 	begoff = expdest - stackblock();
-	VTRACE(DBG_EXPAND, ("expari: '%c' \"%s\" begoff %d\n", p[-1],p,begoff));
+	VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n",
+	    quoted ? "(quoted)" : "", p, begoff));
+
 	p = argstr(p, EXP_NL);			/* expand $(( )) string */
 	STPUTC('\0', expdest);
 	start = stackblock() + begoff;
-	VTRACE(DBG_EXPAND, ("expari: argstr added: '%s' start: \"%.8s\"\n",
-	    ed, start));
-	removerecordregions(begoff);
-	rmescapes_nl(start);	/* convert CRTNONL back into \n's */
-	q = grabstackstr(expdest);
+
+	removerecordregions(begoff);		/* nothing there is kept */
+	rmescapes_nl(start);		/* convert CRTNONL back into \n's */
+
+	setstackmark(&smark);
+	q = grabstackstr(expdest);	/* keep the expression while eval'ing */
 	result = arith(start, line_number);
-	VTRACE(DBG_EXPAND, ("expari: after arith: result=%jd '%s' q@'%.3s'\n",
-	    result, ed, q));
-	ungrabstackstr(q, expdest);
+	popstackmark(&smark);		/* return the stack to before grab */
 
-	start = stackblock() + begoff;
+	start = stackblock() + begoff;		/* block may have moved */
 	adjustment = expdest - start;
-	VTRACE(DBG_EXPAND, ("expari: removing %d ", adjustment));
-	STADJUST(-adjustment, expdest);		/* remove the argstr() */
-	VTRACE(DBG_EXPAND, ("ed \"%.*s\", expdest \"%s\"\n",
-	    expdest-ed, ed, expdest));
+	STADJUST(-adjustment, expdest);		/* remove the argstr() result */
 
-	CHECKSTRSPACE(SPACE_NEEDED, expdest);
+	CHECKSTRSPACE(SPACE_NEEDED, expdest);	/* nb: stack block might move */
 	fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result);
-	VTRACE(DBG_EXPAND, ("expari: after fmtstr: ed \"%s\" expdest \"%s\"\n",
-	    ed, expdest));
 
-	for (q = expdest; *q++ != '\0'; )
+	for (q = expdest; *q++ != '\0'; )	/* find end of what we added */
 		;
 
 	if (quoted == 0)			/* allow weird splitting */
 		recordregion(begoff, begoff + q - 1 - expdest, 0);
 	adjustment = q - expdest - 1;
-	STADJUST(adjustment, expdest);
-	VTRACE(DBG_EXPAND, ("expari: adding %d ed \"%.*s\", "
-	    "expdest \"%s\" returning \"%.5s...\"\n", adjustment, expdest - ed,
-	    ed, expdest, p));
+	STADJUST(adjustment, expdest);		/* move expdest to end */
+	VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", produced \"%s\" "
+	    "returning \"%.5s...\"\n", adjustment, stackblock() + begoff, p));
 
 	return p;
 }
 
 
 /*
- * Expand stuff in backwards quotes.
+ * Expand stuff in backwards quotes (these days, any command substitution).
  */
 
 STATIC void
@@ -529,7 +555,7 @@ expbackq(union node *cmd, int quoted, in
 	int i;
 	char buf[128];
 	char *p;
-	char *dest = expdest;
+	char *dest = expdest;	/* expdest may be reused by eval, use an alt */
 	struct ifsregion saveifs, *savelastp;
 	struct nodelist *saveargbackq;
 	char lastc;
@@ -538,25 +564,29 @@ expbackq(union node *cmd, int quoted, in
 	int saveherefd;
 	int quotes = flag & (EXP_GLOB | EXP_CASE);
 	int nnl;
+	struct stackmark smark;
 
-	VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x)\n", quoted, flag));
+	VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n",
+	    quoted, flag, startloc));
 	INTOFF;
 	saveifs = ifsfirst;
 	savelastp = ifslastp;
 	saveargbackq = argbackq;
 	saveherefd = herefd;
 	herefd = -1;
-	p = grabstackstr(dest);
-	evalbackcmd(cmd, &in);
-	ungrabstackstr(p, dest);
+
+	setstackmark(&smark);	/* preserve the stack */
+	p = grabstackstr(dest);	/* save what we have there currently */
+	evalbackcmd(cmd, &in);	/* evaluate the $( ) tree (using stack) */
+	popstackmark(&smark);	/* and return stack to when we entered */
+
 	ifsfirst = saveifs;
 	ifslastp = savelastp;
 	argbackq = saveargbackq;
 	herefd = saveherefd;
 
-	p = in.buf;
-	lastc = '\0';
-	nnl = 0;
+	p = in.buf;		/* now extract the results */
+	nnl = 0;		/* dropping trailing \n's */
 	for (;;) {
 		if (--in.nleft < 0) {
 			if (in.fd < 0)
@@ -571,13 +601,38 @@ expbackq(union node *cmd, int quoted, in
 		}
 		lastc = *p++;
 		if (lastc != '\0') {
-			if (lastc == '\n')
-				nnl++;
+			if (lastc == '\n')	/* don't save \n yet */
+				nnl++;		/* it might be trailing */
 			else {
-				CHECKSTRSPACE(nnl + 2, dest);
-				while (nnl > 0) {
-					nnl--;
-					USTPUTC('\n', dest);
+				/*
+				 * We have something other than \n
+				 *
+				 * Before saving it, we need to insert
+				 * any \n's that we have just skipped.
+				 */
+
+				/* XXX
+				 * this hack is just because our
+				 * CHECKSTRSPACE() is lazy, and only
+				 * ever grows the stack once, even
+				 * if that does not allocate the space
+				 * we requested.  ie: safe for small
+				 * requests, but not large ones.
+				 * FIXME someday...
+				 */
+				if (nnl < 20) {
+					CHECKSTRSPACE(nnl + 2, dest);
+					while (nnl > 0) {
+						nnl--;
+						USTPUTC('\n', dest);
+					}
+				} else {
+					/* The slower, safer, way */
+					while (nnl > 0) {
+						nnl--;
+						STPUTC('\n', dest);
+					}
+					CHECKSTRSPACE(2, dest);
 				}
 				if (quotes && syntax[(int)lastc] == CCTL)
 					USTPUTC(CTLESC, dest);
@@ -598,7 +653,8 @@ expbackq(union node *cmd, int quoted, in
 		(int)((dest - stackblock()) - startloc),
 		(int)((dest - stackblock()) - startloc),
 		stackblock() + startloc));
-	expdest = dest;
+
+	expdest = dest;		/* all done, expdest is all ours again */
 	INTON;
 }
 
@@ -613,6 +669,8 @@ subevalvar(const char *p, const char *st
 	int amount;
 
 	herefd = -1;
+	VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n",
+	    subtype, p, p-str, str, startloc, varflags));
 	argstr(p, EXP_TILDE);
 	STACKSTRNUL(expdest);
 	herefd = saveherefd;
@@ -622,10 +680,10 @@ subevalvar(const char *p, const char *st
 	switch (subtype) {
 	case VSASSIGN:
 		setvar(str, startp, 0);
-		amount = startp - expdest;
+		amount = startp - expdest;	/* remove what argstr added */
 		STADJUST(amount, expdest);
-		varflags &= ~VSNUL;
-		return 1;
+		varflags &= ~VSNUL;	/*XXX Huh?   What's that achieve? */
+		return 1;			/* go back and eval var again */
 
 	case VSQUESTION:
 		if (*p != CTLENDVAR) {
@@ -833,7 +891,7 @@ evalvar(const char *p, int flag)
 		if (special) {
 			if (varflags & VSLINENO) {
 				/*
-				 * The LINENO hack
+				 * The LINENO hack (expansion part)
 				 */
 				while (--special > 0) {
 /*						not needed, it is a number...
@@ -946,15 +1004,12 @@ evalvar(const char *p, int flag)
 			removerecordregions(startloc);
 			goto again;
 		}
-		apply_ifs = 0;
+		apply_ifs = 0;		/* never executed */
 		break;
 
 	default:
 		abort();
 	}
-#if 0
-	p[-1] = '=';	/* recover overwritten '=' */
-#endif
 
 	if (apply_ifs)
 		recordregion(startloc, expdest - stackblock(),
@@ -985,7 +1040,7 @@ evalvar(const char *p, int flag)
 
 
 /*
- * Test whether a specialized variable is set.
+ * Test whether a special parameter is set.
  */
 
 STATIC int
@@ -1007,7 +1062,15 @@ varisset(const char *name, int nulok)
 		}
 	} else if (is_digit(*name)) {
 		char *ap;
-		int num = atoi(name);
+		long num;
+
+		/*
+		 * handle overflow sensibly (the *ap tests should never fail)
+		 */
+		errno = 0;
+		num = strtol(name, &ap, 10);
+		if (errno != 0 || (*ap != '\0' && *ap != '='))
+			return 0;
 
 		if (num == 0)
 			ap = arg0;
@@ -1043,16 +1106,16 @@ varvalue(const char *name, int quoted, i
 
 #define STRTODEST(p) \
 	do {\
-	if (flag & (EXP_GLOB | EXP_CASE) && subtype != VSLENGTH) { \
-		syntax = quoted? DQSYNTAX : BASESYNTAX; \
-		while (*p) { \
-			if (syntax[(int)*p] == CCTL) \
-				STPUTC(CTLESC, expdest); \
-			STPUTC(*p++, expdest); \
-		} \
-	} else \
-		while (*p) \
-			STPUTC(*p++, expdest); \
+		if (flag & (EXP_GLOB | EXP_CASE) && subtype != VSLENGTH) { \
+			syntax = quoted? DQSYNTAX : BASESYNTAX; \
+			while (*p) { \
+				if (syntax[(int)*p] == CCTL) \
+					STPUTC(CTLESC, expdest); \
+				STPUTC(*p++, expdest); \
+			} \
+		} else \
+			while (*p) \
+				STPUTC(*p++, expdest); \
 	} while (0)
 
 

Index: src/bin/sh/memalloc.c
diff -u src/bin/sh/memalloc.c:1.29 src/bin/sh/memalloc.c:1.30
--- src/bin/sh/memalloc.c:1.29	Fri Feb 15 17:26:06 2008
+++ src/bin/sh/memalloc.c	Sat Jun 17 07:22:12 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $	*/
+/*	$NetBSD: memalloc.c,v 1.30 2017/06/17 07:22:12 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)memalloc.c	8.3 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: memalloc.c,v 1.29 2008/02/15 17:26:06 matt Exp $");
+__RCSID("$NetBSD: memalloc.c,v 1.30 2017/06/17 07:22:12 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -167,6 +167,7 @@ setstackmark(struct stackmark *mark)
 	mark->stackp = stackp;
 	mark->stacknxt = stacknxt;
 	mark->stacknleft = stacknleft;
+	mark->sstrnleft = sstrnleft;
 	mark->marknext = markp;
 	markp = mark;
 }
@@ -186,6 +187,7 @@ popstackmark(struct stackmark *mark)
 	}
 	stacknxt = mark->stacknxt;
 	stacknleft = mark->stacknleft;
+	sstrnleft = mark->sstrnleft;
 	INTON;
 }
 
@@ -297,11 +299,26 @@ makestrspace(void)
 	return stackblock() + len;
 }
 
+/*
+ * Note that this only works to release stack space for reuse
+ * if nothing else has allocated space on the stack since the grabstackstr()
+ *
+ * "s" is the start of the area to be released, and "p" represents the end
+ * of the string we have stored beyond there and are now releasing.
+ * (ie: "p" should be the same as in the call to grabstackstr()).
+ *
+ * stunalloc(s) and ungrabstackstr(s, p) are almost interchangable after
+ * a grabstackstr(), however the latter also returns string space so we
+ * can just continue with STPUTC() etc without needing a new STARTSTACKSTR(s)
+ */
 void
 ungrabstackstr(char *s, char *p)
 {
+#ifdef DEBUG
+	if (s < stacknxt || stacknxt + stacknleft < s)
+		abort();
+#endif
 	stacknleft += stacknxt - s;
 	stacknxt = s;
 	sstrnleft = stacknleft - (p - s);
-
 }

Index: src/bin/sh/memalloc.h
diff -u src/bin/sh/memalloc.h:1.16 src/bin/sh/memalloc.h:1.17
--- src/bin/sh/memalloc.h:1.16	Sat Jun  3 10:31:16 2017
+++ src/bin/sh/memalloc.h	Sat Jun 17 07:22:12 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: memalloc.h,v 1.16 2017/06/03 10:31:16 kre Exp $	*/
+/*	$NetBSD: memalloc.h,v 1.17 2017/06/17 07:22:12 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -38,6 +38,7 @@ struct stackmark {
 	struct stack_block *stackp;
 	char *stacknxt;
 	int stacknleft;
+	int sstrnleft;
 	struct stackmark *marknext;
 };
 
@@ -68,11 +69,10 @@ void ungrabstackstr(char *, char *);
 #define STPUTC(c, p)	(--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
 #define CHECKSTRSPACE(n, p)	{ if (sstrnleft < n) p = makestrspace(); }
 #define USTPUTC(c, p)	(--sstrnleft, *p++ = (c))
-#define STACKSTRNUL(p)	(sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STACKSTRNUL(p)	(sstrnleft == 0? (p = growstackstr(), sstrnleft++, *p = '\0') : (*p = '\0'))
 #define STUNPUTC(p)	(++sstrnleft, --p)
 #define STTOPC(p)	p[-1]
 #define STADJUST(amount, p)	(p += (amount), sstrnleft -= (amount))
-#define STUNSTR(amount)	(sstrnleft -= (amount))
-#define grabstackstr(p)	stalloc(stackblocksize() - sstrnleft)
+#define grabstackstr(p)	stalloc((p) - stackblock())
 
 #define ckfree(p)	free((pointer)(p))

Index: src/bin/sh/parser.c
diff -u src/bin/sh/parser.c:1.137 src/bin/sh/parser.c:1.138
--- src/bin/sh/parser.c:1.137	Thu Jun  8 22:10:39 2017
+++ src/bin/sh/parser.c	Sat Jun 17 07:22:12 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.c,v 1.137 2017/06/08 22:10:39 kre Exp $	*/
+/*	$NetBSD: parser.c,v 1.138 2017/06/17 07:22:12 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
 #else
-__RCSID("$NetBSD: parser.c,v 1.137 2017/06/08 22:10:39 kre Exp $");
+__RCSID("$NetBSD: parser.c,v 1.138 2017/06/17 07:22:12 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -825,7 +825,10 @@ checkend(int c, char * const eofmark, co
 		char *q;
 
 		for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++)
-			;
+			if (c2 == '\n') {
+				plinno++;
+				needprompt = doprompt;
+			}
 		if ((c2 == PEOF || c2 == '\n') && *q == '\0') {
 			c = PEOF;
 			if (c2 == '\n') {
@@ -893,13 +896,13 @@ slurp_heredoc(char *const eofmark, const
 			 * not suppress the newline (the \ quotes itself)
 			 */
 			if (c == '\\') {		/* A backslash */
+				STPUTC(c, out);
 				c = pgetc();		/* followed by */
 				if (c == '\n') {	/* a newline?  */
-					/*XXX CTLNONL ?? XXX*/
+					STPUTC(c, out);
 					plinno++;
-					continue;	/*    :drop both */
+					continue;	/* don't break */
 				}
-				STPUTC('\\', out);	/* else keep \ */
 			}
 			STPUTC(c, out);			/* keep the char */
 			if (c == '\n') {		/* at end of line */
@@ -980,6 +983,8 @@ readheredocs(void)
 		 * Now "parse" here docs that have unquoted eofmarkers.
 		 */
 		setinputstring(wordtext, 1, line);
+		VTRACE(DBG_PARSE, ("Reprocessing %d line here doc from %d\n",
+			l, line));
 		readtoken1(pgetc(), DQSYNTAX, 1);
 		n->narg.text = wordtext;
 		n->narg.backquote = backquotelist;
@@ -1341,7 +1346,7 @@ cleanup_state_stack(VSS *stack)
  */
 static char *
 parsebackq(VSS *const stack, char * const in,
-    struct nodelist **const pbqlist, const int oldstyle)
+    struct nodelist **const pbqlist, const int oldstyle, const int magicq)
 {
 	struct nodelist **nlpp;
 	const int savepbq = parsebackquote;
@@ -1353,6 +1358,7 @@ parsebackq(VSS *const stack, char * cons
 	struct jmploc *const savehandler = handler;
 	const int savelen = in - stackblock();
 	int saveprompt;
+	int lno;
 
 	if (setjmp(jmploc.loc)) {
 		if (sstr)
@@ -1381,23 +1387,35 @@ parsebackq(VSS *const stack, char * cons
 		char *pstr;
 		int line1 = plinno;
 
+		VTRACE(DBG_PARSE, ("parsebackq: repackaging `` as $( )"));
 		/*
 		 * Because the entire `...` is read here, we don't
 		 * need to bother the state stack.  That will be used
 		 * (as appropriate) when the processed string is re-read.
 		 */
 		STARTSTACKSTR(out);
+#ifdef DEBUG
+		for (psavelen = 0;;psavelen++) {
+#else
 		for (;;) {
+#endif
 			if (needprompt) {
 				setprompt(2);
 				needprompt = 0;
 			}
-			pc = pgetc_linecont();
+			pc = pgetc();
 			if (pc == '`')
 				break;
 			switch (pc) {
 			case '\\':
-				pc = pgetc();	/* cannot be '\n' */
+				pc = pgetc();
+#ifdef DEBUG
+				psavelen++;
+#endif
+				if (pc == '\n') {   /* keep \ \n for later */
+					plinno++;
+					needprompt = doprompt;
+				}
 				if (pc != '\\' && pc != '`' && pc != '$'
 				    && (!ISDBLQUOTE() || pc != '"'))
 					STPUTC('\\', out);
@@ -1405,14 +1423,11 @@ parsebackq(VSS *const stack, char * cons
 
 			case '\n':
 				plinno++;
-				/*
-				out = insert_elided_nl(out);
-				*/
 				needprompt = doprompt;
 				break;
 
 			case PEOF:
-			        startlinno = plinno;
+			        startlinno = line1;
 				synerror("EOF in backquote substitution");
  				break;
 
@@ -1422,7 +1437,9 @@ parsebackq(VSS *const stack, char * cons
 			STPUTC(pc, out);
 		}
 		STPUTC('\0', out);
+		VTRACE(DBG_PARSE, (" read %d", psavelen));
 		psavelen = out - stackblock();
+		VTRACE(DBG_PARSE, (" produced %d\n", psavelen));
 		if (psavelen > 0) {
 			pstr = grabstackstr(out);
 			setinputstring(pstr, 1, line1);
@@ -1441,7 +1458,9 @@ parsebackq(VSS *const stack, char * cons
 	} else
 		saveprompt = 0;
 
+	lno = -plinno;
 	n = list(0, oldstyle);
+	lno += plinno;
 
 	if (oldstyle)
 		doprompt = saveprompt;
@@ -1475,9 +1494,11 @@ parsebackq(VSS *const stack, char * cons
 	}
 	parsebackquote = savepbq;
 	handler = savehandler;
-	if (arinest || ISDBLQUOTE())
+	if (arinest || ISDBLQUOTE()) {
 		USTPUTC(CTLBACKQ | CTLQUOTE, out);
-	else
+		while (--lno >= 0)
+			USTPUTC(CTLNONL, out);
+	} else
 		USTPUTC(CTLBACKQ, out);
 
 	return out;
@@ -1729,25 +1750,25 @@ readtoken1(int firstc, char const *syn, 
 				USTPUTC(c, out);
 				--parenlevel;
 			} else {
-				if (pgetc_linecont() == ')') {
+				if (pgetc_linecont() == /*(*/ ')') {
 					out = insert_elided_nl(out);
 					if (--arinest == 0) {
 						TS_POP();
 						USTPUTC(CTLENDARI, out);
 					} else
-						USTPUTC(')', out);
+						USTPUTC(/*(*/ ')', out);
 				} else {
 					/*
 					 * unbalanced parens
 					 *  (don't 2nd guess - no error)
 					 */
 					pungetc();
-					USTPUTC(')', out);
+					USTPUTC(/*(*/ ')', out);
 				}
 			}
 			continue;
 		case CBQUOTE:	/* '`' */
-			out = parsebackq(stack, out, &bqlist, 1);
+			out = parsebackq(stack, out, &bqlist, 1, magicq);
 			continue;
 		case CEOF:		/* --> c == PEOF */
 			break;		/* will exit loop */
@@ -1762,7 +1783,7 @@ readtoken1(int firstc, char const *syn, 
 
 	if (syntax == ARISYNTAX) {
 		cleanup_state_stack(stack);
-		synerror("Missing '))'");
+		synerror(/*((*/ "Missing '))'");
 	}
 	if (syntax != BASESYNTAX && /* ! parsebackquote && */ !magicq) {
 		cleanup_state_stack(stack);
@@ -1775,7 +1796,7 @@ readtoken1(int firstc, char const *syn, 
 		synerror("Missing '}'");
 	}
 
-	USTPUTC('\0', out);
+	STPUTC('\0', out);
 	len = out - stackblock();
 	out = stackblock();
 
@@ -1790,6 +1811,11 @@ readtoken1(int firstc, char const *syn, 
 		}
 	}
 
+	VTRACE(DBG_PARSE,
+	    ("readtoken1 %sword \"%s\", completed%s (%d) left %d enl\n",
+	    (quotef ? "quoted " : ""), out, (bqlist ? " with cmdsubs" : ""),
+	    len, elided_nl));
+
 	quoteflag = quotef;
 	backquotelist = bqlist;
 	grabstackblock(len);
@@ -1822,7 +1848,7 @@ parsesub: {
 		} else {
 			out = insert_elided_nl(out);
 			pungetc();
-			out = parsebackq(stack, out, &bqlist, 0);
+			out = parsebackq(stack, out, &bqlist, 0, magicq);
 		}
 	} else {
 		USTPUTC(CTLVAR, out);
@@ -1978,8 +2004,8 @@ parsearith: {
 		 *
 		 *	XXX It isn't, must fix, soonish...
 		 */
-		USTPUTC('(', out);
-		USTPUTC('(', out);
+		USTPUTC('(' /*)*/, out);
+		USTPUTC('(' /*)*/, out);
 		/*
 		 * Need 2 of them because there will (should be)
 		 * two closing ))'s to follow later.
@@ -2009,8 +2035,12 @@ parsearith: {
 
 #ifdef mkinit
 RESET {
+	struct heredoc;
+	extern struct heredoc *heredoclist;
+
 	tokpushback = 0;
 	checkkwd = 0;
+	heredoclist = NULL;
 }
 #endif
 

Reply via email to