about summary refs log tree commit diff
path: root/Src/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c141
1 files changed, 106 insertions, 35 deletions
diff --git a/Src/exec.c b/Src/exec.c
index f021a0805..978a32d20 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2642,6 +2642,27 @@ execcmd_analyse(Estate state, Execcmd_params eparams)
 }
 
 /*
+ * Transfer the first node of args to preargs, performing
+ * prefork expansion on the way if necessary.
+ */
+static void execcmd_getargs(LinkList preargs, LinkList args, int expand)
+{
+    if (!firstnode(args)) {
+	return;
+    } else if (expand) {
+	local_list0(svl);
+	init_list0(svl);
+	/* not init_list1, as we need real nodes */
+	addlinknode(&svl, uremnode(args, firstnode(args)));
+	/* Analysing commands, so vanilla options to prefork */
+	prefork(&svl, 0, NULL);
+	joinlists(preargs, &svl);
+    } else {
+        addlinknode(preargs, uremnode(args, firstnode(args)));
+    }
+}
+
+/*
  * Execute a command at the lowest level of the hierarchy.
  */
 
@@ -2671,6 +2692,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
     LinkList redir = eparams->redir;
     Wordcode varspc = eparams->varspc;
     int type = eparams->type;
+    /*
+     * preargs comes from expanding the head of the args list
+     * in order to check for prefix commands.
+     */
+    LinkList preargs;
 
     doneps4 = 0;
 
@@ -2725,9 +2751,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
      * command if it contains some tokens (e.g. x=ex; ${x}port), so this *
      * only works in simple cases.  has_token() is called to make sure   *
      * this really is a simple case.                                     */
-    if (type == WC_SIMPLE || type == WC_TYPESET) {
-	while (args && nonempty(args)) {
-	    char *cmdarg = (char *) peekfirst(args);
+    if ((type == WC_SIMPLE || type == WC_TYPESET) && args) {
+	/*
+	 * preargs contains args that have been expanded by prefork.
+	 * Running execcmd_getargs() causes the any argument available
+	 * in args to be exanded where necessary and transferred to
+	 * preargs.  We call execcmd_getargs() every time we need to
+	 * analyse an argument not available in preargs, though there is
+	 * no guarantee a further argument will be available.
+	 */
+	preargs = newlinklist();
+	execcmd_getargs(preargs, args, eparams->htok);
+	while (nonempty(preargs)) {
+	    char *cmdarg = (char *) peekfirst(preargs);
 	    checked = !has_token(cmdarg);
 	    if (!checked)
 		break;
@@ -2766,7 +2802,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		break;
 	    }
 	    checked = 0;
-	    if ((cflags & BINF_COMMAND) && nextnode(firstnode(args))) {
+	    /*
+	     * We usually don't need the argument containing the
+	     * precommand modifier itself.  Exception: when "command"
+	     * will implemented by a call to "whence", in which case
+	     * we'll simply re-insert the argument.
+	     */
+	    uremnode(preargs, firstnode(preargs));
+	    if (!firstnode(preargs)) {
+		execcmd_getargs(preargs, args, eparams->htok);
+		if (!firstnode(preargs))
+		    break;
+	    }
+	    if ((cflags & BINF_COMMAND)) {
 		/*
 		 * Check for options to "command".
 		 * If just -p, this is handled here: use the default
@@ -2776,10 +2824,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		 * Otherwise, just leave marked as BINF_COMMAND
 		 * modifier with no additional action.
 		 */
-		LinkNode argnode = nextnode(firstnode(args));
-		char *argdata = (char *) getdata(argnode);
-		char *cmdopt;
+		LinkNode argnode, oldnode;
+		char *argdata, *cmdopt;
 		int has_p = 0, has_vV = 0, has_other = 0;
+		argnode = firstnode(preargs);
+		argdata = (char *) getdata(argnode);
 		while (IS_DASH(*argdata)) {
 		    /* Just to be definite, stop on single "-", too, */
 		    if (!argdata[1] ||
@@ -2812,25 +2861,30 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			break;
 		    }
 
+		    oldnode = argnode;
 		    argnode = nextnode(argnode);
-		    if (!argnode)
-			break;
+		    if (!argnode) {
+			execcmd_getargs(preargs, args, eparams->htok);
+			if (!(argnode = nextnode(oldnode)))
+			    break;
+		    }
 		    argdata = (char *) getdata(argnode);
 		}
 		if (has_vV) {
-		    /* Leave everything alone, dispatch to whence */
+		    /*
+		     * Leave everything alone, dispatch to whence.
+		     * We need to put the name back in the list.
+		     */
+		    pushnode(preargs, "command");
 		    hn = &commandbn.node;
 		    is_builtin = 1;
 		    break;
 		} else if (has_p) {
-		    /* Use default path; absorb command and option. */
-		    uremnode(args, firstnode(args));
+		    /* Use default path */
 		    use_defpath = 1;
-		    if ((argnode = nextnode(firstnode(args))))
-			argdata = (char *) getdata(argnode);
 		}
 		/*
-		 * Else just absorb command and any trailing
+		 * Else just any trailing
 		 * end-of-options marker.  This can only occur
 		 * if we just had -p or something including more
 		 * than just -p, -v and -V, in which case we behave
@@ -2838,16 +2892,16 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		 * isn't a good place for standard option handling.
 		 */
 		if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
-		     uremnode(args, firstnode(args));
-	    }
-	    if ((cflags & BINF_EXEC) && nextnode(firstnode(args))) {
+		     uremnode(preargs, argnode);
+	    } else if (cflags & BINF_EXEC) {
 		/*
 		 * Check for compatibility options to exec builtin.
 		 * It would be nice to do these more generically,
 		 * but currently we don't have a mechanism for
 		 * precommand modifiers.
 		 */
-		char *next = (char *) getdata(nextnode(firstnode(args)));
+		LinkNode argnode = firstnode(preargs), oldnode;
+		char *argdata = (char *) getdata(argnode);
 		char *cmdopt, *exec_argv0 = NULL;
 		/*
 		 * Careful here: we want to make sure a final dash
@@ -2857,17 +2911,23 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		 * people aren't likely to mix the option style
 		 * with the zsh style.
 		 */
-		while (next && IS_DASH(*next) && strlen(next) >= 2) {
-		    if (!firstnode(args)) {
+		while (argdata && IS_DASH(*argdata) && strlen(argdata) >= 2) {
+		    oldnode = argnode;
+		    argnode = nextnode(oldnode);
+		    if (!argnode) {
+			execcmd_getargs(preargs, args, eparams->htok);
+			argnode = nextnode(oldnode);
+		    }
+		    if (!argnode) {
 			zerr("exec requires a command to execute");
 			lastval = 1;
 			errflag |= ERRFLAG_ERROR;
 			goto done;
 		    }
-		    uremnode(args, firstnode(args));
-		    if (IS_DASH(next[0]) && IS_DASH(next[1]) && !next[2])
+		    uremnode(preargs, oldnode);
+		    if (IS_DASH(argdata[0]) && IS_DASH(argdata[1]) && !argdata[2])
 			break;
-		    for (cmdopt = &next[1]; *cmdopt; ++cmdopt) {
+		    for (cmdopt = &argdata[1]; *cmdopt; ++cmdopt) {
 			switch (*cmdopt) {
 			case 'a':
 			    /* argument is ARGV0 string */
@@ -2876,21 +2936,25 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 				/* position on last non-NULL character */
 				cmdopt += strlen(cmdopt+1);
 			    } else {
-				if (!firstnode(args)) {
+				if (!argnode) {
 				    zerr("exec requires a command to execute");
 				    lastval = 1;
 				    errflag |= ERRFLAG_ERROR;
 				    goto done;
 				}
-				if (!nextnode(firstnode(args))) {
+				if (!nextnode(argnode))
+				    execcmd_getargs(preargs, args,
+						    eparams->htok);
+				if (!nextnode(argnode)) {
 				    zerr("exec flag -a requires a parameter");
 				    lastval = 1;
 				    errflag |= ERRFLAG_ERROR;
 				    goto done;
 				}
-				exec_argv0 = (char *)
-				    getdata(nextnode(firstnode(args)));
-				uremnode(args, firstnode(args));
+				exec_argv0 = (char *) getdata(argnode);
+				oldnode = argnode;
+				argnode = nextnode(argnode);
+				uremnode(args, oldnode);
 			    }
 			    break;
 			case 'c':
@@ -2906,8 +2970,9 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			    return;
 			}
 		    }
-		    if (firstnode(args) && nextnode(firstnode(args)))
-			next = (char *) getdata(nextnode(firstnode(args)));
+		    if (!argnode)
+			break;
+		    argdata = (char *) getdata(argnode);
 		}
 		if (exec_argv0) {
 		    char *str, *s;
@@ -2919,12 +2984,14 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		    zputenv(str);
 		}
 	    }
-	    uremnode(args, firstnode(args));
 	    hn = NULL;
 	    if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
 		break;
+	    if (!nonempty(preargs))
+		execcmd_getargs(preargs, args, eparams->htok);
 	}
-    }
+    } else
+	preargs = NULL;
 
     /* if we get this far, it is OK to pay attention to lastval again */
     if (noerrexit == 2 && !is_shfunc)
@@ -2946,8 +3013,12 @@ execcmd_exec(Estate state, Execcmd_params eparams,
     esprefork = (magic_assign ||
 		 (isset(MAGICEQUALSUBST) && type != WC_TYPESET)) ?
 		 PREFORK_TYPESET : 0;
-    if (args && eparams->htok)
-	prefork(args, esprefork, NULL);
+    if (args) {
+	if (eparams->htok)
+	    prefork(args, esprefork, NULL);
+	if (preargs)
+	    args = joinlists(preargs, args);
+    }
 
     if (type == WC_SIMPLE || type == WC_TYPESET) {
 	int unglobbed = 0;