summaryrefslogtreecommitdiff
path: root/Src/exec.c
diff options
context:
space:
mode:
authorAxel Beckert <abe@deuxchevaux.org>2015-05-05 23:32:59 +0200
committerAxel Beckert <abe@deuxchevaux.org>2015-05-05 23:58:59 +0200
commitdb38e167634b6c2217eec3a5aafc37c46d9e5a8d (patch)
treedaa342d423febbd3a5a7ef97053037677fab004a /Src/exec.c
parent01eea47617a6e06debdb4330f92ae69f92089fd2 (diff)
parent3c3c8d3d13fd4cf6c03f81ca8dc18a1efd561728 (diff)
downloadzsh-db38e167634b6c2217eec3a5aafc37c46d9e5a8d.tar.gz
zsh-db38e167634b6c2217eec3a5aafc37c46d9e5a8d.zip
Merge branch 'upstream' into debian
Diffstat (limited to 'Src/exec.c')
-rw-r--r--Src/exec.c239
1 files changed, 177 insertions, 62 deletions
diff --git a/Src/exec.c b/Src/exec.c
index d0fadd69a..6a8b35a36 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -59,7 +59,7 @@ mod_export int noerrs;
/**/
int nohistsave;
-/* error/break flag */
+/* error flag: bits from enum errflag_bits */
/**/
mod_export int errflag;
@@ -217,7 +217,7 @@ parse_string(char *s, int reset_lineno)
Eprog p;
zlong oldlineno;
- lexsave();
+ zcontext_save();
inpush(s, INP_LINENO, NULL);
strinbeg(0);
oldlineno = lineno;
@@ -229,7 +229,7 @@ parse_string(char *s, int reset_lineno)
lastval = 1;
strinend();
inpop();
- lexrestore();
+ zcontext_restore();
return p;
}
@@ -250,6 +250,7 @@ zsetlimit(int limnum, char *nam)
if (setrlimit(limnum, limits + limnum)) {
if (nam)
zwarnnam(nam, "setrlimit failed: %e", errno);
+ limits[limnum] = current_limits[limnum];
return -1;
}
current_limits[limnum] = limits[limnum];
@@ -1145,7 +1146,8 @@ execlist(Estate state, int dont_change_job, int exiting)
Wordcode next;
wordcode code;
int ret, cj, csp, ltype;
- int old_pline_level, old_list_pipe;
+ int old_pline_level, old_list_pipe, old_list_pipe_job;
+ char *old_list_pipe_text;
zlong oldlineno;
/*
* ERREXIT only forces the shell to exit if the last command in a &&
@@ -1158,10 +1160,17 @@ execlist(Estate state, int dont_change_job, int exiting)
cj = thisjob;
old_pline_level = pline_level;
old_list_pipe = list_pipe;
+ old_list_pipe_job = list_pipe_job;
+ if (*list_pipe_text)
+ old_list_pipe_text = ztrdup(list_pipe_text);
+ else
+ old_list_pipe_text = NULL;
oldlineno = lineno;
- if (sourcelevel && unset(SHINSTDIN))
- pline_level = list_pipe = 0;
+ if (sourcelevel && unset(SHINSTDIN)) {
+ pline_level = list_pipe = list_pipe_job = 0;
+ *list_pipe_text = '\0';
+ }
/* Loop over all sets of comands separated by newline, *
* semi-colon or ampersand (`sublists'). */
@@ -1396,6 +1405,13 @@ sublist_done:
}
pline_level = old_pline_level;
list_pipe = old_list_pipe;
+ list_pipe_job = old_list_pipe_job;
+ if (old_list_pipe_text) {
+ strcpy(list_pipe_text, old_list_pipe_text);
+ zsfree(old_list_pipe_text);
+ } else {
+ *list_pipe_text = '\0';
+ }
lineno = oldlineno;
if (dont_change_job)
thisjob = cj;
@@ -1600,7 +1616,8 @@ execpline(Estate state, wordcode slcode, int how, int last1)
(killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1);
list_pipe_pid = pid;
list_pipe_start = bgtime;
- nowait = errflag = 1;
+ nowait = 1;
+ errflag |= ERRFLAG_ERROR;
breaks = loops;
close(synch[1]);
read_loop(synch[0], &dummy, 1);
@@ -1633,7 +1650,10 @@ execpline(Estate state, wordcode slcode, int how, int last1)
list_pipe_child = 1;
opts[INTERACTIVE] = 0;
if (errbrk_saved) {
- errflag = prev_errflag;
+ /*
+ * Keep any user interrupt bit in errflag.
+ */
+ errflag = prev_errflag | (errflag & ERRFLAG_INT);
breaks = prev_breaks;
}
break;
@@ -1718,12 +1738,14 @@ execpline2(Estate state, wordcode pcode,
if (pipe(synch) < 0) {
zerr("pipe failed: %e", errno);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (pid) {
char dummy, *text;
@@ -2242,8 +2264,16 @@ addvars(Estate state, Wordcode pc, int addflags)
state->pc = opc;
return;
}
- if (isset(GLOBASSIGN) || !isstr)
+ if (!isstr || (isset(GLOBASSIGN) &&
+ haswilds((char *)getdata(firstnode(vl))))) {
globlist(vl, 0);
+ /* Unset the parameter to force it to be recreated
+ * as either scalar or array depending on how many
+ * matches were found for the glob.
+ */
+ if (isset(GLOBASSIGN))
+ unsetparam(name);
+ }
if (errflag) {
state->pc = opc;
return;
@@ -2279,6 +2309,8 @@ addvars(Estate state, Wordcode pc, int addflags)
}
allexp = opts[ALLEXPORT];
opts[ALLEXPORT] = 1;
+ if (isset(KSHARRAYS))
+ unsetparam(name);
pm = assignsparam(name, val, myflags);
opts[ALLEXPORT] = allexp;
} else
@@ -2290,13 +2322,13 @@ addvars(Estate state, Wordcode pc, int addflags)
continue;
}
if (vl) {
- ptr = arr = (char **) zalloc(sizeof(char **) *
+ ptr = arr = (char **) zalloc(sizeof(char *) *
(countlinknodes(vl) + 1));
while (nonempty(vl))
*ptr++ = ztrdup((char *) ugetnode(vl));
} else
- ptr = arr = (char **) zalloc(sizeof(char **));
+ ptr = arr = (char **) zalloc(sizeof(char *));
*ptr = NULL;
if (xtr) {
@@ -2320,6 +2352,7 @@ addvars(Estate state, Wordcode pc, int addflags)
void
setunderscore(char *str)
{
+ queue_signals();
if (str && *str) {
int l = strlen(str) + 1, nl = (l + 31) & ~31;
@@ -2337,6 +2370,7 @@ setunderscore(char *str)
*zunderscore = '\0';
underscoreused = 1;
}
+ unqueue_signals();
}
/* These describe the type of expansions that need to be done on the words
@@ -2551,7 +2585,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
while (next && *next == '-' && strlen(next) >= 2) {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
uremnode(args, firstnode(args));
@@ -2568,12 +2603,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else {
if (!firstnode(args)) {
zerr("exec requires a command to execute");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
if (!nextnode(firstnode(args))) {
zerr("exec flag -a requires a parameter");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto done;
}
exec_argv0 = (char *)
@@ -2589,7 +2626,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
break;
default:
zerr("unknown exec flag -%c", *cmdopt);
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
}
}
@@ -2613,6 +2651,10 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
}
+ /* if we get this far, it is OK to pay attention to lastval again */
+ if (noerrexit == 2 && !is_shfunc)
+ noerrexit = 0;
+
/* Do prefork substitutions */
esprefork = (assign || isset(MAGICEQUALSUBST)) ? PREFORK_TYPESET : 0;
if (args && htok)
@@ -2652,7 +2694,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] ||
(cflags & BINF_PREFIX)) {
zerr("redirection with no command");
- errflag = lastval = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
return;
} else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
if (!args)
@@ -2682,7 +2725,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
if (varspc)
addvars(state, varspc, 0);
if (errflag)
- lastval = errflag;
+ lastval = 1;
else
lastval = cmdoutval;
if (isset(XTRACE)) {
@@ -2758,9 +2801,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
else
text = NULL;
- /* Set up special parameter $_ */
-
- setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : "");
+ /*
+ * Set up special parameter $_
+ * For execfuncdef we may need to take account of an
+ * anonymous function with arguments.
+ */
+ if (type != WC_FUNCDEF)
+ setunderscore((args && nonempty(args)) ?
+ ((char *) getdata(lastnode(args))) : "");
/* Warn about "rm *" */
if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) &&
@@ -2786,7 +2834,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
}
if (!nextnode(firstnode(args)))
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
if (type == WC_FUNCDEF) {
@@ -2882,6 +2930,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
* is a directory we should AUTOCD to. */
if (!hn && trycd && (s = cancd(peekfirst(args)))) {
peekfirst(args) = (void *) s;
+ pushnode(args, dupstring("--"));
pushnode(args, dupstring("cd"));
if ((hn = builtintab->getnode(builtintab, "cd")))
is_builtin = 1;
@@ -2931,7 +2980,8 @@ execcmd(Estate state, int input, int output, int how, int last1)
} else if ((pid = zfork(&bgtime)) == -1) {
close(synch[0]);
close(synch[1]);
- lastval = errflag = 1;
+ lastval = 1;
+ errflag |= ERRFLAG_ERROR;
goto fatal;
}
if (pid) {
@@ -2941,8 +2991,6 @@ execcmd(Estate state, int input, int output, int how, int last1)
close(synch[0]);
if (how & Z_ASYNC) {
lastpid = (zlong) pid;
- /* indicate it's possible to set status for lastpid */
- lastpid_status = -2L;
} else if (!jobtab[thisjob].stty_in_env && varspc) {
/* search for STTY=... */
Wordcode p = varspc;
@@ -2996,6 +3044,15 @@ execcmd(Estate state, int input, int output, int how, int last1)
* Note that any form of exec means that the subshell is fake *
* (but we may be in a subshell already). */
is_exec = 1;
+ /*
+ * If we are in a subshell environment anyway, say we're forked,
+ * even if we're actually not forked because we know the
+ * subshell is exiting. This ensures SHLVL reflects the current
+ * shell, and also optimises out any save/restore we'd need to
+ * do if we were returning to the main shell.
+ */
+ if (type == WC_SUBSH)
+ forked = 1;
}
if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) {
@@ -3169,7 +3226,12 @@ execcmd(Estate state, int input, int output, int how, int last1)
}
if (fn->fd1 < 10)
closemn(mfds, fn->fd1, REDIR_CLOSE);
- if (!closed && zclose(fn->fd1) < 0) {
+ /*
+ * Only report failures to close file descriptors
+ * if they're under user control as we don't know
+ * what the previous status of others was.
+ */
+ if (!closed && zclose(fn->fd1) < 0 && fn->varid) {
zwarn("failed to close file descriptor %d: %e",
fn->fd1, errno);
}
@@ -3259,6 +3321,20 @@ execcmd(Estate state, int input, int output, int how, int last1)
closemn(mfds, i, REDIR_CLOSE);
if (nullexec) {
+ /*
+ * If nullexec is 2, we have variables to add with the redirections
+ * in place. If nullexec is 1, we may have variables but they
+ * need the standard restore logic.
+ */
+ if (varspc) {
+ LinkList restorelist = 0, removelist = 0;
+ if (!isset(POSIXBUILTINS) && nullexec != 2)
+ save_params(state, varspc, &restorelist, &removelist);
+ addvars(state, varspc, 0);
+ if (restorelist)
+ restore_params(restorelist, removelist);
+ }
+ lastval = errflag ? errflag : cmdoutval;
if (nullexec == 1) {
/*
* If nullexec is 1 we specifically *don't* restore the original
@@ -3269,13 +3345,6 @@ execcmd(Estate state, int input, int output, int how, int last1)
zclose(save[i]);
goto done;
}
- /*
- * If nullexec is 2, we have variables to add with the redirections
- * in place.
- */
- if (varspc)
- addvars(state, varspc, 0);
- lastval = errflag ? errflag : cmdoutval;
if (isset(XTRACE)) {
fputc('\n', xtrerr);
fflush(xtrerr);
@@ -3315,14 +3384,14 @@ execcmd(Estate state, int input, int output, int how, int last1)
* The copy uses the wordcode parsing area, so save and
* restore state.
*/
- lexsave();
+ zcontext_save();
redir_prog = eccopyredirs(&s);
- lexrestore();
+ zcontext_restore();
} else
redir_prog = NULL;
-
+
lastval = execfuncdef(state, redir_prog);
- }
+ }
else if (type >= WC_CURSH) {
if (last1 == 1)
do_exec = 1;
@@ -3338,14 +3407,28 @@ execcmd(Estate state, int input, int output, int how, int last1)
LinkList restorelist = 0, removelist = 0;
/* builtin or shell function */
- if (!forked && ((cflags & BINF_COMMAND) ||
- (unset(POSIXBUILTINS) && !assign) ||
- (isset(POSIXBUILTINS) && !is_shfunc &&
- !(hn->flags & BINF_PSPECIAL)))) {
- if (varspc)
+ if (!forked && varspc) {
+ int do_save = 0;
+ if (isset(POSIXBUILTINS)) {
+ /*
+ * If it's a function or special builtin --- save
+ * if it's got "command" in front.
+ * If it's a normal command --- save.
+ */
+ if (is_shfunc || (hn->flags & BINF_PSPECIAL))
+ do_save = (orig_cflags & BINF_COMMAND);
+ else
+ do_save = 1;
+ } else {
+ /*
+ * Save if it's got "command" in front or it's
+ * not a magic-equals assignment.
+ */
+ if ((cflags & BINF_COMMAND) || !assign)
+ do_save = 1;
+ }
+ if (do_save)
save_params(state, varspc, &restorelist, &removelist);
- else
- restorelist = removelist = NULL;
}
if (varspc) {
/* Export this if the command is a shell function,
@@ -3513,7 +3596,7 @@ execcmd(Estate state, int input, int output, int how, int last1)
else
exit(1);
}
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
}
}
if (newxtrerr) {
@@ -3738,17 +3821,20 @@ gethere(char **strp, int typ)
*bptr++ = '\n';
}
*t = '\0';
+ s = buf;
+ buf = dupstring(buf);
+ zfree(s, bsiz);
if (!qt) {
int ef = errflag;
- parsestr(buf);
+ parsestr(&buf);
- if (!errflag)
- errflag = ef;
+ if (!errflag) {
+ /* Retain any user interrupt error */
+ errflag = ef | (errflag & ERRFLAG_INT);
+ }
}
- s = dupstring(buf);
- zfree(buf, bsiz);
- return s;
+ return buf;
}
/* open here string fd */
@@ -3832,13 +3918,13 @@ getoutput(char *cmd, int qt)
return NULL;
untokenize(s);
if ((stream = open(unmeta(s), O_RDONLY | O_NOCTTY)) == -1) {
- zerr("%e: %s", errno, s);
- return NULL;
+ zwarn("%e: %s", errno, s);
+ return newlinklist();
}
return readoutput(stream, qt);
}
if (mpipe(pipes) < 0) {
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
return NULL;
}
@@ -3848,7 +3934,7 @@ getoutput(char *cmd, int qt)
/* fork error */
zclose(pipes[0]);
zclose(pipes[1]);
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdoutpid = 0;
child_unblock();
return NULL;
@@ -4258,7 +4344,7 @@ execcond(Estate state, UNUSED(int do_exec))
* into a shell error.
*/
if (stat == 2)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
cmdpop();
if (isset(XTRACE)) {
fprintf(xtrerr, " ]]\n");
@@ -4298,7 +4384,7 @@ execarith(Estate state, UNUSED(int do_exec))
fflush(xtrerr);
}
if (errflag) {
- errflag = 0;
+ errflag &= ~ERRFLAG_ERROR;
return 2;
}
/* should test for fabs(val.u.d) < epsilon? */
@@ -4332,7 +4418,7 @@ execfuncdef(Estate state, Eprog redir_prog)
Shfunc shf;
char *s = NULL;
int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0;
- int nfunc = 0;
+ int nfunc = 0, anon_func = 0;
Wordcode beg = state->pc, end;
Eprog prog;
Patprog *pp;
@@ -4414,10 +4500,12 @@ execfuncdef(Estate state, Eprog redir_prog)
if (!names) {
/*
* Anonymous function, execute immediately.
- * Function name is "(anon)", parameter list is empty.
+ * Function name is "(anon)".
*/
LinkList args;
+ anon_func = 1;
+
state->pc = end;
end += *state->pc++;
args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
@@ -4425,11 +4513,19 @@ execfuncdef(Estate state, Eprog redir_prog)
if (htok && args) {
execsubst(args);
if (errflag) {
+ freeeprog(shf->funcdef);
+ if (shf->redir) /* shouldn't be */
+ freeeprog(shf->redir);
+ zsfree(shf->filename);
+ zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
}
}
+ setunderscore((args && nonempty(args)) ?
+ ((char *) getdata(lastnode(args))) : "");
+
if (!args)
args = newlinklist();
shf->node.nam = "(anon)";
@@ -4438,6 +4534,16 @@ execfuncdef(Estate state, Eprog redir_prog)
execshfunc(shf, args);
ret = lastval;
+ if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
+ lastval) {
+#if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD)
+ fprintf(stderr, "zsh: exit %lld\n", lastval);
+#else
+ fprintf(stderr, "zsh: exit %ld\n", (long)lastval);
+#endif
+ fflush(stderr);
+ }
+
freeeprog(shf->funcdef);
if (shf->redir) /* shouldn't be */
freeeprog(shf->redir);
@@ -4450,6 +4556,7 @@ execfuncdef(Estate state, Eprog redir_prog)
(signum = getsignum(s + 4)) != -1) {
if (settrap(signum, NULL, ZSIG_FUNC)) {
freeeprog(shf->funcdef);
+ zsfree(shf->filename);
zfree(shf, sizeof(*shf));
state->pc = end;
return 1;
@@ -4464,6 +4571,8 @@ execfuncdef(Estate state, Eprog redir_prog)
shfunctab->addnode(shfunctab, ztrdup(s), shf);
}
}
+ if (!anon_func)
+ setunderscore("");
if (!nfunc && redir_prog) {
/* For completeness, shouldn't happen */
freeeprog(redir_prog);
@@ -4547,6 +4656,7 @@ execshfunc(Shfunc shf, LinkList args)
fputc('\n', xtrerr);
fflush(xtrerr);
}
+ queue_signals();
ocs = cmdstack;
ocsp = cmdsp;
cmdstack = (unsigned char *) zalloc(CMDSTACKSZ);
@@ -4554,7 +4664,11 @@ execshfunc(Shfunc shf, LinkList args)
if ((osfc = sfcontext) == SFC_NONE)
sfcontext = SFC_DIRECT;
xtrerr = stderr;
+ unqueue_signals();
+
doshfunc(shf, args, 0);
+
+ queue_signals();
sfcontext = osfc;
free(cmdstack);
cmdstack = ocs;
@@ -4562,6 +4676,7 @@ execshfunc(Shfunc shf, LinkList args)
if (!list_pipe)
deletefilelist(last_file_list, 0);
+ unqueue_signals();
}
/*
@@ -4910,7 +5025,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
(name = fname)))) {
zwarn("%s: function not defined by file", name);
if (noreturnval)
- errflag = 1;
+ errflag |= ERRFLAG_ERROR;
else
lastval = 1;
goto doneshfunc;
@@ -5243,7 +5358,7 @@ execsave(void)
{
struct execstack *es;
- es = (struct execstack *) malloc(sizeof(struct execstack));
+ es = (struct execstack *) zalloc(sizeof(struct execstack));
es->list_pipe_pid = list_pipe_pid;
es->nowait = nowait;
es->pline_level = pline_level;