summary refs log tree commit diff
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2018-05-19 02:39:52 +0800
committerHerbert Xu <herbert@gondor.apana.org.au>2018-05-28 17:12:23 +0800
commitcbb71a836874d176809a34e22f6b6e4e3ba8c85b (patch)
tree7bfba4c4202e600b13d660d74b6d93cb914a7733
parentexec: Never rehash regular built-ins (diff)
downloaddash-cbb71a836874d176809a34e22f6b6e4e3ba8c85b.tar.gz
dash-cbb71a836874d176809a34e22f6b6e4e3ba8c85b.zip
eval: Add assignment built-in support again
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>
-rw-r--r--src/eval.c145
-rw-r--r--src/exec.c21
-rw-r--r--src/exec.h2
-rw-r--r--src/parser.c3
-rw-r--r--src/parser.h1
5 files changed, 97 insertions, 75 deletions
diff --git a/src/eval.c b/src/eval.c
index 1b803db..bdc3893 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 *);
ommits we're actually showing. We also teach the "diff" and "commit" UIs to respect the follow flag on URLs, causing the single-file version of these UIs to detect renames. This feature is needed only for commits that rename the path we're interested in. For commits before the file has been renamed (i.e. that appear later in the log list) we change the file path in the links from the log to point to the old name; this means that links to commits always limit by the path known to that commit. If we didn't do this we would need to walk down the log diff'ing every commit whenever we want to show a commit. The drawback is that the "Log" link in the top bar of such a page links to the log limited by the old name, so it will only show pre-rename commits. I consider this a reasonable trade-off since the "Back" button still works and the log matches the path displayed in the top bar. Since following renames requires running diff on every commit we consider, I've added a knob to the configuration file to globally enable/disable this feature. Note that we may consider a large number of commits the revision walking machinery no longer performs any path limitation so we have to examine every commit until we find a page full of commits that affect the target path or something related to it. Suggested-by: René Neumann <necoro@necoro.eu> Signed-off-by: John Keeping <john@keeping.me.uk> 2015-08-12shared: make cgit_diff_tree_cb publicJohn Keeping This will allow us to use this nice wrapper function elsewhere, avoiding dealing with the diff queue when we only need to inspect a filepair. Signed-off-by: John Keeping <john@keeping.me.uk> 2015-08-12t0110: Chain together using &&Jason A. Donenfeld 2015-08-12about: always ensure page has a trailing slashJason A. Donenfeld Otherwise we can't easily embed links to other /about/ pages. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> 2015-08-12filters: apply HTML escapingLazaros Koromilas http://www.w3.org/International/questions/qa-escapes#use 2015-08-12git: update to v2.5.0Christian Hesse Update to git version v2.5.0. * Upstream commit 5455ee0573a22bb793a7083d593ae1ace909cd4c (Merge branch 'bc/object-id') changed API: for_each_ref() callback functions were taught to name the objects not with "unsigned char sha1[20]" but with "struct object_id". * Upstream commit dcf692625ac569fefbe52269061230f4fde10e47 (path.c: make get_pathname() call sites return const char *) Signed-off-by: Christian Hesse <mail@eworm.de> 2015-08-12Fix processing of repo.hide and repo.ignoreDaniel Reichelt