Module Name:    src
Committed By:   kre
Date:           Mon Dec  3 06:40:26 UTC 2018

Modified Files:
        src/bin/sh: alias.c alias.h eval.c input.c parser.c parser.h syntax.c
            syntax.h

Log Message:
Revamp aliases - as dumb an idea as they are, if we're going
to have them, they should work as documented, not cause core
dumps, reference after free, incorrect replacements, failing
to implement alias after alias, ...

The big comment that ended:
          This is a good idea ------- ***NOT***
and the hack it was describing are gone.

Note that most of this was from original CVS version 1.1
code (ie: came from the original import, even before 4.4-Lite
was merged.   That is, May 1994.  And no-one in 24.5 years
noticed (or at least complained about)  all the bugs (or at
least, most of them)).

With these changes, aliases ought to work (if you can call it
that) as they are expected to by POSIX.   Now if only we could
get POSIX to delete them (or make them optional)...

Changes partly inspired by similar changes made by FreeBSD,
(as was the previous change to alias.c, forgot ack in commit
log for that one, apologies) but done a little differently,
and perhaps with a slightly better outcome.


To generate a diff of this commit:
cvs rdiff -u -r1.19 -r1.20 src/bin/sh/alias.c
cvs rdiff -u -r1.8 -r1.9 src/bin/sh/alias.h
cvs rdiff -u -r1.165 -r1.166 src/bin/sh/eval.c
cvs rdiff -u -r1.63 -r1.64 src/bin/sh/input.c
cvs rdiff -u -r1.155 -r1.156 src/bin/sh/parser.c
cvs rdiff -u -r1.25 -r1.26 src/bin/sh/parser.h
cvs rdiff -u -r1.6 -r1.7 src/bin/sh/syntax.c
cvs rdiff -u -r1.10 -r1.11 src/bin/sh/syntax.h

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/alias.c
diff -u src/bin/sh/alias.c:1.19 src/bin/sh/alias.c:1.20
--- src/bin/sh/alias.c:1.19	Sun Dec  2 10:27:58 2018
+++ src/bin/sh/alias.c	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: alias.c,v 1.19 2018/12/02 10:27:58 kre Exp $	*/
+/*	$NetBSD: alias.c,v 1.20 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)alias.c	8.3 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: alias.c,v 1.19 2018/12/02 10:27:58 kre Exp $");
+__RCSID("$NetBSD: alias.c,v 1.20 2018/12/03 06:40:26 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -61,7 +61,9 @@ STATIC void setalias(char *, char *);
 STATIC int by_name(const void *, const void *);
 STATIC void list_aliases(void);
 STATIC int unalias(char *);
+STATIC struct alias **freealias(struct alias **, int);
 STATIC struct alias **hashalias(const char *);
+STATIC size_t countaliases(void);
 
 STATIC
 void
@@ -76,97 +78,80 @@ setalias(char *name, char *val)
 	ap = ckmalloc(sizeof (struct alias));
 	ap->name = savestr(name);
 	ap->flag = 0;
-	/*
-	 * XXX - HACK: in order that the parser will not finish reading the
-	 * alias value off the input before processing the next alias, we
-	 * dummy up an extra space at the end of the alias.  This is a crock
-	 * and should be re-thought.  The idea (if you feel inclined to help)
-	 * is to avoid alias recursions.  The mechanism used is: when
-	 * expanding an alias, the value of the alias is pushed back on the
-	 * input as a string and a pointer to the alias is stored with the
-	 * string.  The alias is marked as being in use.  When the input
-	 * routine finishes reading the string, it markes the alias not
-	 * in use.  The problem is synchronization with the parser.  Since
-	 * it reads ahead, the alias is marked not in use before the
-	 * resulting token(s) is next checked for further alias sub.  The
-	 * H A C K is that we add a little fluff after the alias value
-	 * so that the string will not be exhausted.  This is a good
-	 * idea ------- ***NOT***
-	 */
-#ifdef notyet
 	ap->val = savestr(val);
-#else /* hack */
-	{
-	int len = strlen(val);
-	ap->val = ckmalloc(len + 2);
-	memcpy(ap->val, val, len);
-	ap->val[len] = ' ';	/* fluff */
-	ap->val[len+1] = '\0';
-	}
-#endif
 	ap->next = *app;
 	*app = ap;
 	INTON;
 }
 
+STATIC struct alias **
+freealias(struct alias **app, int force)
+{
+	struct alias *ap = *app;
+
+	if (ap == NULL)
+		return app;
+
+	/*
+	 * if the alias is currently in use (i.e. its
+	 * buffer is being used by the input routine) we
+	 * just null out the name instead of discarding it.
+	 * If we encounter it later, when it is idle,
+	 * we will finish freeing it then.
+	 *
+	 * Unless we want to simply free everything (INIT)
+	 */
+	if (ap->flag & ALIASINUSE && !force) {
+		*ap->name = '\0';
+		return &ap->next;
+	}
+
+	INTOFF;
+	*app = ap->next;
+	ckfree(ap->name);
+	ckfree(ap->val);
+	ckfree(ap);
+	INTON;
+
+	return app;
+}
+
 STATIC int
 unalias(char *name)
 {
 	struct alias *ap, **app;
 
 	app = hashalias(name);
-
-	for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
+	while ((ap = *app) != NULL) {
 		if (equal(name, ap->name)) {
-			/*
-			 * if the alias is currently in use (i.e. its
-			 * buffer is being used by the input routine) we
-			 * just null out the name instead of freeing it.
-			 * We could clear it out later, but this situation
-			 * is so rare that it hardly seems worth it.
-			 */
-			if (ap->flag & ALIASINUSE)
-				*ap->name = '\0';
-			else {
-				INTOFF;
-				*app = ap->next;
-				ckfree(ap->name);
-				ckfree(ap->val);
-				ckfree(ap);
-				INTON;
-			}
+			(void) freealias(app, 0);
 			return 0;
 		}
+		app = &ap->next;
 	}
 
 	return 1;
 }
 
 #ifdef mkinit
-MKINIT void rmaliases(void);
+MKINIT void rmaliases(int);
 
 SHELLPROC {
-	rmaliases();
+	rmaliases(1);
 }
 #endif
 
 void
-rmaliases(void)
+rmaliases(int force)
 {
-	struct alias *ap, *tmp;
+	struct alias **app;
 	int i;
 
 	INTOFF;
 	for (i = 0; i < ATABSIZE; i++) {
-		ap = atab[i];
-		atab[i] = NULL;
-		while (ap) {
-			ckfree(ap->name);
-			ckfree(ap->val);
-			tmp = ap;
-			ap = ap->next;
-			ckfree(tmp);
-		}
+		app = &atab[i];
+		while (*app)
+			app = freealias(app, force);
 	}
 	INTON;
 }
@@ -176,12 +161,13 @@ lookupalias(const char *name, int check)
 {
 	struct alias *ap = *hashalias(name);
 
-	for (; ap; ap = ap->next) {
+	while (ap != NULL) {
 		if (equal(name, ap->name)) {
 			if (check && (ap->flag & ALIASINUSE))
 				return NULL;
 			return ap;
 		}
+		ap = ap->next;
 	}
 
 	return NULL;
@@ -214,12 +200,8 @@ list_aliases(void)
 	const struct alias **aliases;
 	const struct alias *ap;
 
-	n = 0;
-	for (i = 0; i < ATABSIZE; i++)
-		for (ap = atab[i]; ap != NULL; ap = ap->next)
-			if (ap->name[0] != '\0')
-				n++;
-
+	INTOFF;
+	n = countaliases();
 	aliases = ckmalloc(n * sizeof aliases[0]);
 
 	j = 0;
@@ -227,6 +209,9 @@ list_aliases(void)
 		for (ap = atab[i]; ap != NULL; ap = ap->next)
 			if (ap->name[0] != '\0')
 				aliases[j++] = ap;
+	if (j != n)
+		error("Alias count botch");
+	INTON;
 
 	qsort(aliases, n, sizeof aliases[0], by_name);
 
@@ -239,6 +224,34 @@ list_aliases(void)
 	ckfree(aliases);
 }
 
+/*
+ * Count how many aliases are defined (skipping any
+ * that have been deleted, but don't know it yet).
+ * Use this opportunity to clean up any of those
+ * zombies that are no longer needed.
+ */
+STATIC size_t
+countaliases(void)
+{
+	struct alias *ap, **app;
+	size_t n;
+	int i;
+
+	n = 0;
+	for (i = 0; i < ATABSIZE; i++)
+		for (app = &atab[i]; (ap = *app) != NULL;) {
+			if (ap->name[0] != '\0')
+				n++;
+			else {
+				app = freealias(app, 0);
+				continue;
+			}
+			app = &ap->next;
+		}
+
+	return n;
+}
+
 int
 aliascmd(int argc, char **argv)
 {
@@ -277,12 +290,14 @@ unaliascmd(int argc, char **argv)
 
 	while ((i = nextopt("a")) != '\0') {
 		if (i == 'a') {
-			rmaliases();
+			rmaliases(0);
 			return 0;
 		}
 	}
+
+	(void)countaliases();	/* delete any dead ones */
 	for (i = 0; *argptr; argptr++)
-		i = unalias(*argptr);
+		i |= unalias(*argptr);
 
 	return i;
 }

Index: src/bin/sh/alias.h
diff -u src/bin/sh/alias.h:1.8 src/bin/sh/alias.h:1.9
--- src/bin/sh/alias.h:1.8	Wed Jun 18 18:17:30 2014
+++ src/bin/sh/alias.h	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: alias.h,v 1.8 2014/06/18 18:17:30 christos Exp $	*/
+/*	$NetBSD: alias.h,v 1.9 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1993
@@ -45,4 +45,4 @@ struct alias {
 
 struct alias *lookupalias(const char *, int);
 const char *alias_text(void *, const char *);
-void rmaliases(void);
+void rmaliases(int);

Index: src/bin/sh/eval.c
diff -u src/bin/sh/eval.c:1.165 src/bin/sh/eval.c:1.166
--- src/bin/sh/eval.c:1.165	Fri Nov 30 23:22:45 2018
+++ src/bin/sh/eval.c	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: eval.c,v 1.165 2018/11/30 23:22:45 kre Exp $	*/
+/*	$NetBSD: eval.c,v 1.166 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)eval.c	8.9 (Berkeley) 6/8/95";
 #else
-__RCSID("$NetBSD: eval.c,v 1.165 2018/11/30 23:22:45 kre Exp $");
+__RCSID("$NetBSD: eval.c,v 1.166 2018/12/03 06:40:26 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -888,18 +888,11 @@ evalcommand(union node *cmd, int flgs, s
 	varflag = 1;
 	/* Expand arguments, ignoring the initial 'name=value' ones */
 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
-		char *p = argp->narg.text;
-
+		if (varflag && isassignment(argp->narg.text))
+			continue;
+		varflag = 0;
 		line_number = argp->narg.lineno;
-		if (varflag && is_name(*p)) {
-			do {
-				p++;
-			} while (is_in_name(*p));
-			if (*p == '=')
-				continue;
-		}
 		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
-		varflag = 0;
 	}
 	*arglist.lastp = NULL;
 
@@ -908,15 +901,8 @@ evalcommand(union node *cmd, int flgs, s
 	/* Now do the initial 'name=value' ones we skipped above */
 	varlist.lastp = &varlist.list;
 	for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
-		char *p = argp->narg.text;
-
 		line_number = argp->narg.lineno;
-		if (!is_name(*p))
-			break;
-		do
-			p++;
-		while (is_in_name(*p));
-		if (*p != '=')
+		if (!isassignment(argp->narg.text))
 			break;
 		expandarg(argp, &varlist, EXP_VARTILDE);
 	}

Index: src/bin/sh/input.c
diff -u src/bin/sh/input.c:1.63 src/bin/sh/input.c:1.64
--- src/bin/sh/input.c:1.63	Sun Aug 19 23:50:27 2018
+++ src/bin/sh/input.c	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: input.c,v 1.63 2018/08/19 23:50:27 kre Exp $	*/
+/*	$NetBSD: input.c,v 1.64 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
 #else
-__RCSID("$NetBSD: input.c,v 1.63 2018/08/19 23:50:27 kre Exp $");
+__RCSID("$NetBSD: input.c,v 1.64 2018/12/03 06:40:26 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -168,7 +168,12 @@ pfgets(char *line, int len)
 int
 pgetc(void)
 {
-	return pgetc_macro();
+	int c;
+
+	c = pgetc_macro();
+	if (c == PFAKE)
+		c = pgetc_macro();
+	return c;
 }
 
 
@@ -248,6 +253,8 @@ preadbuffer(void)
 	char savec;
 
 	while (parsefile->strpush) {
+		if (parsenleft == -1 && parsefile->strpush->ap != NULL)
+			return PFAKE;
 		popstring();
 		if (--parsenleft >= 0)
 			return (*parsenextc++);
@@ -421,6 +428,12 @@ popstring(void)
 	struct strpush *sp = parsefile->strpush;
 
 	INTOFF;
+	if (sp->ap) {
+		if (parsenextc != sp->ap->val &&
+		   (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
+			checkkwd |= CHKALIAS;
+		sp->ap->flag &= ~ALIASINUSE;
+	}
 	parsenextc = sp->prevstring;
 	parsenleft = sp->prevnleft;
 	parselleft = sp->prevlleft;
@@ -429,8 +442,6 @@ popstring(void)
 	    sp->ap ? " from alias:'" : "", sp->ap ? sp->ap->name : "",
 	    sp->ap ? "'" : "", parsenleft, parselleft, parsenleft, parsenextc));
 
-	if (sp->ap)
-		sp->ap->flag &= ~ALIASINUSE;
 	parsefile->strpush = sp->prev;
 	if (sp != &(parsefile->basestrpush))
 		ckfree(sp);

Index: src/bin/sh/parser.c
diff -u src/bin/sh/parser.c:1.155 src/bin/sh/parser.c:1.156
--- src/bin/sh/parser.c:1.155	Sat Dec  1 07:02:23 2018
+++ src/bin/sh/parser.c	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.c,v 1.155 2018/12/01 07:02:23 kre Exp $	*/
+/*	$NetBSD: parser.c,v 1.156 2018/12/03 06:40:26 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.155 2018/12/01 07:02:23 kre Exp $");
+__RCSID("$NetBSD: parser.c,v 1.156 2018/12/03 06:40:26 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -86,7 +86,6 @@ MKINIT struct parse_state parse_state;
 union parse_state_p psp = { .c_current_parser = &parse_state };
 
 static const struct parse_state init_parse_state = {	/* all 0's ... */
-	.ps_noalias = 0,
 	.ps_heredoclist = NULL,
 	.ps_parsebackquote = 0,
 	.ps_doprompt = 0,
@@ -184,7 +183,7 @@ list(int nlflag)
 
 	CTRACE(DBG_PARSE, ("list(%d): entered @%d\n",nlflag,plinno));
 
-	checkkwd = 2;
+	checkkwd = CHKNL | CHKKWD | CHKALIAS;
 	if (nlflag == 0 && tokendlist[peektoken()])
 		return NULL;
 	ntop = n1 = NULL;
@@ -237,7 +236,7 @@ list(int nlflag)
 			else
 				tokpushback++;
 
-			checkkwd = 2;
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
 			if (!nlflag && tokendlist[peektoken()])
 				return ntop;
 			break;
@@ -290,7 +289,7 @@ pipeline(void)
 	CTRACE(DBG_PARSE, ("pipeline: entered @%d\n", plinno));
 
 	negate = 0;
-	checkkwd = 2;
+	checkkwd = CHKNL | CHKKWD | CHKALIAS;
 	while (readtoken() == TNOT) {
 		CTRACE(DBG_PARSE, ("pipeline: TNOT recognized\n"));
 #ifndef BOGUS_NOT_COMMAND
@@ -345,7 +344,7 @@ command(void)
 
 	CTRACE(DBG_PARSE, ("command: entered @%d\n", plinno));
 
-	checkkwd = 2;
+	checkkwd = CHKNL | CHKKWD | CHKALIAS;
 	redir = NULL;
 	n1 = NULL;
 	rpp = &redir;
@@ -389,7 +388,7 @@ command(void)
 			tokpushback++;
 		}
 		consumetoken(TFI);
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 	case TWHILE:
 	case TUNTIL:
@@ -399,7 +398,7 @@ command(void)
 		consumetoken(TDO);
 		n1->nbinary.ch2 = list(0);
 		consumetoken(TDONE);
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 	case TFOR:
 		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
@@ -438,7 +437,7 @@ command(void)
 			if (lasttoken != TNL && lasttoken != TSEMI)
 				tokpushback++;
 		}
-		checkkwd = 2;
+		checkkwd = CHKNL | CHKKWD | CHKALIAS;
 		if ((t = readtoken()) == TDO)
 			t = TDONE;
 		else if (t == TBEGIN)
@@ -447,7 +446,7 @@ command(void)
 			synexpect(TDO, 0);
 		n1->nfor.body = list(0);
 		consumetoken(t);
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 	case TCASE:
 		n1 = stalloc(sizeof(struct ncase));
@@ -459,8 +458,7 @@ command(void)
 		if (lasttoken != TWORD || !equal(wordtext, "in"))
 			synexpect(-1, "in");
 		cpp = &n1->ncase.cases;
-		noalias = 1;
-		checkkwd = 2;
+		checkkwd = CHKNL | CHKKWD;
 		readtoken();
 		/*
 		 * Both ksh and bash accept 'case x in esac'
@@ -488,36 +486,32 @@ command(void)
 				if (lasttoken < TWORD)
 					synexpect(TWORD, 0);
 				*app = ap = makeword(startlinno);
-				checkkwd = 2;
+				checkkwd = CHKNL | CHKKWD;
 				if (readtoken() != TPIPE)
 					break;
 				app = &ap->narg.next;
 				readtoken();
 			}
-			noalias = 0;
 			if (lasttoken != TRP)
 				synexpect(TRP, 0);
 			cp->nclist.lineno = startlinno;
 			cp->nclist.body = list(0);
 
-			checkkwd = 2;
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
 			if ((t = readtoken()) != TESAC) {
 				if (t != TENDCASE && t != TCASEFALL) {
-					noalias = 0;
 					synexpect(TENDCASE, 0);
 				} else {
 					if (t == TCASEFALL)
 						cp->type = NCLISTCONT;
-					noalias = 1;
-					checkkwd = 2;
+					checkkwd = CHKNL | CHKKWD;
 					readtoken();
 				}
 			}
 			cpp = &cp->nclist.next;
 		}
-		noalias = 0;
 		*cpp = NULL;
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 	case TLP:
 		n1 = stalloc(sizeof(struct nredir));
@@ -527,14 +521,14 @@ command(void)
 		if (n1->nredir.n == NULL)
 			synexpect(-1, 0);
 		consumetoken(TRP);
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 	case TBEGIN:
 		n1 = list(0);
 		if (posix && n1 == NULL)
 			synexpect(-1, 0);
 		consumetoken(TEND);
-		checkkwd = 1;
+		checkkwd = CHKKWD | CHKALIAS;
 		break;
 
 	case TBACKGND:
@@ -621,6 +615,7 @@ simplecmd(union node **rpp, union node *
 	union node *args, **app;
 	union node *n = NULL;
 	int line = 0;
+	int savecheckkwd;
 #ifdef BOGUS_NOT_COMMAND
 	union node *n2;
 	int negate = 0;
@@ -645,13 +640,17 @@ simplecmd(union node **rpp, union node *
 	tokpushback++;
 #endif
 
+	savecheckkwd = CHKALIAS;
 	for (;;) {
+		checkkwd = savecheckkwd;
 		if (readtoken() == TWORD) {
 			if (line == 0)
 				line = startlinno;
 			n = makeword(startlinno);
 			*app = n;
 			app = &n->narg.next;
+			if (savecheckkwd != 0 && !isassignment(wordtext))
+				savecheckkwd = 0;
 		} else if (lasttoken == TREDIR) {
 			if (line == 0)
 				line = startlinno;
@@ -992,33 +991,29 @@ STATIC int
 readtoken(void)
 {
 	int t;
-	int savecheckkwd = checkkwd;
 #ifdef DEBUG
 	int alreadyseen = tokpushback;
+	int savecheckkwd = checkkwd;
 #endif
 	struct alias *ap;
 
  top:
 	t = xxreadtoken();
 
-	if (checkkwd) {
-		/*
-		 * eat newlines
-		 */
-		if (checkkwd == 2) {
-			checkkwd = 0;
-			while (t == TNL) {
-				readheredocs();
-				t = xxreadtoken();
-			}
-		} else
-			checkkwd = 0;
-		/*
-		 * check for keywords and aliases
-		 */
-		if (t == TWORD && !quoteflag) {
-			const char *const *pp;
+	if (checkkwd & CHKNL) {
+		while (t == TNL) {
+			readheredocs();
+			t = xxreadtoken();
+		}
+	}
 
+	/*
+	 * check for keywords and aliases
+	 */
+	if (t == TWORD && !quoteflag) {
+		const char *const *pp;
+
+		if (checkkwd & CHKKWD)
 			for (pp = parsekwd; *pp; pp++) {
 				if (**pp == *wordtext && equal(*pp, wordtext)) {
 					lasttoken = t = pp -
@@ -1029,21 +1024,23 @@ readtoken(void)
 					goto out;
 				}
 			}
-			if (!noalias &&
-			    (ap = lookupalias(wordtext, 1)) != NULL) {
-				VTRACE(DBG_PARSE,
-				    ("alias '%s' recognized -> <:%s:>\n",
-				    wordtext, ap->val));
-				pushstring(ap->val, strlen(ap->val), ap);
-				checkkwd = savecheckkwd;
-				goto top;
-			}
+
+		if (checkkwd & CHKALIAS &&
+		    (ap = lookupalias(wordtext, 1)) != NULL) {
+			VTRACE(DBG_PARSE,
+			    ("alias '%s' recognized -> <:%s:>\n",
+			    wordtext, ap->val));
+			pushstring(ap->val, strlen(ap->val), ap);
+			goto top;
 		}
- out:
-		checkkwd = (t == TNOT) ? savecheckkwd : 0;
 	}
-	VTRACE(DBG_PARSE, ("%stoken %s %s @%d\n", alreadyseen ? "reread " : "",
-	    tokname[t], t == TWORD ? wordtext : "", plinno));
+ out:
+	if (t != TNOT)
+		checkkwd = 0;
+
+	VTRACE(DBG_PARSE, ("%stoken %s %s @%d (chkkwd %x->%x)\n",
+	    alreadyseen ? "reread " : "", tokname[t],
+	    t == TWORD ? wordtext : "", plinno, savecheckkwd, checkkwd));
 	return (t);
 }
 
@@ -1086,7 +1083,7 @@ xxreadtoken(void)
 	for (;;) {	/* until token or start of word found */
 		c = pgetc_macro();
 		switch (c) {
-		case ' ': case '\t':
+		case ' ': case '\t': case PFAKE:
 			continue;
 		case '#':
 			while ((c = pgetc()) != '\n' && c != PEOF)
@@ -1774,6 +1771,10 @@ readtoken1(int firstc, char const *syn, 
 			out = insert_elided_nl(out);
 		CHECKSTRSPACE(6, out);	/* permit 6 calls to USTPUTC */
 		switch (syntax[c]) {
+		case CFAKE:
+			if (syntax == BASESYNTAX && varnest == 0)
+				break;
+			continue;
 		case CNL:	/* '\n' */
 			if (syntax == BASESYNTAX && varnest == 0)
 				break;	/* exit loop */
@@ -2262,6 +2263,17 @@ goodname(const char *name)
 	return 1;
 }
 
+int
+isassignment(const char *p)
+{
+	if (!is_name(*p))
+		return 0;
+	while (*++p != '=')
+		if (*p == '\0' || !is_in_name(*p))
+			return 0;
+	return 1;
+}
+
 /*
  * skip past any \n's, and leave lasttoken set to whatever follows
  */

Index: src/bin/sh/parser.h
diff -u src/bin/sh/parser.h:1.25 src/bin/sh/parser.h:1.26
--- src/bin/sh/parser.h:1.25	Sat Dec  1 01:21:06 2018
+++ src/bin/sh/parser.h	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.h,v 1.25 2018/12/01 01:21:06 kre Exp $	*/
+/*	$NetBSD: parser.h,v 1.26 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -74,6 +74,7 @@
 union node *parsecmd(int);
 void fixredir(union node *, const char *, int);
 int goodname(const char *);
+int isassignment(const char *);
 const char *getprompt(void *);
 const char *expandstr(char *, int);
 
@@ -82,7 +83,6 @@ union node;
 struct nodelist;
 
 struct parse_state {
-	int ps_noalias;			/* when set, don't handle aliases */
 	struct HereDoc *ps_heredoclist;	/* list of here documents to read */
 	int ps_parsebackquote;		/* nonzero inside backquotes */
 	int ps_doprompt;		/* if set, prompt the user */
@@ -90,7 +90,7 @@ struct parse_state {
 	int ps_lasttoken;		/* last token read */
 	int ps_tokpushback;		/* last token pushed back */
 	char *ps_wordtext;	/* text of last word returned by readtoken */
-	int ps_checkkwd;	/* 1 == check for kwds, 2 += eat newlines */
+	int ps_checkkwd;		/* word expansion flags, see below */
 	struct nodelist *ps_backquotelist; /* list of cmdsubs to process */
 	union node *ps_redirnode;	/* node for current redirect */
 	struct HereDoc *ps_heredoc;	/* current heredoc << beign parsed */
@@ -150,6 +150,13 @@ extern union parse_state_p psp;
 #define	elided_nl	(current_parser->ps_elided_nl)
 
 /*
+ * Values that can be set in checkkwd
+ */
+#define CHKKWD		0x01		/* turn word into keyword (if it is) */
+#define CHKNL		0x02		/* ignore leading \n's */
+#define CHKALIAS	0x04		/* lookup words as aliases and ... */
+
+/*
  * NEOF is returned by parsecmd when it encounters an end of file.  It
  * must be distinct from NULL, so we use the address of a variable that
  * happens to be handy.

Index: src/bin/sh/syntax.c
diff -u src/bin/sh/syntax.c:1.6 src/bin/sh/syntax.c:1.7
--- src/bin/sh/syntax.c:1.6	Fri Jul 20 22:47:26 2018
+++ src/bin/sh/syntax.c	Mon Dec  3 06:40:26 2018
@@ -1,7 +1,7 @@
-/*	$NetBSD: syntax.c,v 1.6 2018/07/20 22:47:26 kre Exp $	*/
+/*	$NetBSD: syntax.c,v 1.7 2018/12/03 06:40:26 kre Exp $	*/
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: syntax.c,v 1.6 2018/07/20 22:47:26 kre Exp $");
+__RCSID("$NetBSD: syntax.c,v 1.7 2018/12/03 06:40:26 kre Exp $");
 
 #include <limits.h>
 #include "shell.h"
@@ -12,12 +12,12 @@ __RCSID("$NetBSD: syntax.c,v 1.6 2018/07
 #error initialisation assumes 'CWORD' is zero
 #endif
 
-#define ndx(ch) (ch + 1 - CHAR_MIN)
+#define ndx(ch) (ch + 2 - CHAR_MIN)
 #define set(ch, val) [ndx(ch)] = val,
 #define set_range(s, e, val) [ndx(s) ... ndx(e)] = val,
 
 /* syntax table used when not in quotes */
-const char basesyntax[257] = { CEOF,
+const char basesyntax[258] = { CFAKE, CEOF,
     set_range(CTL_FIRST, CTL_LAST, CCTL)
     set('\n', CNL)
     set('\\', CBACK)
@@ -38,7 +38,7 @@ const char basesyntax[257] = { CEOF,
 };
 
 /* syntax table used when in double quotes */
-const char dqsyntax[257] = { CEOF,
+const char dqsyntax[258] = { CFAKE, CEOF,
     set_range(CTL_FIRST, CTL_LAST, CCTL)
     set('\n', CNL)
     set('\\', CBACK)
@@ -60,7 +60,7 @@ const char dqsyntax[257] = { CEOF,
 };
 
 /* syntax table used when in single quotes */
-const char sqsyntax[257] = { CEOF,
+const char sqsyntax[258] = { CFAKE, CEOF,
     set_range(CTL_FIRST, CTL_LAST, CCTL)
     set('\n', CNL)
     set('\'', CSQUOTE)
@@ -79,7 +79,7 @@ const char sqsyntax[257] = { CEOF,
 };
 
 /* syntax table used when in arithmetic */
-const char arisyntax[257] = { CEOF,
+const char arisyntax[258] = { CFAKE, CEOF,
     set_range(CTL_FIRST, CTL_LAST, CCTL)
     set('\n', CNL)
     set('\\', CBACK)
@@ -93,7 +93,7 @@ const char arisyntax[257] = { CEOF,
 };
 
 /* character classification table */
-const char is_type[257] = { 0,
+const char is_type[258] = { 0, 0,
     set_range('0', '9', ISDIGIT)
     set_range('a', 'z', ISLOWER)
     set_range('A', 'Z', ISUPPER)

Index: src/bin/sh/syntax.h
diff -u src/bin/sh/syntax.h:1.10 src/bin/sh/syntax.h:1.11
--- src/bin/sh/syntax.h:1.10	Sun Nov 18 17:23:37 2018
+++ src/bin/sh/syntax.h	Mon Dec  3 06:40:26 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: syntax.h,v 1.10 2018/11/18 17:23:37 kre Exp $	*/
+/*	$NetBSD: syntax.h,v 1.11 2018/12/03 06:40:26 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -50,6 +50,7 @@
 #define CSPCL 11		/* these terminate a word */
 #define CCTL 12			/* like CWORD, except it must be escaped */
 #define CSBACK 13		/* a backslash in a single quote syntax */
+#define CFAKE 14		/* a delimiter that does not exist */
 	/*
 	 * note CSBACK == (CCTL|1)
 	 * the code does not rely upon that, but keeping it allows a
@@ -64,8 +65,9 @@
 #define ISSPECL 020		/* the name of a special parameter */
 #define ISSPACE 040		/* a white space character */
 
-#define PEOF (CHAR_MIN - 1)
-#define SYNBASE (-PEOF)
+#define PEOF	(CHAR_MIN - 1)
+#define PFAKE	(CHAR_MIN - 2)
+#define SYNBASE	(-PFAKE)
 
 
 #define BASESYNTAX (basesyntax + SYNBASE)

Reply via email to