diff mbox

[v3,14/17] eval: Add assignment built-in support again

Message ID E1fJkIC-0005pn-Pc@gondobar (mailing list archive)
State Accepted
Delegated to: Herbert Xu
Headers show

Commit Message

Herbert Xu May 18, 2018, 6:39 p.m. UTC
This patch adds assignment built-in support that used to exist
in dash prior to 0.3.8-15.  This is because it will soon be part
of POSIX, and the semantics are now much better defined.

Recognition is done at execution time, so even "command -- export"
or "var=export; command $var" should work.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---

 src/eval.c   |  145 +++++++++++++++++++++++++++++++++--------------------------
 src/exec.c   |   21 ++++----
 src/exec.h   |    2 
 src/parser.c |    3 -
 src/parser.h |    1 
 5 files changed, 97 insertions(+), 75 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe dash" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/src/eval.c b/src/eval.c
index 1b803db..22fe2d7 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -100,8 +100,9 @@  STATIC int bltincmd(int, char **);
 
 
 STATIC const struct builtincmd bltin = {
-	name: nullstr,
-	builtin: bltincmd
+	.name = nullstr,
+	.builtin = bltincmd,
+	.flags = BUILTIN_REGULAR,
 };
 
 
@@ -648,22 +649,42 @@  out:
 		result->fd, result->buf, result->nleft, result->jp));
 }
 
-static char **
-parse_command_args(char **argv, const char **path)
+static struct strlist *fill_arglist(struct arglist *arglist,
+				    union node **argpp)
 {
+	struct strlist **lastp = arglist->lastp;
+	union node *argp;
+
+	while ((argp = *argpp)) {
+		expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
+		*argpp = argp->narg.next;
+		if (*lastp)
+			break;
+	}
+
+	return *lastp;
+}
+
+static int parse_command_args(struct arglist *arglist, union node **argpp,
+			      const char **path)
+{
+	struct strlist *sp = arglist->list;
 	char *cp, c;
 
 	for (;;) {
-		cp = *++argv;
-		if (!cp)
+		sp = unlikely(sp->next) ? sp->next :
+					  fill_arglist(arglist, argpp);
+		if (!sp)
 			return 0;
+		cp = sp->text;
 		if (*cp++ != '-')
 			break;
 		if (!(c = *cp++))
 			break;
 		if (c == '-' && !*cp) {
-			if (!*++argv)
+			if (likely(!sp->next) && !fill_arglist(arglist, argpp))
 				return 0;
+			sp = sp->next;
 			break;
 		}
 		do {
@@ -677,10 +698,10 @@  parse_command_args(char **argv, const char **path)
 			}
 		} while ((c = *cp++));
 	}
-	return argv;
-}
-
 
+	arglist->list = sp;
+	return DO_NOFUNC;
+}
 
 /*
  * Execute a simple command.
@@ -702,6 +723,7 @@  evalcommand(union node *cmd, int flags)
 	struct arglist varlist;
 	char **argv;
 	int argc;
+	struct strlist *osp;
 	struct strlist *sp;
 #ifdef notyet
 	int pip[2];
@@ -711,6 +733,7 @@  evalcommand(union node *cmd, int flags)
 	char *lastarg;
 	const char *path;
 	int spclbltin;
+	int cmd_flag;
 	int execcmd;
 	int status;
 	char **nargv;
@@ -733,13 +756,47 @@  evalcommand(union node *cmd, int flags)
 	arglist.lastp = &arglist.list;
 	*arglist.lastp = NULL;
 
+	cmd_flag = 0;
+	execcmd = 0;
+	spclbltin = -1;
+	path = NULL;
+
 	argc = 0;
-	for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
-		struct strlist **spp;
+	argp = cmd->ncmd.args;
+	if ((osp = fill_arglist(&arglist, &argp))) {
+		int pseudovarflag = 0;
 
-		spp = arglist.lastp;
-		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
-		for (sp = *spp; sp; sp = sp->next)
+		for (;;) {
+			find_command(arglist.list->text, &cmdentry,
+				     cmd_flag | DO_REGBLTIN, pathval());
+
+			/* implement bltin and command here */
+			if (cmdentry.cmdtype != CMDBUILTIN)
+				break;
+
+			pseudovarflag = cmdentry.u.cmd->flags & BUILTIN_ASSIGN;
+			if (likely(spclbltin < 0)) {
+				spclbltin = 
+					cmdentry.u.cmd->flags &
+					BUILTIN_SPECIAL
+				;
+			}
+			execcmd = cmdentry.u.cmd == EXECCMD;
+			if (likely(cmdentry.u.cmd != COMMANDCMD))
+				break;
+
+			cmd_flag = parse_command_args(&arglist, &argp, &path);
+			if (!cmd_flag)
+				break;
+		}
+
+		for (; argp; argp = argp->narg.next)
+			expandarg(argp, &arglist,
+				  pseudovarflag &&
+				  isassignment(argp->narg.text) ?
+				  EXP_VARTILDE : EXP_FULL | EXP_TILDE);
+
+		for (sp = arglist.list; sp; sp = sp->next)
 			argc++;
 	}
 
@@ -761,23 +818,13 @@  evalcommand(union node *cmd, int flags)
 	redir_stop = pushredir(cmd->ncmd.redirect);
 	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
 
-	path = vpath.text;
 	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
 		struct strlist **spp;
-		char *p;
 
 		spp = varlist.lastp;
 		expandarg(argp, &varlist, EXP_VARTILDE);
 
 		mklocal((*spp)->text);
-
-		/*
-		 * Modify the command lookup path, if a PATH= assignment
-		 * is present
-		 */
-		p = (*spp)->text;
-		if (varequal(p, path))
-			path = p;
 	}
 
 	/* Print the command if xflag is set. */
@@ -789,53 +836,24 @@  evalcommand(union node *cmd, int flags)
 		outstr(expandstr(ps4val()), out);
 		sep = 0;
 		sep = eprintlist(out, varlist.list, sep);
-		eprintlist(out, arglist.list, sep);
+		eprintlist(out, osp, sep);
 		outcslow('\n', out);
 #ifdef FLUSHERR
 		flushout(out);
 #endif
 	}
 
-	execcmd = 0;
-	spclbltin = -1;
-
 	/* Now locate the command. */
-	if (argc) {
-		const char *oldpath;
-		int cmd_flag = DO_ERR;
-
-		path += 5;
-		oldpath = path;
-		for (;;) {
-			find_command(argv[0], &cmdentry, cmd_flag, path);
-			if (cmdentry.cmdtype == CMDUNKNOWN) {
-				status = 127;
+	if (cmdentry.cmdtype != CMDBUILTIN ||
+	    !(cmdentry.u.cmd->flags & BUILTIN_REGULAR)) {
+		find_command(argv[0], &cmdentry, cmd_flag | DO_ERR,
+			     unlikely(path) ? path : pathval());
+		if (cmdentry.cmdtype == CMDUNKNOWN) {
+			status = 127;
 #ifdef FLUSHERR
-				flushout(&errout);
+			flushout(&errout);
 #endif
-				goto bail;
-			}
-
-			/* implement bltin and command here */
-			if (cmdentry.cmdtype != CMDBUILTIN)
-				break;
-			if (spclbltin < 0)
-				spclbltin = 
-					cmdentry.u.cmd->flags &
-					BUILTIN_SPECIAL
-				;
-			if (cmdentry.u.cmd == EXECCMD)
-				execcmd++;
-			if (cmdentry.u.cmd != COMMANDCMD)
-				break;
-
-			path = oldpath;
-			nargv = parse_command_args(argv, &path);
-			if (!nargv)
-				break;
-			argc -= nargv - argv;
-			argv = nargv;
-			cmd_flag |= DO_NOFUNC;
+			goto bail;
 		}
 	}
 
@@ -864,6 +882,7 @@  bail:
 			FORCEINTON;
 		}
 		listsetvar(varlist.list, VEXPORT|VSTACK);
+		path = unlikely(path) ? path : pathval();
 		shellexec(argv, path, cmdentry.u.index);
 		/* NOTREACHED */
 
diff --git a/src/exec.c b/src/exec.c
index 6c0a64f..9d0215a 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -357,11 +357,8 @@  find_command(char *name, struct cmdentry *entry, int act, const char *path)
 	}
 
 	updatetbl = (path == pathval());
-	if (!updatetbl) {
+	if (!updatetbl)
 		act |= DO_ALTPATH;
-		if (strstr(path, "%builtin") != NULL)
-			act |= DO_ALTBLTIN;
-	}
 
 	/* If name is in the table, check answer will be ok */
 	if ((cmdp = cmdlookup(name, 0)) != NULL) {
@@ -373,17 +370,20 @@  find_command(char *name, struct cmdentry *entry, int act, const char *path)
 			abort();
 #endif
 		case CMDNORMAL:
-			bit = DO_ALTPATH;
+			bit = DO_ALTPATH | DO_REGBLTIN;
 			break;
 		case CMDFUNCTION:
 			bit = DO_NOFUNC;
 			break;
 		case CMDBUILTIN:
 			bit = cmdp->param.cmd->flags & BUILTIN_REGULAR ?
-			      0 : DO_ALTBLTIN;
+			      0 : DO_REGBLTIN;
 			break;
 		}
 		if (act & bit) {
+			if (act & bit & DO_REGBLTIN)
+				goto fail;
+
 			updatetbl = 0;
 			cmdp = NULL;
 		} else if (cmdp->rehash == 0)
@@ -393,11 +393,13 @@  find_command(char *name, struct cmdentry *entry, int act, const char *path)
 
 	/* If %builtin not in path, check for builtin next */
 	bcmd = find_builtin(name);
-	if (bcmd && (bcmd->flags & BUILTIN_REGULAR || (
-		act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
-	)))
+	if (bcmd && ((bcmd->flags & BUILTIN_REGULAR) | (act & DO_ALTPATH) |
+		     (builtinloc <= 0)))
 		goto builtin_success;
 
+	if (act & DO_REGBLTIN)
+		goto fail;
+
 	/* We have to search path. */
 	prev = -1;		/* where to start */
 	if (cmdp && cmdp->rehash) {	/* doing a rehash */
@@ -489,6 +491,7 @@  loop:
 		delete_cmd_entry();
 	if (act & DO_ERR)
 		sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
+fail:
 	entry->cmdtype = CMDUNKNOWN;
 	return;
 
diff --git a/src/exec.h b/src/exec.h
index f394f3f..2b31825 100644
--- a/src/exec.h
+++ b/src/exec.h
@@ -56,7 +56,7 @@  struct cmdentry {
 #define DO_ABS		0x02	/* checks absolute paths */
 #define DO_NOFUNC	0x04	/* don't return shell functions, for command */
 #define DO_ALTPATH	0x08	/* using alternate path */
-#define DO_ALTBLTIN	0x20	/* %builtin in alt. path */
+#define DO_REGBLTIN	0x10	/* regular built-ins and functions only */
 
 extern const char *pathopt;	/* set by padvance */
 
diff --git a/src/parser.c b/src/parser.c
index 3de977c..c4e6378 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -125,8 +125,7 @@  STATIC void synerror(const char *) __attribute__((__noreturn__));
 STATIC void setprompt(int);
 
 
-static inline int
-isassignment(const char *p)
+int isassignment(const char *p)
 {
 	const char *q = endofname(p);
 	if (p == q)
diff --git a/src/parser.h b/src/parser.h
index 2875cce..524ac1c 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -82,6 +82,7 @@  extern int whichprompt;		/* 1 == PS1, 2 == PS2 */
 extern int checkkwd;
 
 
+int isassignment(const char *p);
 union node *parsecmd(int);
 void fixredir(union node *, const char *, int);
 const char *getprompt(void *);