Module Name:    src
Committed By:   kre
Date:           Fri Jun 30 23:02:56 UTC 2017

Modified Files:
        src/bin/sh: expand.c input.c input.h option.list parser.c parser.h

Log Message:
Implement PS1, PS2 and PS4 expansions (variable expansions, arithmetic
expansions, and if enabled by the promptcmds option, command substitutions.)


To generate a diff of this commit:
cvs rdiff -u -r1.118 -r1.119 src/bin/sh/expand.c
cvs rdiff -u -r1.58 -r1.59 src/bin/sh/input.c
cvs rdiff -u -r1.19 -r1.20 src/bin/sh/input.h
cvs rdiff -u -r1.4 -r1.5 src/bin/sh/option.list
cvs rdiff -u -r1.139 -r1.140 src/bin/sh/parser.c
cvs rdiff -u -r1.22 -r1.23 src/bin/sh/parser.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/expand.c
diff -u src/bin/sh/expand.c:1.118 src/bin/sh/expand.c:1.119
--- src/bin/sh/expand.c:1.118	Mon Jun 19 02:46:50 2017
+++ src/bin/sh/expand.c	Fri Jun 30 23:02:56 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: expand.c,v 1.118 2017/06/19 02:46:50 kre Exp $	*/
+/*	$NetBSD: expand.c,v 1.119 2017/06/30 23:02:56 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.118 2017/06/19 02:46:50 kre Exp $");
+__RCSID("$NetBSD: expand.c,v 1.119 2017/06/30 23:02:56 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -177,7 +177,7 @@ expandarg(union node *arg, struct arglis
 	line_number = arg->narg.lineno;
 	argstr(arg->narg.text, flag);
 	if (arglist == NULL) {
-		NULLTERM_4_TRACE(expdest);
+		STACKSTRNUL(expdest);
 		CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n",
 		    expdest - stackblock(), stackblock()));
 		return;			/* here document expanded */
@@ -597,8 +597,10 @@ expbackq(union node *cmd, int quoted, in
 		if (--in.nleft < 0) {
 			if (in.fd < 0)
 				break;
+			INTON;
 			while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
 				continue;
+			INTOFF;
 			VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i));
 			if (i <= 0)
 				break;

Index: src/bin/sh/input.c
diff -u src/bin/sh/input.c:1.58 src/bin/sh/input.c:1.59
--- src/bin/sh/input.c:1.58	Wed Jun  7 05:08:32 2017
+++ src/bin/sh/input.c	Fri Jun 30 23:02:56 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre Exp $	*/
+/*	$NetBSD: input.c,v 1.59 2017/06/30 23:02:56 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.58 2017/06/07 05:08:32 kre Exp $");
+__RCSID("$NetBSD: input.c,v 1.59 2017/06/30 23:02:56 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -471,6 +471,7 @@ setinputfd(int fd, int push)
 		parsefile->buf = ckmalloc(BUFSIZ);
 	parselleft = parsenleft = 0;
 	plinno = 1;
+	CTRACE(DBG_INPUT, ("setinputfd(%d, %d); plinno=1\n", fd, push));
 }
 
 
@@ -489,7 +490,7 @@ setinputstring(char *string, int push, i
 	parselleft = parsenleft = strlen(string);
 	parsefile->buf = NULL;
 	plinno = line1;
-	TRACE(("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string,
+	CTRACE(DBG_INPUT, ("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string,
 	    (parsenleft > 20 ? "..." : ""), parsenleft, push, line1));
 	INTON;
 }
@@ -506,6 +507,10 @@ pushfile(void)
 {
 	struct parsefile *pf;
 
+	VTRACE(DBG_INPUT, ("pushfile(): fd=%d nl=%d ll=%d \"%.*s\" plinno=%d\n",
+	    parsefile->fd, parsenleft, parselleft,
+	    parsenleft, parsenextc, plinno));
+
 	parsefile->nleft = parsenleft;
 	parsefile->lleft = parselleft;
 	parsefile->nextc = parsenextc;
@@ -536,10 +541,40 @@ popfile(void)
 	parsenleft = parsefile->nleft;
 	parselleft = parsefile->lleft;
 	parsenextc = parsefile->nextc;
+	VTRACE(DBG_INPUT,
+	    ("popfile(): fd=%d nl=%d ll=%d \"%.*s\" plinno:%d->%d\n",
+	    parsefile->fd, parsenleft, parselleft,
+	    parsenleft, parsenextc, plinno, parsefile->linno));
 	plinno = parsefile->linno;
 	INTON;
 }
 
+/*
+ * Return current file (to go back to it later using popfilesupto()).
+ */
+
+struct parsefile *
+getcurrentfile(void)
+{
+	return parsefile;
+}
+
+
+/*
+ * Pop files until the given file is on top again. Useful for regular
+ * builtins that read shell commands from files or strings.
+ * If the given file is not an active file, an error is raised.
+ */
+
+void
+popfilesupto(struct parsefile *file)
+{
+	while (parsefile != file && parsefile != &basepf)
+		popfile();
+	if (parsefile != file)
+		error("popfilesupto() misused");
+}
+
 
 /*
  * Return to top level.

Index: src/bin/sh/input.h
diff -u src/bin/sh/input.h:1.19 src/bin/sh/input.h:1.20
--- src/bin/sh/input.h:1.19	Wed Jun  7 04:44:17 2017
+++ src/bin/sh/input.h	Fri Jun 30 23:02:56 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: input.h,v 1.19 2017/06/07 04:44:17 kre Exp $	*/
+/*	$NetBSD: input.h,v 1.20 2017/06/30 23:02:56 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -45,8 +45,10 @@ extern int plinno;
 extern int parsenleft;		/* number of characters left in input buffer */
 extern const char *parsenextc;	/* next character in input buffer */
 extern int init_editline;	/* 0 == not setup, 1 == OK, -1 == failed */
+extern int whichprompt;		/* 1 ==> PS1, 2 ==> PS2 */
 
 struct alias;
+struct parsefile;
 
 char *pfgets(char *, int);
 int pgetc(void);
@@ -58,6 +60,8 @@ void setinputfile(const char *, int);
 void setinputfd(int, int);
 void setinputstring(char *, int, int);
 void popfile(void);
+struct parsefile *getcurrentfile(void);
+void popfilesupto(struct parsefile *);
 void popallfiles(void);
 void closescript(int);
 

Index: src/bin/sh/option.list
diff -u src/bin/sh/option.list:1.4 src/bin/sh/option.list:1.5
--- src/bin/sh/option.list:1.4	Sat Jun 17 07:50:35 2017
+++ src/bin/sh/option.list	Fri Jun 30 23:02:56 2017
@@ -1,10 +1,10 @@
-/* $NetBSD: option.list,v 1.4 2017/06/17 07:50:35 kre Exp $ */
+/* $NetBSD: option.list,v 1.5 2017/06/30 23:02:56 kre Exp $ */
 
 /*
  * define the shell's settable options
  *
  *	new options can be defined by adding them here,
- *	but they do nothing untilcode to implement them
+ *	but they do nothing until code to implement them
  *	is added (using the "var name" field)
  */
 
@@ -64,6 +64,7 @@ pflag	nopriv		p		# preserve privs if set
 posix	posix				# be closer to POSIX compat
 qflag	quietprofile	q		# disable -v/-x in startup files
 fnline1	local_lineno	L on		# number lines in funcs starting at 1
+promptcmds promptcmds			# allow $( ) in PS1 (et al).
 
 // editline/history related options ("vi" is standard, 'V' and others are not)
 // only one of vi/emacs can be set, hence the "set" definition, value

Index: src/bin/sh/parser.c
diff -u src/bin/sh/parser.c:1.139 src/bin/sh/parser.c:1.140
--- src/bin/sh/parser.c:1.139	Sat Jun 24 11:23:35 2017
+++ src/bin/sh/parser.c	Fri Jun 30 23:02:56 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.c,v 1.139 2017/06/24 11:23:35 kre Exp $	*/
+/*	$NetBSD: parser.c,v 1.140 2017/06/30 23:02:56 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.139 2017/06/24 11:23:35 kre Exp $");
+__RCSID("$NetBSD: parser.c,v 1.140 2017/06/30 23:02:56 kre Exp $");
 #endif
 #endif /* not lint */
 
@@ -74,34 +74,34 @@ __RCSID("$NetBSD: parser.c,v 1.139 2017/
 #define OPENBRACE '{'
 #define CLOSEBRACE '}'
 
-
-struct heredoc {
-	struct heredoc *next;	/* next here document in list */
+struct HereDoc {
+	struct HereDoc *next;	/* next here document in list */
 	union node *here;		/* redirection node */
 	char *eofmark;		/* string indicating end of input */
 	int striptabs;		/* if set, strip leading tabs */
 	int startline;		/* line number where << seen */
 };
 
+MKINIT struct parse_state parse_state;
+union parse_state_p psp = { .c_current_parser = &parse_state };
 
-
-static int noalias = 0;		/* when set, don't handle aliases */
-struct heredoc *heredoclist;	/* list of here documents to read */
-int parsebackquote;		/* nonzero if we are inside backquotes */
-int doprompt;			/* if set, prompt the user */
-int needprompt;			/* true if interactive and at start of line */
-int lasttoken;			/* last token read */
-MKINIT int tokpushback;		/* last token pushed back */
-char *wordtext;			/* text of last word returned by readtoken */
-MKINIT int checkkwd;		/* 1 == check for kwds, 2 == also eat newlines */
-struct nodelist *backquotelist;
-union node *redirnode;
-struct heredoc *heredoc;
-int quoteflag;			/* set if (part of) last token was quoted */
-int startlinno;			/* line # where last token started */
-int funclinno;			/* line # where the current function started */
-int elided_nl;			/* count of \ \n (deleted \n's) we have seen */
-
+static const struct parse_state init_parse_state = {	/* all 0's ... */
+	.ps_noalias = 0,
+	.ps_heredoclist = NULL,
+	.ps_parsebackquote = 0,
+	.ps_doprompt = 0,
+	.ps_needprompt = 0,
+	.ps_lasttoken = 0,
+	.ps_tokpushback = 0,
+	.ps_wordtext = NULL,
+	.ps_checkkwd = 0,
+	.ps_redirnode = NULL,
+	.ps_heredoc = NULL,
+	.ps_quoteflag = 0,
+	.ps_startlinno = 0,
+	.ps_funclinno = 0,
+	.ps_elided_nl = 0,
+};
 
 STATIC union node *list(int, int);
 STATIC union node *andor(void);
@@ -122,7 +122,6 @@ STATIC void synerror(const char *) __dea
 STATIC void setprompt(int);
 STATIC int pgetc_linecont(void);
 
-
 static const char EOFhere[] = "EOF reading here (<<) document";
 
 #ifdef DEBUG
@@ -758,8 +757,8 @@ parsefname(void)
 	if (readtoken() != TWORD)
 		synexpect(-1, 0);
 	if (n->type == NHERE) {
-		struct heredoc *here = heredoc;
-		struct heredoc *p;
+		struct HereDoc *here = heredoc;
+		struct HereDoc *p;
 
 		if (quoteflag == 0)
 			n->type = NXHERE;
@@ -948,7 +947,7 @@ insert_elided_nl(char *str)
 STATIC void
 readheredocs(void)
 {
-	struct heredoc *here;
+	struct HereDoc *here;
 	union node *n;
 	int line, l;
 
@@ -1012,7 +1011,7 @@ readtoken(void)
 #endif
 	struct alias *ap;
 
-	top:
+ top:
 	t = xxreadtoken();
 
 	if (checkkwd) {
@@ -1045,6 +1044,9 @@ readtoken(void)
 			}
 			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;
@@ -1547,7 +1549,7 @@ parseredir(const char *out,  int c)
 				np->nfile.fd = 0;
 			}
 			np->type = NHERE;
-			heredoc = stalloc(sizeof(struct heredoc));
+			heredoc = stalloc(sizeof(struct HereDoc));
 			heredoc->here = np;
 			heredoc->startline = plinno;
 			if ((c = pgetc_linecont()) == '-') {
@@ -2034,13 +2036,14 @@ parsearith: {
 
 
 #ifdef mkinit
+INCLUDE "parser.h"
+
 RESET {
-	struct heredoc;
-	extern struct heredoc *heredoclist;
+	psp.v_current_parser = &parse_state;
 
-	tokpushback = 0;
-	checkkwd = 0;
-	heredoclist = NULL;
+	parse_state.ps_tokpushback = 0;
+	parse_state.ps_checkkwd = 0;
+	parse_state.ps_heredoclist = NULL;
 }
 #endif
 
@@ -2177,14 +2180,102 @@ pgetc_linecont(void)
 const char *
 getprompt(void *unused)
 {
+	char *p;
+	const char *cp;
+
+	if (!doprompt)
+		return "";
+
+	VTRACE(DBG_PARSE|DBG_EXPAND, ("getprompt %d\n", whichprompt));
+
 	switch (whichprompt) {
 	case 0:
 		return "";
 	case 1:
-		return ps1val();
+		p = ps1val();
+		break;
 	case 2:
-		return ps2val();
+		p = ps2val();
+		break;
 	default:
 		return "<internal prompt error>";
 	}
+	if (p == NULL)
+		return "";
+
+	VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt <<%s>>\n", p));
+
+	cp = expandstr(p, plinno);
+
+	VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt -> <<%s>>\n", cp));
+
+	return cp;
+}
+
+/*
+ * Expand a string ... used for expanding prompts (PS1...)
+ *
+ * Never return NULL, always some string (return input string if invalid)
+ */
+const char *
+expandstr(char *ps, int lineno)
+{
+	union node n;
+	struct jmploc jmploc;
+	struct jmploc *const savehandler = handler;
+	struct parsefile *const savetopfile = getcurrentfile();
+	const int save_x = xflag;
+	struct parse_state new_state = init_parse_state;
+	struct parse_state *const saveparser = psp.v_current_parser;
+	struct stackmark smark;
+	const char *result = NULL;
+
+	setstackmark(&smark);
+	/*
+	 * At this point we anticipate that there may be a string
+	 * growing on the stack, but we have no idea how big it is.
+	 * However we know that it cannot be bigger than the current
+	 * allocated stack block, so simply reserve the whole thing,
+	 * then we can use the stack without barfing all over what
+	 * is there already...   (the stack mark undoes this later.)
+	 */
+	(void) stalloc(stackblocksize());
+
+	if (!setjmp(jmploc.loc)) {
+		handler = &jmploc;
+
+		psp.v_current_parser = &new_state;
+		setinputstring(ps, 1, lineno);
+
+		readtoken1(pgetc(), DQSYNTAX, 1);
+		if (backquotelist != NULL && !promptcmds)
+			result = "-o promptcmds not set: ";
+		else {
+			n.narg.type = NARG;
+			n.narg.next = NULL;
+			n.narg.text = wordtext;
+			n.narg.lineno = lineno;
+			n.narg.backquote = backquotelist;
+
+			xflag = 0;	/* we might be expanding PS4 ... */
+			expandarg(&n, NULL, 0);
+			result = stackblock();
+		}
+		INTOFF;
+	}
+	psp.v_current_parser = saveparser;
+	xflag = save_x;
+	popfilesupto(savetopfile);
+	handler = savehandler;
+	popstackmark(&smark);
+
+	if (result != NULL) {
+		INTON;
+	} else {
+		if (exception == EXINT)
+			exraise(SIGINT);
+		result = ps;
+	}
+
+	return result;
 }

Index: src/bin/sh/parser.h
diff -u src/bin/sh/parser.h:1.22 src/bin/sh/parser.h:1.23
--- src/bin/sh/parser.h:1.22	Wed Jun  7 05:08:32 2017
+++ src/bin/sh/parser.h	Fri Jun 30 23:02:56 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: parser.h,v 1.22 2017/06/07 05:08:32 kre Exp $	*/
+/*	$NetBSD: parser.h,v 1.23 2017/06/30 23:02:56 kre Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993
@@ -70,21 +70,90 @@
 #define VSTRIMRIGHTMAX 	0x9		/* ${var%%pattern} */
 #define VSLENGTH	0xa		/* ${#var} */
 
+union node *parsecmd(int);
+void fixredir(union node *, const char *, int);
+int goodname(char *);
+const char *getprompt(void *);
+const char *expandstr(char *, int);
+
+struct HereDoc;
+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 */
+	int ps_needprompt;		/* true if interactive at line start */
+	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 */
+	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 */
+	int ps_quoteflag;		/* set if (part) of token was quoted */
+	int ps_startlinno;		/* line # where last token started */
+	int ps_funclinno;		/* line # of the current function */
+	int ps_elided_nl;		/* count of \ \n pairs we have seen */
+};
+
+/*
+ * The parser references the elements of struct parse_state quite
+ * frequently - they used to be simple globals, so one memory ref
+ * per access, adding an indirect through global ptr would not be
+ * nice.   The following gross hack allows most of that cost to be
+ * avoided, by allowing the compiler to understand that the global
+ * pointer is in fact constant in any function, and so its value can
+ * be cached, rather than needing to be fetched every time in case
+ * some other called function has changed it.
+ *
+ * The rule to make this work is that any function that wants
+ * to alter the global must restore it before it returns (and thus
+ * must have an error trap handler).  That means that the struct
+ * used for the new parser state can be a local in that function's
+ * stack frame, it never needs to be malloc'd.
+ */
+
+union parse_state_p {
+	struct parse_state *const	c_current_parser;
+	struct parse_state *		v_current_parser;
+};
+
+extern union parse_state_p psp;
+
+#define	current_parser (psp.c_current_parser)
+
+/*
+ * Perhaps one day emulate "static" by moving most of these definitions into
+ * parser.c ...  (only checkkwd & tokpushback are used outside parser.c,
+ * and only in init.c as a RESET activity)
+ */
+#define	tokpushback	(current_parser->ps_tokpushback)
+#define	checkkwd	(current_parser->ps_checkkwd)
+
+#define	noalias		(current_parser->ps_noalias)
+#define	heredoclist	(current_parser->ps_heredoclist)
+#define	parsebackquote	(current_parser->ps_parsebackquote)
+#define	doprompt	(current_parser->ps_doprompt)
+#define	needprompt	(current_parser->ps_needprompt)
+#define	lasttoken	(current_parser->ps_lasttoken)
+#define	wordtext	(current_parser->ps_wordtext)
+#define	backquotelist	(current_parser->ps_backquotelist)
+#define	redirnode	(current_parser->ps_redirnode)
+#define	heredoc		(current_parser->ps_heredoc)
+#define	quoteflag	(current_parser->ps_quoteflag)
+#define	startlinno	(current_parser->ps_startlinno)
+#define	funclinno	(current_parser->ps_funclinno)
+#define	elided_nl	(current_parser->ps_elided_nl)
 
 /*
  * 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.
  */
-extern int tokpushback;
-#define NEOF ((union node *)&tokpushback)
-extern int whichprompt;		/* 1 == PS1, 2 == PS2 */
-
-
-union node *parsecmd(int);
-void fixredir(union node *, const char *, int);
-int goodname(char *);
-const char *getprompt(void *);
+#define NEOF ((union node *)&psp)
 
 #ifdef DEBUG
 extern int parsing;

Reply via email to