From 2d4743ce50ba0ae50c0cfa1404a163dbd61ad446 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Wed, 22 Oct 2014 08:42:37 -0700 Subject: 33493: use correct command name in error messages about "ulimit" failure; restore internal copy of limits if setrlimit() fails, so the error won't repeat --- Src/exec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'Src/exec.c') diff --git a/Src/exec.c b/Src/exec.c index d0fadd69a..2f896d8d5 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -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]; -- cgit v1.2.3 From b4f7ccecd93ca9e64c3c3c774fdaefae83d7204a Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 26 Oct 2014 17:47:42 +0000 Subject: 33531 with additions: retain status of exited background jobs. Add linked list of unwaited-for background jobs. Truncate at value of _SC_CHILD_MAX discarding oldest. Remove old lastpid_status mechanism for latest exited process only. Slightly tighten safety of permanently allocated linked lists so that this doesn't compromise signal handling. --- ChangeLog | 9 ++++ Doc/Zsh/builtins.yo | 16 ++++++ Doc/Zsh/options.yo | 8 +-- Src/exec.c | 2 - Src/init.c | 1 - Src/jobs.c | 138 ++++++++++++++++++++++++++++++++++++++++++++-------- Src/linklist.c | 4 ++ Src/signals.c | 14 +++--- 8 files changed, 157 insertions(+), 35 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 48efd1c0d..c75b870de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2014-10-26 Peter Stephenson + + * 33531 (plus fix to test job pointer and removing + lastpid_status): Doc/Zsh/builtins.yo, Doc/Zsh/options.yo, + Src/exec.c, Src/init.c, Src/jobs.c, Src/linklist.c, + Src/signals.c: retain up to CHILD_MAX statuses of exited + background processes; remove old lastpid_status mechanism; + slightly improve safety of permanently allocated linked lists. + 2014-10-24 Barton E. Schaefer * 33526: Completion/Unix/Type/_path_files: fix path prefix diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 46f40cc3a..edc335e8b 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2059,6 +2059,22 @@ then all currently active child processes are waited for. Each var(job) can be either a job specification or the process ID of a job in the job table. The exit status from this command is that of the job waited for. + +It is possible to wait for recent processes (specified by process ID, +not by job) that were running in the background even if the process has +exited. Typically the process ID will be recorded by capturing the +value of the variable tt($!) immediately after the process has been +started. There is a limit on the number of process IDs remembered by +the shell; this is given by the value of the system configuration +parameter tt(CHILD_MAX). When this limit is reached, older process IDs +are discarded, least recently started processes first. + +Note there is no protection against the process ID wrapping, i.e. if the +wait is not executed soon enough there is a chance the process waited +for is the wrong one. A conflict implies both process IDs have been +generated by the shell, as other processes are not recorded, and that +the user is potentially interested in both, so this problem is intrinsic +to process IDs. ) findex(whence) item(tt(whence) [ tt(-vcwfpams) ] var(name) ...)( diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 068a253ac..452b258b4 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -1434,10 +1434,10 @@ shell is saved for output within a subshell (for example, within a pipeline). When the option is set, the output of tt(jobs) is empty until a job is started within the subshell. -When the option is set, it becomes possible to use the tt(wait) builtin to -wait for the last job started in the background (as given by tt($!)) even -if that job has already exited. This works even if the option is turned -on temporarily around the use of the tt(wait) builtin. +In previous versions of the shell, it was necessary to enable +tt(POSIX_JOBS) in order for the builtin command tt(wait) to return the +status of background jobs that had already exited. This is no longer +the case. ) enditem() diff --git a/Src/exec.c b/Src/exec.c index 2f896d8d5..5bbd4e15d 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2942,8 +2942,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; diff --git a/Src/init.c b/Src/init.c index 68d36125e..655166135 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1049,7 +1049,6 @@ setupvals(void) bufstack = znewlinklist(); hsubl = hsubr = NULL; lastpid = 0; - lastpid_status = -1L; get_usage(); diff --git a/Src/jobs.c b/Src/jobs.c index bd95afb7a..18bb648d9 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -104,15 +104,6 @@ int prev_errflag, prev_breaks, errbrk_saved; /**/ int numpipestats, pipestats[MAX_PIPESTATS]; -/* - * The status associated with the process lastpid. - * -1 if not set and no associated lastpid - * -2 if lastpid is set and status isn't yet - * else the value returned by wait(). - */ -/**/ -long lastpid_status; - /* Diff two timevals for elapsed-time computations */ /**/ @@ -1309,14 +1300,6 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime) { Process pn, *pnlist; - if (pid == lastpid && lastpid_status != -2L) { - /* - * The status for the previous lastpid is invalid. - * Presumably process numbers have wrapped. - */ - lastpid_status = -1L; - } - DPUTS(thisjob == -1, "No valid job in addproc."); pn = (Process) zshcalloc(sizeof *pn); pn->pid = pid; @@ -1940,6 +1923,122 @@ maybeshrinkjobtab(void) unqueue_signals(); } +/* + * Definitions for the background process stuff recorded below. + * This would be more efficient as a hash, but + * - that's quite heavyweight for something not needed very often + * - we need some kind of ordering as POSIX allows us to limit + * the size of the list to the value of _SC_CHILD_MAX and clearly + * we want to clear the oldest first + * - cases with a long list of background jobs where the user doesn't + * wait for a large number, and then does wait for one (the only + * inefficient case) are rare + * - in the context of waiting for an external process, looping + * over a list isn't so very inefficient. + * Enough excuses already. + */ + +/* Data in the link list, a key (process ID) / value (exit status) pair. */ +struct bgstatus { + pid_t pid; + int status; +}; +typedef struct bgstatus *Bgstatus; +/* The list of those entries */ +LinkList bgstatus_list; +/* Count of entries. Reaches value of _SC_CHILD_MAX and stops. */ +long bgstatus_count; + +/* + * Remove and free a bgstatus entry. + */ +static void rembgstatus(LinkNode node) +{ + zfree(remnode(bgstatus_list, node), sizeof(struct bgstatus)); + bgstatus_count--; +} + +/* + * Record the status of a background process that exited so we + * can execute the builtin wait for it. + * + * We can't execute the wait builtin for something that exited in the + * foreground as it's not visible to the user, so don't bother recording. + */ + +/**/ +void +addbgstatus(pid_t pid, int status) +{ + static long child_max; + Bgstatus bgstatus_entry; + + if (!child_max) { +#ifdef _SC_CHILD_MAX + child_max = sysconf(_SC_CHILD_MAX); + if (!child_max) /* paranoia */ +#endif + { + /* Be inventive */ + child_max = 1024L; + } + } + + if (!bgstatus_list) { + bgstatus_list = znewlinklist(); + /* + * We're not always robust about memory failures, but + * this is pretty deep in the shell basics to be failing owing + * to memory, and a failure to wait is reported loudly, so test + * and fail silently here. + */ + if (!bgstatus_list) + return; + } + if (bgstatus_count == child_max) { + /* Overflow. List is in order, remove first */ + rembgstatus(firstnode(bgstatus_list)); + } + bgstatus_entry = (Bgstatus)zalloc(sizeof(*bgstatus_entry)); + if (!bgstatus_entry) { + /* See note above */ + return; + } + bgstatus_entry->pid = pid; + bgstatus_entry->status = status; + if (!zaddlinknode(bgstatus_list, bgstatus_entry)) { + zfree(bgstatus_entry, sizeof(*bgstatus_entry)); + return; + } + bgstatus_count++; +} + +/* + * See if pid has a recorded exit status. + * Note we make no guarantee that the PIDs haven't wrapped, so this + * may not be the right process. + * + * This is only used by wait, which must only work on each + * pid once, so we need to remove the entry if we find it. + */ + +static int getbgstatus(pid_t pid) +{ + LinkNode node; + Bgstatus bgstatus_entry; + + if (!bgstatus_list) + return -1; + for (node = firstnode(bgstatus_list); node; incnode(node)) { + bgstatus_entry = (Bgstatus)getdata(node); + if (bgstatus_entry->pid == pid) { + int status = bgstatus_entry->status; + rembgstatus(node); + return status; + } + } + return -1; +} /* bg, disown, fg, jobs, wait: most of the job control commands are * * here. They all take the same type of argument. Exception: wait can * @@ -2085,10 +2184,7 @@ bin_fg(char *name, char **argv, Options ops, int func) } if (retval == 0) retval = lastval2; - } else if (isset(POSIXJOBS) && - pid == lastpid && lastpid_status >= 0L) { - retval = (int)lastpid_status; - } else { + } else if ((retval = getbgstatus(pid)) < 0) { zwarnnam(name, "pid %d is not a child of this shell", pid); /* presumably lastval2 doesn't tell us a heck of a lot? */ retval = 1; diff --git a/Src/linklist.c b/Src/linklist.c index 1e364fb4e..3aa8125d9 100644 --- a/Src/linklist.c +++ b/Src/linklist.c @@ -118,6 +118,8 @@ znewlinklist(void) LinkList list; list = (LinkList) zalloc(sizeof *list); + if (!list) + return NULL; list->list.first = NULL; list->list.last = &list->node; list->list.flags = 0; @@ -152,6 +154,8 @@ zinsertlinknode(LinkList list, LinkNode node, void *dat) tmp = node->next; node->next = new = (LinkNode) zalloc(sizeof *tmp); + if (!new) + return NULL; new->prev = node; new->dat = dat; new->next = tmp; diff --git a/Src/signals.c b/Src/signals.c index 2df69f96e..e72850516 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -522,14 +522,14 @@ wait_for_processes(void) get_usage(); } /* - * Remember the status associated with $!, so we can - * wait for it even if it's exited. This value is - * only used if we can't find the PID in the job table, - * so it doesn't matter that the value we save here isn't - * useful until the process has exited. + * Accumulate a list of older jobs. We only do this for + * background jobs, which is something in the job table + * that's not marked as in the current shell or as shell builtin + * and is not equal to the current foreground job. */ - if (pn != NULL && pid == lastpid && lastpid_status != -1L) - lastpid_status = lastval2; + if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && + jn - jobtab != thisjob) + addbgstatus(pid, (int)lastval2); } } -- cgit v1.2.3 From 023c2236e1280ad1539a2885c6479d0b89d6dc46 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 2 Nov 2014 15:29:06 +0000 Subject: Treat exec from subshell as if forked This removes weird behaviour when optimising the last command. In particular SHLVL is correct from a subshell executed as the last command. --- ChangeLog | 6 ++++++ Src/exec.c | 9 +++++++++ Test/D04parameter.ztst | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 6a3d18bf2..678b561ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2014-11-02 Peter Stephenson + + * 33591: Src/exec.c, Test/D04parameter.ztst: don't treat an exec + within an subshell as an exec of the parent shell even if about + to exit. Fixes incorrect SHLVL in subshell. + 2014-10-31 Oliver Kiddle * unposted: Src/jobs.c: quash compiler warning diff --git a/Src/exec.c b/Src/exec.c index 5bbd4e15d..d2d4e800b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2995,6 +2995,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) { diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index d7f39cb37..0cbe6c95d 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1548,7 +1548,7 @@ foo= print ${foo:wq} print ${:wq} -0:Empty parameter shouldn't cause modifiers to crash the shell +0:Empty parameter should not cause modifiers to crash the shell > > @@ -1656,3 +1656,10 @@ >h:i >j,k >l + + SHLVL=1 + $ZTST_testdir/../Src/zsh -c 'echo $SHLVL' + $ZTST_testdir/../Src/zsh -c '(echo $SHLVL)' +0:SHLVL appears sensible when about to exit shell +>2 +>2 -- cgit v1.2.3 From 7abd611a2396bad9d93d18681a2c59cb1ea0e158 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Thu, 6 Nov 2014 10:50:20 -0800 Subject: 33614 (based on RedHat BZ-978613): signal safety when updating global state in execshfunc() --- ChangeLog | 5 +++++ Src/exec.c | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index cb739d5a1..9c772c7d0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2014-11-06 Barton E. Schaefer + + * 33614 (based on RedHat BZ-978613): Src/exec.c: signal safety + when updating global state in execshfunc() + 2014-11-05 Oliver Kiddle * 33604: Src/Zle/zle_utils.c, Src/Zle/zle_vi.c, Test/X02zlevi.ztst: diff --git a/Src/exec.c b/Src/exec.c index d2d4e800b..042215d34 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4555,6 +4555,7 @@ execshfunc(Shfunc shf, LinkList args) fputc('\n', xtrerr); fflush(xtrerr); } + queue_signals(); ocs = cmdstack; ocsp = cmdsp; cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); @@ -4562,7 +4563,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; @@ -4570,6 +4575,7 @@ execshfunc(Shfunc shf, LinkList args) if (!list_pipe) deletefilelist(last_file_list, 0); + unqueue_signals(); } /* -- cgit v1.2.3 From 2b615bedaff1d64a1521972717a91058c74b579f Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sun, 23 Nov 2014 16:02:48 -0800 Subject: 33775: error opening file in $(<...) is not fatal --- ChangeLog | 4 ++++ Src/exec.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index a11982965..d59c99290 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2014-11-23 Barton E. Schaefer + + * 33775: Src/exec.c: error opening file in $(<...) is not fatal + 2014-11-23 Oliver Kiddle * 33743: Src/Modules/zpty.c, configure.ac: use posix_openpt diff --git a/Src/exec.c b/Src/exec.c index 042215d34..02a8fe3ad 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3840,8 +3840,8 @@ 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); } -- cgit v1.2.3 From 49d6aace41f5fe47abfaa87d25c42dbdb84dfb88 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Fri, 28 Nov 2014 13:30:22 -0800 Subject: 33816, 33819: GLOB_ASSIGN changes integer and floating type variables to string scalars --- ChangeLog | 8 ++++++++ Src/exec.c | 10 +++++++++- Test/A06assign.ztst | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 7a9de40d4..2a308a37e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2014-11-28 Barton E. Schaefer + + * 33819: Test/A06assign.ztst: regression tests for 33816 + + * 33816 (2nd part): Src/exec.c: GLOB_ASSIGN changes integer and + floating type variables to string scalars rather than treat single + match file names as arithmetic expressions + 2014-11-28 Wayne Davison * unposted: avoid compiler warning about a set-but-not-used var. diff --git a/Src/exec.c b/Src/exec.c index 02a8fe3ad..2b7c55f8f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2243,8 +2243,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; diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index 9a0a4f0cc..3c9ea0837 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -1,5 +1,10 @@ # Tests of parameter assignments +%prep + mkdir assign.tmp && cd assign.tmp + + touch tmpfile1 tmpfile2 + %test typeset -A assoc @@ -413,3 +418,18 @@ >world >worldliness >world + + integer i n x + float f + setopt globassign + i=tmpfile1 + n=tmp* + x=*2 + f=2+2 + typeset -p i n x f +0:GLOB_ASSIGN with numeric types +>typeset -i i=0 +>typeset -a n +>n=(tmpfile1 tmpfile2) +>typeset x=tmpfile2 +>typeset -E f=4.000000000e+00 -- cgit v1.2.3 From 0d4b548d1e4a08105597791fd6308d7fd70d3ddf Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 30 Nov 2014 23:19:55 +0100 Subject: 33818: fix types passed to sizeof detected by coverity as being wrong --- ChangeLog | 6 ++++++ Src/Builtins/sched.c | 2 +- Src/Zle/complist.c | 4 ++-- Src/exec.c | 4 ++-- Src/sort.c | 2 +- Src/utils.c | 6 +++--- 6 files changed, 15 insertions(+), 9 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 2a308a37e..fed76e508 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2014-11-30 Oliver Kiddle + + * 33818: Src/Builtins/sched.c, Src/Zle/complist.c, + Src/exec.c, Src/sort.c, Src/utils.c: fix types passed to sizeof + detected by coverity as being wrong + 2014-11-28 Barton E. Schaefer * 33819: Test/A06assign.ztst: regression tests for 33816 diff --git a/Src/Builtins/sched.c b/Src/Builtins/sched.c index c1cc98354..bcf7661f4 100644 --- a/Src/Builtins/sched.c +++ b/Src/Builtins/sched.c @@ -346,7 +346,7 @@ schedgetfn(UNUSED(Param pm)) for (i = 0, sch = schedcmds; sch; sch = sch->next, i++) ; - aptr = ret = zhalloc(sizeof(char **) * (i+1)); + aptr = ret = zhalloc(sizeof(char *) * (i+1)); for (sch = schedcmds; sch; sch = sch->next, aptr++) { char tbuf[40], *flagstr; time_t t; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 2e1a5273c..c129940f7 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -2059,8 +2059,8 @@ complistmatches(UNUSED(Hookdef dummy), Chdata dat) i = zterm_columns * listdat.nlines; free(mtab); - mtab = (Cmatch **) zalloc(i * sizeof(Cmatch **)); - memset(mtab, 0, i * sizeof(Cmatch **)); + mtab = (Cmatch **) zalloc(i * sizeof(Cmatch *)); + memset(mtab, 0, i * sizeof(Cmatch *)); free(mgtab); mgtab = (Cmgroup *) zalloc(i * sizeof(Cmgroup)); #ifdef DEBUG diff --git a/Src/exec.c b/Src/exec.c index 2b7c55f8f..a5f877191 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2299,13 +2299,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) { diff --git a/Src/sort.c b/Src/sort.c index 3d00bb576..92ee1c0d4 100644 --- a/Src/sort.c +++ b/Src/sort.c @@ -368,7 +368,7 @@ strmetasort(char **array, int sortwhat, int *unmetalenp) sortdir = (sortwhat & SORTIT_BACKWARDS) ? -1 : 1; sortnumeric = (sortwhat & SORTIT_NUMERICALLY) ? 1 : 0; - qsort(sortptrarr, nsort, sizeof(SortElt *), eltpcmp); + qsort(sortptrarr, nsort, sizeof(SortElt), eltpcmp); sortnumeric = oldsortnumeric; sortdir = oldsortdir; diff --git a/Src/utils.c b/Src/utils.c index 5f0c1062b..926814759 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -693,12 +693,12 @@ slashsplit(char *s) int t0; if (!*s) - return (char **) zshcalloc(sizeof(char **)); + return (char **) zshcalloc(sizeof(char *)); for (t = s, t0 = 0; *t; t++) if (*t == '/') t0++; - q = r = (char **) zalloc(sizeof(char **) * (t0 + 2)); + q = r = (char **) zalloc(sizeof(char *) * (t0 + 2)); while ((t = strchr(s, '/'))) { *q++ = ztrduppfx(s, t - s); @@ -2955,7 +2955,7 @@ colonsplit(char *s, int uniq) for (t = s, ct = 0; *t; t++) /* count number of colons */ if (*t == ':') ct++; - ptr = ret = (char **) zalloc(sizeof(char **) * (ct + 2)); + ptr = ret = (char **) zalloc(sizeof(char *) * (ct + 2)); t = s; do { -- cgit v1.2.3 From d067ebcacd55472f720b2765ec686a69b25c9a90 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 7 Dec 2014 16:24:19 +0000 Subject: 33876: etc.: Separate errors and keyboards interrupts Combination of 12 commits from interrupt_abort branch. Basic strategy is to introduce bits to errflag and to set and reset them separately. Remove interrupt status on return to main keymap. Turn off ERRFLAG_INT for always block. Restore bit thereafter: we probably need a new variable in order to allow user interrupts to be reset in the always block. Add TRY_BLOCK_INTERRUPT This works the same as TRY_BLOCK_ERROR, but for a SIGINT, too. Ensure propagation of SIGINT from exited job. If received by foreground job, shell uses ERRFLAG_INT, not ERRFLAG_ERROR, to set the new state. Reset errflag before precmd() Add always block in _main_completion to fix ZLS_COLORS Ensures we get the right state of $ZLS_COLORS at the end of _main_complete even if there's an interrupt. However, the "right state" is a bit messy as it depends on styles. --- ChangeLog | 20 +++++++++++-- Completion/Base/Core/_main_complete | 21 ++++++++------ Doc/Zsh/params.yo | 11 ++++++++ Src/Modules/zpty.c | 2 +- Src/Modules/zutil.c | 8 ++++-- Src/Zle/compcore.c | 2 +- Src/Zle/compctl.c | 10 +++---- Src/Zle/compresult.c | 2 +- Src/Zle/textobjects.c | 2 +- Src/Zle/zle_hist.c | 6 ++-- Src/Zle/zle_keymap.c | 10 +++++++ Src/Zle/zle_main.c | 14 ++++++---- Src/Zle/zle_misc.c | 2 +- Src/Zle/zle_tricky.c | 20 +++++++++++-- Src/Zle/zle_utils.c | 3 +- Src/builtin.c | 22 +++++++-------- Src/exec.c | 56 +++++++++++++++++++++++-------------- Src/glob.c | 12 ++++---- Src/hist.c | 10 ++++--- Src/init.c | 29 ++++++++++++++++--- Src/input.c | 3 +- Src/jobs.c | 23 +++++++++------ Src/lex.c | 5 ++-- Src/loop.c | 28 ++++++++++++++++--- Src/params.c | 13 +++++---- Src/parse.c | 31 ++++++++++---------- Src/prompt.c | 7 +++-- Src/signals.c | 28 ++++++++++++++++--- Src/subst.c | 27 ++++++++++++------ Src/utils.c | 8 +++--- Src/zsh.h | 14 ++++++++++ 31 files changed, 315 insertions(+), 134 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 94e28ccb2..e30324894 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2014-12-11 Peter Stephenson + + * 33876: etc.: Completion/Base/Core/_main_complete, + Doc/Zsh/params.yo, Src/Modules/zpty.c, Src/Modules/zutil.c, + Src/Zle/compcore.c, Src/Zle/compctl.c, Src/Zle/compresult.c, + Src/Zle/textobjects.c, Src/Zle/zle_hist.c, Src/Zle/zle_keymap.c, + Src/Zle/zle_main.c, Src/Zle/zle_misc.c, Src/Zle/zle_tricky.c, + Src/Zle/zle_utils.c, Src/builtin.c, Src/exec.c, Src/glob.c, + Src/hist.c, Src/init.c, Src/input.c, Src/jobs.c, Src/lex.c, + Src/loop.c, Src/params.c, Src/parse.c, Src/prompt.c, + Src/signals.c, Src/subst.c, Src/utils.c, Src/zsh.h: Separate + shell errors and user interrupt flags into different bits of + errflag: ERRFLAG_ERROR and ERRFLAG_INT. Various + rationalisations to make keyboard interrupts work smoothly. + Work done on interrupt_abort branch. + 2014-12-10 Mikael Magnusson * 33948: Completion/Unix/Command/_getent, @@ -612,7 +628,7 @@ * 33354: Src/jobs.c, Test/A05execution.ztst: when backgrounding a pipeline, close all pipe descriptors in the parent; add test for both this and 33345+33346 - + 2014-10-03 Bart Schaefer * 33346: Src/parse.c: another bit of the 33345 repair @@ -2083,7 +2099,7 @@ 2013-12-20 Barton E. Schaefer * 32172; Test/A05execution.ztst: regression test for 32171 - + * 32171: Src/exec.c: fix leaked pipe descriptor that could deadlock a pipeline from a complex shell construct or function into an external command diff --git a/Completion/Base/Core/_main_complete b/Completion/Base/Core/_main_complete index bc63e83fb..d6a100777 100644 --- a/Completion/Base/Core/_main_complete +++ b/Completion/Base/Core/_main_complete @@ -43,6 +43,8 @@ local -a precommands typeset -U _lastdescr _comp_ignore _comp_colors +{ + [[ -z "$curcontext" ]] && curcontext=::: zstyle -s ":completion:${curcontext}:" insert-tab tmp || tmp=yes @@ -349,17 +351,20 @@ fi ( "$_comp_force_list" = ?* && nm -ge _comp_force_list ) ]] && compstate[list]="${compstate[list]//messages} force" -if [[ "$compstate[old_list]" = keep ]]; then - if [[ $_saved_colors_set = 1 ]]; then - ZLS_COLORS="$_saved_colors" +} always { + # Stuff we always do to clean up. + if [[ "$compstate[old_list]" = keep ]]; then + if [[ $_saved_colors_set = 1 ]]; then + ZLS_COLORS="$_saved_colors" + else + unset ZLS_COLORS + fi + elif (( $#_comp_colors )); then + ZLS_COLORS="${(j.:.)_comp_colors}" else unset ZLS_COLORS fi -elif (( $#_comp_colors )); then - ZLS_COLORS="${(j.:.)_comp_colors}" -else - unset ZLS_COLORS -fi +} # Now call the post-functions. diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 5833d6be9..391a5fb0a 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -754,6 +754,17 @@ It may be reset, clearing the error condition. See ifzman(em(Complex Commands) in zmanref(zshmisc))\ ifnzman(noderef(Complex Commands)) ) +vindex(TRY_BLOCK_INTERRUPT) +item(tt(TRY_BLOCK_INTERRUPT) )( +This variable works in a similar way to tt(TRY_BLOCK_ERROR), but +represents the status of an interrupt from the signal SIGINT, which +typically comes from the keyboard when the user types tt(^C). If set to +0, any such interrupt will be reset; otherwise, the interrupt is +propagated after the tt(always) block. + +Note that it is possible that an interrupt arrives during the execution +of the tt(always) block; this interrupt is also propagated. +) vindex(TTY) item(tt(TTY))( The name of the tty associated with the shell, if any. diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 63c79a731..7b6130c6f 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -308,7 +308,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) prog = parse_string(zjoin(args, ' ', 1), 0); if (!prog) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; scriptname = oscriptname; ineval = oineval; return 1; diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 1cca0c4b8..c89495070 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -301,7 +301,8 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) int ef = errflag; eprog = parse_string(zjoin(vals, ' ', 1), 0); - errflag = ef; + /* Keep any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); if (!eprog) { @@ -394,10 +395,11 @@ evalstyle(Stypat p) unsetparam("reply"); execode(p->eval, 1, 0, "style"); if (errflag) { - errflag = ef; + /* Keep any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); return NULL; } - errflag = ef; + errflag = ef | (errflag & ERRFLAG_INT); queue_signals(); if ((ret = getaparam("reply"))) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 35d410cc6..b0c6e06f8 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1671,7 +1671,7 @@ set_comp_sep(void) noaliases = ona; strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; noerrs = ne; lexrestore(); wb = owb; diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 0b7a32445..d15c2d1b0 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -1879,7 +1879,7 @@ ccmakehookfn(UNUSED(Hookdef dummy), struct ccmakedat *dat) if (!m || !(m = m->next)) break; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; } redup(osi, 0); dat->lst = 1; @@ -2121,7 +2121,7 @@ getreal(char *str) if (!errflag && nonempty(l) && ((char *) peekfirst(l)) && ((char *) peekfirst(l))[0]) return dupstring(peekfirst(l)); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; return dupstring(str); } @@ -2599,7 +2599,7 @@ makecomplistlist(Compctl cc, char *s, int incmd, int compadd) makecomplistflags(cc, s, incmd, compadd); /* Reset some information variables for the next try. */ - errflag = 0; + errflag &= ~ERRFLAG_ERROR; offs = oloffs; wb = owb; we = owe; @@ -2847,7 +2847,7 @@ sep_comp_string(char *ss, char *s, int noffs) noaliases = ona; strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; noerrs = ne; lexrestore(); wb = owb; @@ -3725,7 +3725,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) noaliases = ona; strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; lexrestore(); /* Fine, now do full expansion. */ prefork(foo, 0); diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index fcceb670c..93438a053 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1092,7 +1092,7 @@ do_single(Cmatch m) noerrs = 1; parsestr(p); singsub(&p); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; noerrs = ne; } } else { diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c index 85d014b27..37d2c0ad9 100644 --- a/Src/Zle/textobjects.c +++ b/Src/Zle/textobjects.c @@ -275,7 +275,7 @@ selectargument(UNUSED(char **args)) noaliases = ona; strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; noerrs = ne; lexrestore(); zlemetacs = ocs; diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 9f65994dc..88623bb3c 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -853,8 +853,10 @@ pushlineoredit(char **args) free(zhline); } ret = pushline(args); - if (!isfirstln) - errflag = done = 1; + if (!isfirstln) { + errflag |= ERRFLAG_ERROR; + done = 1; + } clearlist = 1; return ret; } diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 30d25ebaa..48f210c7e 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -504,6 +504,16 @@ mod_export void selectlocalmap(Keymap m) { localkeymap = m; + if (!m) + { + /* + * No local keymap; so we are returning to the global map. If + * the user ^Ced in the local map, they probably just want to go + * back to normal editing. So remove the interrupt error + * status. + */ + errflag &= ~ERRFLAG_INT; + } } /* Reopen the currently selected keymap, in case it got deleted. This * diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index caa052b13..a2f48e13a 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -744,7 +744,7 @@ raw_getbyte(long do_keytmout, char *cptr) } if (errflag) { /* No sensible way of handling errors here */ - errflag = 0; + errflag &= ~ERRFLAG_ERROR; /* * Paranoia: don't run the hooks again this * time. @@ -882,7 +882,7 @@ getbyte(long do_keytmout, int *timeout) die = 0; if (!errflag && !retflag && !breaks && !exit_pending) continue; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; breaks = obreaks; errno = old_errno; return lastchar = EOF; @@ -1075,7 +1075,7 @@ zlecore(void) DECCS(); handleundo(); } else { - errflag = 1; + errflag |= ERRFLAG_ERROR; break; } #ifdef HAVE_POLL @@ -1233,6 +1233,10 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) zleactive = 1; resetneeded = 1; + /* + * Start of the main zle read. + * Fully reset error conditions, including user interrupt. + */ errflag = retflag = 0; lastcol = -1; initmodifier(&zmod); @@ -1658,7 +1662,7 @@ bin_vared(char *name, char **args, Options ops, UNUSED(int func)) } if (!t || errflag) { /* error in editing */ - errflag = 0; + errflag &= ~ERRFLAG_ERROR; breaks = obreaks; if (t) zsfree(t); @@ -1778,7 +1782,7 @@ recursiveedit(UNUSED(char **args)) zrefresh(); zlecore(); - locerror = errflag; + locerror = errflag ? 1 : 0; errflag = done = eofsent = 0; return locerror; diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index d432acf7b..23286fc20 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -1041,7 +1041,7 @@ copyprevshellword(UNUSED(char **args)) int sendbreak(UNUSED(char **args)) { - errflag = 1; + errflag |= ERRFLAG_ERROR; return 1; } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index b15d91c8e..864f804b7 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -829,7 +829,7 @@ docomplete(int lst) if (olst == COMP_EXPAND_COMPLETE && !strcmp(ol, zlemetaline)) { zlemetacs = ocs; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; if (!compfunc) { char *p; @@ -877,6 +877,19 @@ docomplete(int lst) active = 0; makecommaspecial(0); + + /* + * As a special case, we reset user interrupts here. + * That's because completion is an intensive piece of + * computation that the user might want to interrupt separately + * from anything else going on. If they do, they probably + * want to keep the line edit buffer intact. + * + * There's a race here that the user might hit ^C just + * after completion exited anyway, but that's inevitable. + */ + errflag &= ~ERRFLAG_INT; + return dat[1]; } @@ -1394,7 +1407,8 @@ get_comp_string(void) } strinend(); inpop(); - errflag = lexflags = 0; + lexflags = 0; + errflag &= ~ERRFLAG_ERROR; if (parbegin != -1) { /* We are in command or process substitution if we are not in * a $((...)). */ @@ -2917,7 +2931,7 @@ getcurcmd(void) popheap(); strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; unmetafy_line(); lexrestore(); diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 08a32c30e..de91182b5 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1715,7 +1715,8 @@ zlecallhook(char *name, char *arg) execzlefunc(thingy, args, 1); unrefthingy(thingy); - errflag = saverrflag; + /* Retain any user interrupt error status */ + errflag = saverrflag | (errflag & ERRFLAG_INT); retflag = savretflag; } diff --git a/Src/builtin.c b/Src/builtin.c index c2af51f2e..ad3a1925f 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -422,7 +422,7 @@ execbuiltin(LinkList args, Builtin bn) argc -= argv - argarr; if (errflag) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; return 1; } @@ -3136,7 +3136,7 @@ bin_unset(char *name, char **argv, Options ops, int func) } } returnval = errflag; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; } else { zerrnam(name, "%s: invalid element for unset", s); returnval = 1; @@ -4242,7 +4242,7 @@ bin_print(char *name, char **args, Options ops, int func) if (*argp) { width = (int)mathevali(*argp++); if (errflag) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; ret = 1; } } @@ -4272,7 +4272,7 @@ bin_print(char *name, char **args, Options ops, int func) if (*argp) { prec = (int)mathevali(*argp++); if (errflag) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; ret = 1; } } @@ -4452,7 +4452,7 @@ bin_print(char *name, char **args, Options ops, int func) zlongval = (curarg) ? mathevali(curarg) : 0; if (errflag) { zlongval = 0; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; ret = 1; } print_val(zlongval) @@ -4481,7 +4481,7 @@ bin_print(char *name, char **args, Options ops, int func) } else doubleval = 0; if (errflag) { doubleval = 0; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; ret = 1; } print_val(doubleval) @@ -4494,7 +4494,7 @@ bin_print(char *name, char **args, Options ops, int func) zulongval = (curarg) ? mathevali(curarg) : 0; if (errflag) { zulongval = 0; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; ret = 1; } print_val(zulongval) @@ -4871,7 +4871,7 @@ zexit(int val, int from_where) in_exit = -1; /* * We want to do all remaining processing regardless of preceding - * errors. + * errors, even user interrupts. */ errflag = 0; @@ -5074,7 +5074,7 @@ eval(char **argv) if (fpushed) funcstack = funcstack->prev; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; scriptname = oscriptname; ineval = oineval; @@ -6101,7 +6101,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) condlex = zshlex; if (errflag) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; lexrestore(); return 1; } @@ -6278,7 +6278,7 @@ bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func)) while (*argv) val = matheval(*argv++); /* Errors in math evaluation in let are non-fatal. */ - errflag = 0; + errflag &= ~ERRFLAG_ERROR; /* should test for fabs(val.u.d) < epsilon? */ return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0; } diff --git a/Src/exec.c b/Src/exec.c index a5f877191..6a7dbb1e1 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; @@ -1601,7 +1601,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); @@ -1634,7 +1635,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; @@ -1719,12 +1723,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; @@ -2560,7 +2566,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)); @@ -2577,12 +2584,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 *) @@ -2598,7 +2607,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; } } @@ -2661,7 +2671,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) @@ -2691,7 +2702,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)) { @@ -2795,7 +2806,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) { @@ -2940,7 +2951,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) { @@ -3529,7 +3541,7 @@ execcmd(Estate state, int input, int output, int how, int last1) else exit(1); } - errflag = 1; + errflag |= ERRFLAG_ERROR; } } if (newxtrerr) { @@ -3759,8 +3771,10 @@ gethere(char **strp, int typ) parsestr(buf); - if (!errflag) - errflag = ef; + if (!errflag) { + /* Retain any user interrupt error */ + errflag = ef | (errflag & ERRFLAG_INT); + } } s = dupstring(buf); zfree(buf, bsiz); @@ -3854,7 +3868,7 @@ getoutput(char *cmd, int qt) return readoutput(stream, qt); } if (mpipe(pipes) < 0) { - errflag = 1; + errflag |= ERRFLAG_ERROR; cmdoutpid = 0; return NULL; } @@ -3864,7 +3878,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; @@ -4274,7 +4288,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"); @@ -4314,7 +4328,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? */ @@ -4932,7 +4946,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; diff --git a/Src/glob.c b/Src/glob.c index b3903f2ff..82f8d626c 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -682,7 +682,7 @@ parsecomplist(char *instr) /* Now get the next path component if there is one. */ l1 = (Complist) zhalloc(sizeof *l1); if ((l1->next = parsecomplist(instr)) == NULL) { - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } l1->pat = patcompile(NULL, compflags | PAT_ANY, NULL); @@ -728,7 +728,7 @@ parsecomplist(char *instr) return (ef && !l1->next) ? NULL : l1; } } - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } @@ -1790,7 +1790,7 @@ zglob(LinkList list, LinkNode np, int nountok) insertlinknode(list, node, ostr); return; } - errflag = 0; + errflag &= ~ERRFLAG_ERROR; zerr("bad pattern: %s", ostr); return; } @@ -1873,7 +1873,8 @@ zglob(LinkList list, LinkNode np, int nountok) tmpptr->sortstrs[iexec] = tmpptr->name; } - errflag = ef; + /* Retain any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); lastval = lv; } else { /* Failed, let's be safe */ @@ -3733,7 +3734,8 @@ qualsheval(char *name, UNUSED(struct stat *buf), UNUSED(off_t days), char *str) execode(prog, 1, 0, "globqual"); ret = lastval; - errflag = ef; + /* Retain any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); lastval = lv; if (!(inserts = getaparam("reply")) && diff --git a/Src/hist.c b/Src/hist.c index 7fe843a4a..a0061707c 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -287,7 +287,8 @@ ihgetc(void) c = histsubchar(c); if (c < 0) { /* bad expansion */ - errflag = lexstop = 1; + lexstop = 1; + errflag |= ERRFLAG_ERROR; return ' '; } } @@ -721,7 +722,7 @@ histsubchar(int c) noerrs = 1; parse_subst_string(sline); noerrs = one; - errflag = oef; + errflag = oef | (errflag & ERRFLAG_INT); remnulargs(sline); untokenize(sline); } @@ -880,7 +881,8 @@ hbegin(int dohist) char *hf; isfirstln = isfirstch = 1; - errflag = histdone = 0; + errflag &= ~ERRFLAG_ERROR; + histdone = 0; if (!dohist) stophist = 2; else if (dohist != 2) @@ -3182,7 +3184,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags) noaliases = ona; strinend(); inpop(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; nocomments = onc; noerrs = ne; lexrestore(); diff --git a/Src/init.c b/Src/init.c index 655166135..305908724 100644 --- a/Src/init.c +++ b/Src/init.c @@ -118,11 +118,24 @@ loop(int toplevel, int justonce) if (interact && toplevel) { int hstop = stophist; stophist = 3; + /* + * Reset all errors including the interrupt error status + * immediately, so preprompt runs regardless of what + * just happened. We'll reset again below as a + * precaution to ensure we get back to the command line + * no matter what. + */ + errflag = 0; preprompt(); if (stophist != 3) hbegin(1); else stophist = hstop; + /* + * Reset all errors, including user interupts. + * This is what allows ^C in an interactive shell + * to return us to the command line. + */ errflag = 0; } } @@ -178,7 +191,15 @@ loop(int toplevel, int justonce) /* The only permanent storage is from getpermtext() */ zsfree(cmdstr); - errflag = 0; + /* + * Note this does *not* remove a user interrupt error + * condition, even though we're at the top level loop: + * that would be inconsistent with the case where + * we didn't execute a preexec function. This is + * an implementation detail that an interrupting user + * does't care about. + */ + errflag &= ~ERRFLAG_ERROR; } if (stopmsg) /* unset 'you have stopped jobs' flag */ stopmsg--; @@ -689,7 +710,7 @@ init_term(void) { if (isset(INTERACTIVE)) zerr("can't find terminal definition for %s", term); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; termflags |= TERM_BAD; return 0; } else { @@ -1336,7 +1357,7 @@ source(char *s) if (prog) { pushheap(); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; execode(prog, 1, 0, "filecode"); popheap(); if (errflag) @@ -1379,7 +1400,7 @@ source(char *s) lineno = oldlineno; /* our current lineno */ loops = oloops; /* the # of nested loops we are in */ dosetopt(SHINSTDIN, oldshst, 1, opts); /* SHINSTDIN option */ - errflag = 0; + errflag &= ~ERRFLAG_ERROR; if (!exit_pending) retflag = 0; scriptname = old_scriptname; diff --git a/Src/input.c b/Src/input.c index 4ac7e6ec8..9552331a9 100644 --- a/Src/input.c +++ b/Src/input.c @@ -291,7 +291,8 @@ inputline(void) } if (errflag) { free(ingetcline); - return lexstop = errflag = 1; + errflag |= ERRFLAG_ERROR; + return lexstop = 1; } if (isset(VERBOSE)) { /* Output the whole line read so far. */ diff --git a/Src/jobs.c b/Src/jobs.c index 6663a40a6..a668b07e6 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -509,7 +509,7 @@ update_job(Job jn) prev_errflag = errflag; } breaks = loops; - errflag = 1; + errflag |= ERRFLAG_INT; inerrflush(); } } else { @@ -526,7 +526,7 @@ update_job(Job jn) prev_errflag = errflag; } breaks = loops; - errflag = 1; + errflag |= ERRFLAG_INT; inerrflush(); } if (somestopped && jn->stat & STAT_SUPERJOB) @@ -581,7 +581,7 @@ update_job(Job jn) breaks = loops; } else { breaks = loops; - errflag = 1; + errflag |= ERRFLAG_INT; } check_cursh_sig(sig); } @@ -1444,12 +1444,19 @@ zwaitjob(int job, int wait_cmd) restore_queue_signals(q); return 128 + last_signal; } - /* Commenting this out makes ^C-ing a job started by a function - stop the whole function again. But I guess it will stop - something else from working properly, we have to find out - what this might be. --oberon + /* Commenting this out makes ^C-ing a job started by a function + stop the whole function again. But I guess it will stop + something else from working properly, we have to find out + what this might be. --oberon + + When attempting to separate errors and interrupts, we + assumed because of the previous comment it would be OK + to remove ERRFLAG_ERROR and leave ERRFLAG_INT set, since + that's the one related to ^C. But that doesn't work. + There's something more here we don't understand. --pws + + errflag = 0; */ - errflag = 0; */ if (subsh) { killjb(jn, SIGCONT); jn->stat &= ~STAT_STOPPED; diff --git a/Src/lex.c b/Src/lex.c index b2a05448c..4addf8033 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -385,7 +385,7 @@ lexrestore(void) ecnfunc = ln->ecnfunc; hlinesz = ln->hlinesz; toklineno = ln->toklineno; - errflag = 0; + errflag &= ~ERRFLAG_ERROR; free(ln); unqueue_signals(); @@ -1737,7 +1737,8 @@ parse_subst_string(char *s) inpop(); DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty."); lexrestore(); - errflag = err; + /* Keep any interrupt error status */ + errflag = err | (errflag & ERRFLAG_INT); if (ctok == LEXERR) { untokenize(s); return 1; diff --git a/Src/loop.c b/Src/loop.c index 82d2fe31a..8bb1ec9dd 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -259,7 +259,8 @@ execselect(Estate state, UNUSED(int do_exec)) 0, ZLCON_SELECT); if (errflag) str = NULL; - errflag = oef; + /* Keep any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); } else { str = promptexpand(prompt3, 0, NULL, NULL, NULL); zputs(str, stderr); @@ -632,6 +633,14 @@ execcase(Estate state, int do_exec) zlong try_errflag = -1; +/** + * Corresponding interrupt error status form `try' block. + */ + +/**/ +zlong +try_interrupt = -1; + /**/ zlong try_tryflag = 0; @@ -643,7 +652,7 @@ exectry(Estate state, int do_exec) Wordcode end, always; int endval; int save_retflag, save_breaks, save_contflag; - zlong save_try_errflag, save_try_tryflag; + zlong save_try_errflag, save_try_tryflag, save_try_interrupt; end = state->pc + WC_TRY_SKIP(state->pc[-1]); always = state->pc + 1 + WC_TRY_SKIP(*state->pc); @@ -670,7 +679,10 @@ exectry(Estate state, int do_exec) /* The always clause. */ save_try_errflag = try_errflag; - try_errflag = (zlong)errflag; + save_try_interrupt = try_interrupt; + try_errflag = (zlong)(errflag & ERRFLAG_ERROR); + try_interrupt = (zlong)((errflag & ERRFLAG_INT) ? 1 : 0); + /* We need to reset all errors to allow the block to execute */ errflag = 0; save_retflag = retflag; retflag = 0; @@ -682,8 +694,16 @@ exectry(Estate state, int do_exec) state->pc = always; execlist(state, 1, do_exec); - errflag = try_errflag ? 1 : 0; + if (try_errflag) + errflag |= ERRFLAG_ERROR; + else + errflag &= ~ERRFLAG_ERROR; + if (try_interrupt) + errflag |= ERRFLAG_INT; + else + errflag &= ~ERRFLAG_INT; try_errflag = save_try_errflag; + try_interrupt = save_try_interrupt; if (!retflag) retflag = save_retflag; if (!breaks) diff --git a/Src/params.c b/Src/params.c index 61edc5d08..79088d162 100644 --- a/Src/params.c +++ b/Src/params.c @@ -331,6 +331,7 @@ IPDEF5("SHLVL", &shlvl, varinteger_gsu), #define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} IPDEF6("OPTIND", &zoptind, varinteger_gsu), IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu), +IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu), #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} @@ -2654,7 +2655,7 @@ assignsparam(char *s, char *val, int flags) if (!isident(s)) { zerr("not an identifier: %s", s); zsfree(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } queue_signals(); @@ -2783,7 +2784,7 @@ assignaparam(char *s, char **val, int flags) if (!isident(s)) { zerr("not an identifier: %s", s); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } queue_signals(); @@ -2799,7 +2800,7 @@ assignaparam(char *s, char **val, int flags) zerr("%s: attempt to set slice of associative array", v->pm->node.nam); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } v = NULL; @@ -2870,13 +2871,13 @@ sethparam(char *s, char **val) if (!isident(s)) { zerr("not an identifier: %s", s); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (strchr(s, '[')) { freearray(val); zerr("nested associative arrays not yet supported"); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (unset(EXECOPT)) @@ -2916,7 +2917,7 @@ setnparam(char *s, mnumber val) if (!isident(s)) { zerr("not an identifier: %s", s); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (unset(EXECOPT)) diff --git a/Src/parse.c b/Src/parse.c index 4ceeb4eaf..c1709e03a 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -71,13 +71,14 @@ struct heredocs *hdocs; #define YYERROR(O) { tok = LEXERR; ecused = (O); return 0; } #define YYERRORV(O) { tok = LEXERR; ecused = (O); return; } -#define COND_ERROR(X,Y) do { \ - zwarn(X,Y); \ - herrflush(); \ - if (noerrs != 2) \ - errflag = 1; \ - YYERROR(ecused) \ -} while(0) +#define COND_ERROR(X,Y) \ + do { \ + zwarn(X,Y); \ + herrflush(); \ + if (noerrs != 2) \ + errflag |= ERRFLAG_ERROR; \ + YYERROR(ecused) \ + } while(0) /* @@ -506,7 +507,7 @@ par_event(void) yyerror(1); herrflush(); if (noerrs != 2) - errflag = 1; + errflag |= ERRFLAG_ERROR; ecused--; return 0; } else { @@ -2339,7 +2340,7 @@ yyerror(int noerr) zwarn("parse error"); } if (!noerr && noerrs != 2) - errflag = 1; + errflag |= ERRFLAG_ERROR; } /* @@ -3031,7 +3032,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) file = metafy(file, flen, META_REALLOC); if (!(prog = parse_string(file, 1)) || errflag) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); zfree(file, flen); zwarnnam(nam, "can't read file: %s", *files); @@ -3141,7 +3142,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map, for (hn = shfunctab->nodes[i]; hn; hn = hn->next) if (cur_add_func(nam, (Shfunc) hn, lnames, progs, &hlen, &tlen, what)) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); unlink(dump); return 1; @@ -3166,7 +3167,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map, pattry(pprog, hn->nam) && cur_add_func(nam, (Shfunc) hn, lnames, progs, &hlen, &tlen, what)) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); unlink(dump); return 1; @@ -3177,13 +3178,13 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map, if (errflag || !(shf = (Shfunc) shfunctab->getnode(shfunctab, *names))) { zwarnnam(nam, "unknown function: %s", *names); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); unlink(dump); return 1; } if (cur_add_func(nam, shf, lnames, progs, &hlen, &tlen, what)) { - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); unlink(dump); return 1; @@ -3192,7 +3193,7 @@ build_cur_dump(char *nam, char *dump, char **names, int match, int map, } if (empty(progs)) { zwarnnam(nam, "no functions"); - errflag = 0; + errflag &= ~ERRFLAG_ERROR; close(dfd); unlink(dump); return 1; diff --git a/Src/prompt.c b/Src/prompt.c index 0cc9ef917..3552575f3 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -192,8 +192,11 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) if (*s == Nularg && s[1] == '\0') *s = '\0'; - /* Ignore errors and status change in prompt substitution */ - errflag = olderr; + /* + * Ignore errors and status change in prompt substitution. + * However, keep any user interrupt error that occurred. + */ + errflag = olderr | (errflag & ERRFLAG_INT); lastval = oldval; } diff --git a/Src/signals.c b/Src/signals.c index e72850516..899f1217b 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -619,7 +619,7 @@ zhandler(int sig) zexit(SIGINT, 1); if (list_pipe || chline || simple_pline) { breaks = loops; - errflag = 1; + errflag |= ERRFLAG_INT; inerrflush(); check_cursh_sig(SIGINT); } @@ -640,6 +640,11 @@ zhandler(int sig) if (idle >= 0 && idle < tmout) alarm(tmout - idle); else { + /* + * We want to exit now. + * Cancel all errors, including a user interrupt + * which is now redundant. + */ errflag = noerrs = 0; zwarn("timeout"); stopmsg = 1; @@ -1267,7 +1272,18 @@ dotrapargs(int sig, int *sigtr, void *sigfn) !(isfunc && new_trap_return == 0)) { if (isfunc) { breaks = loops; - errflag = 1; + /* + * For SIGINT we behave the same as the default behaviour + * i.e. we set the error bit indicating an interrupt. + * We do this with SIGQUIT, too, even though we don't + * handle SIGQUIT by default. That's to try to make + * it behave a bit more like its normal behaviour when + * the trap handler has told us that's what it wants. + */ + if (sig == SIGINT || sig == SIGQUIT) + errflag |= ERRFLAG_INT; + else + errflag |= ERRFLAG_ERROR; } lastval = new_trap_return; /* return triggered */ @@ -1282,8 +1298,12 @@ dotrapargs(int sig, int *sigtr, void *sigfn) */ lastval = olastval; } - if (try_tryflag) - errflag = traperr; + if (try_tryflag) { + if (traperr) + errflag |= ERRFLAG_ERROR; + else + errflag &= ~ERRFLAG_ERROR; + } breaks += obreaks; /* return not triggered: restore old flag */ retflag = oretflag; diff --git a/Src/subst.c b/Src/subst.c index 61aa1c136..43932c256 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2822,7 +2822,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) haserr = parse_subst_string(s); noerrs = one; if (!quoteerr) { - errflag = oef; + /* Retain user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); if (haserr) shtokenize(s); } else if (haserr || errflag) { @@ -3249,8 +3250,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) haserr = 1; } noerrs = one; - if (!quoteerr) - errflag = oef; + if (!quoteerr) { + /* Retain user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); + } if (haserr || errflag) return NULL; } @@ -3483,8 +3486,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) untokenize(*ap); } noerrs = one; - if (!quoteerr) - errflag = oef; + if (!quoteerr) { + /* Retain any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); + } else if (haserr || errflag) { zerr("parse error in parameter value"); return NULL; @@ -3516,8 +3521,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags) noerrs = 1; haserr = parse_subst_string(val); noerrs = one; - if (!quoteerr) - errflag = oef; + if (!quoteerr) { + /* Retain any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); + } else if (haserr || errflag) { zerr("parse error in parameter value"); return NULL; @@ -4086,7 +4093,8 @@ modify(char **str, char **ptr) noerrs = 1; parse_subst_string(copy); noerrs = one; - errflag = oef; + /* Retain any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); remnulargs(copy); untokenize(copy); } @@ -4161,7 +4169,8 @@ modify(char **str, char **ptr) noerrs = 1; parse_subst_string(*str); noerrs = one; - errflag = oef; + /* Retain any user interrupt error status */ + errflag = oef | (errflag & ERRFLAG_INT); remnulargs(*str); untokenize(*str); } diff --git a/Src/utils.c b/Src/utils.c index ab3d3da93..893a8ca02 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -153,7 +153,7 @@ VA_DCL if (errflag || noerrs) { if (noerrs < 2) - errflag = 1; + errflag |= ERRFLAG_ERROR; return; } @@ -161,7 +161,7 @@ VA_DCL VA_GET_ARG(ap, fmt, const char *); zwarning(NULL, fmt, ap); va_end(ap); - errflag = 1; + errflag |= ERRFLAG_ERROR; } /**/ @@ -181,7 +181,7 @@ VA_DCL VA_GET_ARG(ap, fmt, const char *); zwarning(cmd, fmt, ap); va_end(ap); - errflag = 1; + errflag |= ERRFLAG_ERROR; } /**/ @@ -330,7 +330,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) num = va_arg(ap, int); if (num == EINTR) { fputs("interrupt\n", file); - errflag = 1; + errflag |= ERRFLAG_ERROR; return; } errmsg = strerror(num); diff --git a/Src/zsh.h b/Src/zsh.h index 031deaf3f..b366e0ff4 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2623,6 +2623,20 @@ enum trap_state { #define IN_EVAL_TRAP() \ (intrap && !trapisfunc && traplocallevel == locallevel) +/* + * Bits in the errflag variable. + */ +enum errflag_bits { + /* + * Standard internal error bit. + */ + ERRFLAG_ERROR = 1, + /* + * User interrupt. + */ + ERRFLAG_INT = 2 +}; + /***********/ /* Sorting */ /***********/ -- cgit v1.2.3 From d6a32ddeed914434f5b56b013c9d03b28781d065 Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Sat, 27 Dec 2014 21:55:58 -0800 Subject: 34065: following an "if" condition, do not test lastval for ERR_EXIT until a new command is run Includes unposted regression tests. --- ChangeLog | 7 +++++++ Src/exec.c | 4 ++++ Src/loop.c | 16 +++++++++++----- Test/A05execution.ztst | 17 +++++++++++++++++ 4 files changed, 39 insertions(+), 5 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 055cf2f21..52808bea4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2014-12-27 Barton E. Schaefer + + * unposted: Test/A05execution.ztst: regressions for 34065 + + * 34065: Src/exec.c, Src/loop.c: following an "if" condition, do + not test lastval for ERR_EXIT until a new command is run + 2014-12-21 Oliver Kiddle * Daniel Shahaf: 33977: Completion/Zsh/Command/_bindkey: diff --git a/Src/exec.c b/Src/exec.c index 6a7dbb1e1..eaf73df25 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2632,6 +2632,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) diff --git a/Src/loop.c b/Src/loop.c index 8bb1ec9dd..7b3bdd2c8 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -102,7 +102,10 @@ execfor(Estate state, int do_exec) addlinknode(args, dupstring(*x)); } } - /* lastval = 0; */ + + if (!args || empty(args)) + lastval = 0; + loops++; pushheap(); cmdpush(CS_FOR); @@ -238,10 +241,10 @@ execselect(Estate state, UNUSED(int do_exec)) } if (!args || empty(args)) { state->pc = end; - return 1; + return 0; } loops++; - /* lastval = 0; */ + pushheap(); cmdpush(CS_SELECT); usezle = interact && SHTTY != -1 && isset(USEZLE); @@ -519,14 +522,17 @@ execif(Estate state, int do_exec) s = 1; state->pc = next; } - noerrexit = olderrexit; if (run) { + /* we need to ignore lastval until we reach execcmd() */ + noerrexit = olderrexit ? olderrexit : lastval ? 2 : 0; cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN)); execlist(state, 1, do_exec); cmdpop(); - } else + } else { + noerrexit = olderrexit; lastval = 0; + } state->pc = end; return lastval; diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index 042b2d0a5..cc2d34d23 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -269,3 +269,20 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline 1:The status of recently exited background jobs is recorded >3 >2 + +# Regression test for workers/34060 (patch in 34065) + setopt ERR_EXIT NULL_GLOB + if false; then :; else echo if:$?; fi + if false; then :; else for x in _*_; do :; done; echo for:$?; fi +0:False "if" condition handled correctly by "for" loops with ERR_EXIT +>if:1 +>for:0 + +# Regression test for workers/34065 (uses setopt from preceding test) + select x; do :; done; echo $? + select x in; do :; done; echo $? + select x in _*_; do :; done; echo $? +0:The status of "select" is zero when the loop body does not execute +>0 +>0 +>0 -- cgit v1.2.3 From 1cd802357096b60561b1a50c0c23ab357bbc0de3 Mon Sep 17 00:00:00 2001 From: Takeshi Banse Date: Sat, 27 Dec 2014 22:05:05 -0800 Subject: 34064: assignment before command replaces array with export even when KSH_ARRAYS --- ChangeLog | 3 +++ Src/exec.c | 2 ++ Test/A06assign.ztst | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 52808bea4..67057cb9f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,9 @@ * 34065: Src/exec.c, Src/loop.c: following an "if" condition, do not test lastval for ERR_EXIT until a new command is run + * Takeshi Banse: 34064: Src/exec.c, Test/A06assign.ztst: assignment + before command replaces array with export even when KSH_ARRAYS + 2014-12-21 Oliver Kiddle * Daniel Shahaf: 33977: Completion/Zsh/Command/_bindkey: diff --git a/Src/exec.c b/Src/exec.c index eaf73df25..6b93008bd 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2294,6 +2294,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 diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index 3c9ea0837..0ad9a0aca 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -433,3 +433,21 @@ >n=(tmpfile1 tmpfile2) >typeset x=tmpfile2 >typeset -E f=4.000000000e+00 + + A=(first second) + A="${A[*]}" /bin/sh -c 'echo $A' + print -l "${A[@]}" +0:command execution with assignments shadowing array parameter +>first second +>first +>second + + setopt ksharrays + A=(first second) + A="${A[*]}" /bin/sh -c 'echo $A' + print -l "${A[@]}" + unsetopt ksharrays +0:command execution with assignments shadowing array parameter with ksharrays +>first second +>first +>second -- cgit v1.2.3 From bd2175fe7dad87575ae838fa116226aff4c18191 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Mon, 5 Jan 2015 16:46:26 +0100 Subject: 34134: anon funcs: don't leak shf and related data Found by Coverity (Issue 439076). --- ChangeLog | 2 ++ Src/exec.c | 6 ++++++ 2 files changed, 8 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 2afb52cc2..f54188af9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2015-01-06 Mikael Magnusson + * 34134: Src/exec.c: anon funcs: don't leak shf and related data + * 34112: Src/builtin.c: typeset: fix leak of oldval * 34106: Src/hist.c: use zhtricat instead of tricat diff --git a/Src/exec.c b/Src/exec.c index 6b93008bd..ab9291024 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4461,6 +4461,11 @@ 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; } @@ -4486,6 +4491,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; -- cgit v1.2.3 From cfd91eac0732da8ece012ca4ab051d928a85c9dd Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 8 Jan 2015 21:39:26 +0000 Subject: Rearrange context saving. Variables are now associated with the module that declares them, being initialised and saved/restored there. However, as many variables are used for communication between modules, many of them are set in multiple places, so the assignment is ambiguous. --- ChangeLog | 9 ++ Src/Zle/compcore.c | 4 +- Src/Zle/compctl.c | 8 +- Src/Zle/textobjects.c | 4 +- Src/Zle/zle_tricky.c | 24 ++-- Src/builtin.c | 8 +- Src/context.c | 116 ++++++++++++++++++ Src/exec.c | 8 +- Src/hist.c | 88 +++++++++++++- Src/init.c | 4 +- Src/lex.c | 321 ++++++++++---------------------------------------- Src/parse.c | 83 ++++++++++++- Src/signals.c | 4 +- Src/zsh.h | 65 ++++++++++ Src/zsh.mdd | 3 +- 15 files changed, 450 insertions(+), 299 deletions(-) create mode 100644 Src/context.c (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index c4ea61b41..a78425bda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2015-01-09 Peter Stephenson + + * 34189: Src/Zle/compcore.c, Src/Zle/compctl.c, + Src/Zle/textobjects.c, Src/Zle/zle_tricky.c, Src/builtin.c, + Src/context.c, Src/exec.c, Src/hist.c, Src/init.c, Src/lex.c, + Src/parse.c, Src/signals.c, Src/zsh.h, Src/zsh.mdd: + vain attempt to make context save and restore neater and + control the status variables thereby managed. + 2015-01-09 Peter Stephenson * 34182: Doc/Zsh/mod_files.yo: to add zf_* builtins you can diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index f5056058a..000f9da2a 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1524,7 +1524,7 @@ set_comp_sep(void) ol = zlemetaline; addedx = 1; noerrs = 1; - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ZLE; /* * tl is the length of the temporary string including @@ -1673,7 +1673,7 @@ set_comp_sep(void) inpop(); errflag &= ~ERRFLAG_ERROR; noerrs = ne; - lexrestore(); + zcontext_restore(); wb = owb; we = owe; zlemetaline = ol; diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 2a80e6c84..43dd4e2a9 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -2795,7 +2795,7 @@ sep_comp_string(char *ss, char *s, int noffs) * get the words we have to expand. */ addedx = 1; noerrs = 1; - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ZLE; tmp = (char *) zhalloc(tl = sl + 3 + strlen(s)); strcpy(tmp, ss); @@ -2849,7 +2849,7 @@ sep_comp_string(char *ss, char *s, int noffs) inpop(); errflag &= ~ERRFLAG_ERROR; noerrs = ne; - lexrestore(); + zcontext_restore(); wb = owb; we = owe; zlemetacs = ocs; @@ -3707,7 +3707,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) /* Put the string in the lexer buffer and call the lexer to * * get the words we have to expand. */ - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ZLE; tmpbuf = (char *)zhalloc(strlen(cc->str) + 5); sprintf(tmpbuf, "foo %s", cc->str); /* KLUDGE! */ @@ -3726,7 +3726,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) strinend(); inpop(); errflag &= ~ERRFLAG_ERROR; - lexrestore(); + zcontext_restore(); /* Fine, now do full expansion. */ prefork(foo, 0); if (!errflag) { diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c index 37d2c0ad9..9b3277a97 100644 --- a/Src/Zle/textobjects.c +++ b/Src/Zle/textobjects.c @@ -241,7 +241,7 @@ selectargument(UNUSED(char **args)) addedx = 0; noerrs = 1; - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ACTIVE; linein = zlegetline(&ll, &cs); zlemetall = ll; @@ -277,7 +277,7 @@ selectargument(UNUSED(char **args)) inpop(); errflag &= ~ERRFLAG_ERROR; noerrs = ne; - lexrestore(); + zcontext_restore(); zlemetacs = ocs; wb = owb; we = owe; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 950c22f38..f18ad170e 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -698,7 +698,7 @@ docomplete(int lst) freeheap(); /* Save the lexer state, in case the completion code uses the lexer * * somewhere (e.g. when processing a compctl -s flag). */ - lexsave(); + zcontext_save(); if (inwhat == IN_ENV) lincmd = 0; if (s) { @@ -868,7 +868,7 @@ docomplete(int lst) } else ret = 1; /* Reset the lexer state, pop the heap. */ - lexrestore(); + zcontext_restore(); popheap(); dat[0] = lst; @@ -1164,7 +1164,7 @@ get_comp_string(void) varname = NULL; insubscr = 0; clwpos = -1; - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ZLE; inpush(dupstrspace(linptr), 0, NULL); strinbeg(0); @@ -1422,7 +1422,7 @@ get_comp_string(void) zlemetall -= parend; zlemetaline[zlemetall + addedx] = '\0'; } - lexrestore(); + zcontext_restore(); tt = NULL; goto start; } @@ -1496,12 +1496,12 @@ get_comp_string(void) if (tmp) { tmp = NULL; linptr = zlemetaline; - lexrestore(); + zcontext_restore(); addedx = 0; goto start; } noaliases = ona; - lexrestore(); + zcontext_restore(); return NULL; } @@ -2151,7 +2151,7 @@ get_comp_string(void) offs = boffs; } } - lexrestore(); + zcontext_restore(); return (char *)s; } @@ -2791,7 +2791,7 @@ doexpandhist(void) expanding = 1; excs = zlemetacs; zlemetall = zlemetacs = 0; - lexsave(); + zcontext_save(); /* We push ol as it will remain unchanged */ inpush(ol, 0, NULL); strinbeg(1); @@ -2803,7 +2803,7 @@ doexpandhist(void) } while (tok != ENDINPUT && tok != LEXERR); while (!lexstop) hgetc(); - /* We have to save errflags because it's reset in lexrestore. Since * + /* We have to save errflags because it's reset in zcontext_restore. Since * * noerrs was set to 1 errflag is true if there was a habort() which * * means that the expanded string is unusable. */ err = errflag; @@ -2811,7 +2811,7 @@ doexpandhist(void) noaliases = ona; strinend(); inpop(); - lexrestore(); + zcontext_restore(); expanding = 0; if (!err) { @@ -2910,7 +2910,7 @@ getcurcmd(void) int curlincmd; char *s = NULL; - lexsave(); + zcontext_save(); lexflags = LEXFLAGS_ZLE; metafy_line(); inpush(dupstrspace(zlemetaline), 0, NULL); @@ -2934,7 +2934,7 @@ getcurcmd(void) inpop(); errflag &= ~ERRFLAG_ERROR; unmetafy_line(); - lexrestore(); + zcontext_restore(); return s; } diff --git a/Src/builtin.c b/Src/builtin.c index d2108264f..8abe728db 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6102,7 +6102,7 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) } } - lexsave(); + zcontext_save(); testargs = argv; tok = NULLTOK; condlex = testlex; @@ -6112,16 +6112,16 @@ bin_test(char *name, char **argv, UNUSED(Options ops), int func) if (errflag) { errflag &= ~ERRFLAG_ERROR; - lexrestore(); + zcontext_restore(); return 1; } if (!prog || tok == LEXERR) { zwarnnam(name, tokstr ? "parse error" : "argument expected"); - lexrestore(); + zcontext_restore(); return 1; } - lexrestore(); + zcontext_restore(); if (*curtestarg) { zwarnnam(name, "too many arguments"); diff --git a/Src/context.c b/Src/context.c new file mode 100644 index 000000000..bd8d191bf --- /dev/null +++ b/Src/context.c @@ -0,0 +1,116 @@ +/* + * context.c - context save and restore + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ +/* + * This short file provides a home for the stack of saved contexts. + * The actions for saving and restoring are encapsulated within + * individual modules. + */ + +#include "zsh.mdh" +#include "context.pro" + +struct context_stack { + struct context_stack *next; + + struct hist_stack hist_stack; + struct lex_stack lex_stack; + struct parse_stack parse_stack; +}; + +static struct context_stack *cstack; + +/* save some or all of current context */ + +/**/ +mod_export void +zcontext_save_partial(int parts) +{ + struct context_stack *cs; + + cs = (struct context_stack *)malloc(sizeof(struct context_stack)); + + if (parts & ZCONTEXT_HIST) { + hist_context_save(&cs->hist_stack, !cstack); + } + if (parts & ZCONTEXT_LEX) { + lex_context_save(&cs->lex_stack, !cstack); + } + if (parts & ZCONTEXT_PARSE) { + parse_context_save(&cs->parse_stack, !cstack); + } + + cs->next = cstack; + cstack = cs; +} + +/* save context in full */ + +/**/ +mod_export void +zcontext_save(void) +{ + zcontext_save_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE); +} + +/* restore context or part thereof */ + +/**/ +mod_export void +zcontext_restore_partial(int parts) +{ + struct context_stack *cs = cstack; + + DPUTS(!cstack, "BUG: zcontext_restore() without zcontext_save()"); + + queue_signals(); + cstack = cstack->next; + + if (parts & ZCONTEXT_HIST) { + hist_context_restore(&cs->hist_stack, !cstack); + } + if (parts & ZCONTEXT_LEX) { + lex_context_restore(&cs->lex_stack, !cstack); + } + if (parts & ZCONTEXT_PARSE) { + parse_context_restore(&cs->parse_stack, !cstack); + } + + free(cs); + + unqueue_signals(); +} + +/* restore full context */ + +/**/ +mod_export void +zcontext_restore(void) +{ + zcontext_restore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE); +} diff --git a/Src/exec.c b/Src/exec.c index ab9291024..7b6495113 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -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; } @@ -3349,9 +3349,9 @@ 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; diff --git a/Src/hist.c b/Src/hist.c index e65d78bfd..447f00e84 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -222,6 +222,85 @@ static int histsave_stack_pos = 0; static zlong histfile_linect; +/* save history context */ + +/**/ +void +hist_context_save(struct hist_stack *hs, int toplevel) +{ + if (toplevel) { + /* top level, make this version visible to ZLE */ + zle_chline = chline; + /* ensure line stored is NULL-terminated */ + if (hptr) + *hptr = '\0'; + } + hs->histactive = histactive; + hs->histdone = histdone; + hs->stophist = stophist; + hs->hline = chline; + hs->hptr = hptr; + hs->chwords = chwords; + hs->chwordlen = chwordlen; + hs->chwordpos = chwordpos; + hs->hwgetword = hwgetword; + hs->hgetc = hgetc; + hs->hungetc = hungetc; + hs->hwaddc = hwaddc; + hs->hwbegin = hwbegin; + hs->hwend = hwend; + hs->addtoline = addtoline; + hs->hlinesz = hlinesz; + /* + * We save and restore the command stack with history + * as it's visible to the user interactively, so if + * we're preserving history state we'll continue to + * show the current set of commands from input. + */ + hs->cstack = cmdstack; + hs->csp = cmdsp; + + stophist = 0; + chline = NULL; + hptr = NULL; + histactive = 0; + cmdstack = (unsigned char *)zalloc(CMDSTACKSZ); + cmdsp = 0; +} + +/**/ +void +hist_context_restore(const struct hist_stack *hs, int toplevel) +{ + if (toplevel) { + /* Back to top level: don't need special ZLE value */ + DPUTS(hs->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE"); + zle_chline = NULL; + } + histactive = hs->histactive; + histdone = hs->histdone; + stophist = hs->stophist; + chline = hs->hline; + hptr = hs->hptr; + chwords = hs->chwords; + chwordlen = hs->chwordlen; + chwordpos = hs->chwordpos; + hwgetword = hs->hwgetword; + hgetc = hs->hgetc; + hungetc = hs->hungetc; + hwaddc = hs->hwaddc; + hwbegin = hs->hwbegin; + hwend = hs->hwend; + addtoline = hs->addtoline; + hlinesz = hs->hlinesz; + if (cmdstack) + zfree(cmdstack, CMDSTACKSZ); + cmdstack = hs->cstack; + cmdsp = hs->csp; +} + +/* restore history context */ + /* add a character to the current history word */ static void @@ -815,6 +894,11 @@ strinbeg(int dohist) strin++; hbegin(dohist); lexinit(); + /* + * Also initialise some variables owned by the parser but + * used for communication between the parser and lexer. + */ + init_parse_status(); } /* done reading a string */ @@ -2992,7 +3076,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags) opts[RCQUOTES] = 0; addedx = 0; noerrs = 1; - lexsave(); + zcontext_save(); lexflags = flags | LEXFLAGS_ACTIVE; /* * Are we handling comments? @@ -3189,7 +3273,7 @@ bufferwords(LinkList list, char *buf, int *index, int flags) errflag &= ~ERRFLAG_ERROR; nocomments = onc; noerrs = ne; - lexrestore(); + zcontext_restore(); zlemetacs = ocs; zlemetall = oll; wb = owb; diff --git a/Src/init.c b/Src/init.c index 080fc8561..e7d86feac 100644 --- a/Src/init.c +++ b/Src/init.c @@ -107,7 +107,7 @@ loop(int toplevel, int justonce) pushheap(); if (!toplevel) - lexsave(); + zcontext_save(); for (;;) { freeheap(); if (stophist == 3) /* re-entry via preprompt() */ @@ -227,7 +227,7 @@ loop(int toplevel, int justonce) } err = errflag; if (!toplevel) - lexrestore(); + zcontext_restore(); popheap(); if (err) diff --git a/Src/lex.c b/Src/lex.c index d440f3d70..69441b28d 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -203,263 +203,64 @@ static int dbparens; static int len = 0, bsiz = 256; static char *bptr; -struct lexstack { - struct lexstack *next; - - int incmdpos; - int incond; - int incasepat; - int dbparens; - int isfirstln; - int isfirstch; - int histactive; - int histdone; - int lexflags; - int stophist; - int hlinesz; - char *hline; - char *hptr; - enum lextok tok; - int isnewlin; - char *tokstr; - char *zshlextext; - char *bptr; - int bsiz; - int len; - int lex_add_raw; - char *tokstr_raw; - char *bptr_raw; - int bsiz_raw; - int len_raw; - short *chwords; - int chwordlen; - int chwordpos; - int hwgetword; - int lexstop; - struct heredocs *hdocs; - int (*hgetc) _((void)); - void (*hungetc) _((int)); - void (*hwaddc) _((int)); - void (*hwbegin) _((int)); - void (*hwend) _((void)); - void (*addtoline) _((int)); - - int eclen, ecused, ecnpats; - Wordcode ecbuf; - Eccstr ecstrs; - int ecsoffs, ecssub, ecnfunc; - - unsigned char *cstack; - int csp; - zlong toklineno; -}; - -static struct lexstack *lstack = NULL; - -/* save the context or parts thereof */ - -/* is this a hack or what? */ +/* save lexical context */ /**/ -mod_export void -lexsave_partial(int parts) -{ - struct lexstack *ls; - - ls = (struct lexstack *)malloc(sizeof(struct lexstack)); - - if (parts & ZCONTEXT_LEX) { - ls->incmdpos = incmdpos; - ls->incond = incond; - ls->incasepat = incasepat; - ls->dbparens = dbparens; - ls->isfirstln = isfirstln; - ls->isfirstch = isfirstch; - ls->lexflags = lexflags; - - ls->tok = tok; - ls->isnewlin = isnewlin; - ls->tokstr = tokstr; - ls->zshlextext = zshlextext; - ls->bptr = bptr; - ls->bsiz = bsiz; - ls->len = len; - ls->lex_add_raw = lex_add_raw; - ls->tokstr_raw = tokstr_raw; - ls->bptr_raw = bptr_raw; - ls->bsiz_raw = bsiz_raw; - ls->len_raw = len_raw; - ls->lexstop = lexstop; - ls->toklineno = toklineno; - - tokstr = zshlextext = bptr = NULL; - bsiz = 256; - tokstr_raw = bptr_raw = NULL; - bsiz_raw = len_raw = lex_add_raw = 0; - - inredir = 0; - } - if (parts & ZCONTEXT_HIST) { - if (!lstack) { - /* top level, make this version visible to ZLE */ - zle_chline = chline; - /* ensure line stored is NULL-terminated */ - if (hptr) - *hptr = '\0'; - } - ls->histactive = histactive; - ls->histdone = histdone; - ls->stophist = stophist; - ls->hline = chline; - ls->hptr = hptr; - ls->chwords = chwords; - ls->chwordlen = chwordlen; - ls->chwordpos = chwordpos; - ls->hwgetword = hwgetword; - ls->hgetc = hgetc; - ls->hungetc = hungetc; - ls->hwaddc = hwaddc; - ls->hwbegin = hwbegin; - ls->hwend = hwend; - ls->addtoline = addtoline; - ls->hlinesz = hlinesz; - /* - * We save and restore the command stack with history - * as it's visible to the user interactively, so if - * we're preserving history state we'll continue to - * show the current set of commands from input. - */ - ls->cstack = cmdstack; - ls->csp = cmdsp; - - stophist = 0; - chline = NULL; - hptr = NULL; - histactive = 0; - cmdstack = (unsigned char *)zalloc(CMDSTACKSZ); - cmdsp = 0; - } - if (parts & ZCONTEXT_PARSE) { - ls->hdocs = hdocs; - ls->eclen = eclen; - ls->ecused = ecused; - ls->ecnpats = ecnpats; - ls->ecbuf = ecbuf; - ls->ecstrs = ecstrs; - ls->ecsoffs = ecsoffs; - ls->ecssub = ecssub; - ls->ecnfunc = ecnfunc; - ecbuf = NULL; - hdocs = NULL; - } - - ls->next = lstack; - lstack = ls; -} - -/* save context in full */ - -/**/ -mod_export void -lexsave(void) -{ - lexsave_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE); -} - -/* restore context or part therefore */ - -/**/ -mod_export void -lexrestore_partial(int parts) +void +lex_context_save(struct lex_stack *ls, int toplevel) { - struct lexstack *ln = lstack; - - DPUTS(!lstack, "BUG: lexrestore() without lexsave()"); - - queue_signals(); - lstack = lstack->next; - - if (parts & ZCONTEXT_LEX) { - incmdpos = ln->incmdpos; - incond = ln->incond; - incasepat = ln->incasepat; - dbparens = ln->dbparens; - isfirstln = ln->isfirstln; - isfirstch = ln->isfirstch; - lexflags = ln->lexflags; - tok = ln->tok; - isnewlin = ln->isnewlin; - tokstr = ln->tokstr; - zshlextext = ln->zshlextext; - bptr = ln->bptr; - bsiz = ln->bsiz; - len = ln->len; - lex_add_raw = ln->lex_add_raw; - tokstr_raw = ln->tokstr_raw; - bptr_raw = ln->bptr_raw; - bsiz_raw = ln->bsiz_raw; - len_raw = ln->len_raw; - lexstop = ln->lexstop; - toklineno = ln->toklineno; - } - - if (parts & ZCONTEXT_HIST) { - if (!lstack) { - /* Back to top level: don't need special ZLE value */ - DPUTS(ln->hline != zle_chline, "BUG: Ouch, wrong chline for ZLE"); - zle_chline = NULL; - } - histactive = ln->histactive; - histdone = ln->histdone; - stophist = ln->stophist; - chline = ln->hline; - hptr = ln->hptr; - chwords = ln->chwords; - chwordlen = ln->chwordlen; - chwordpos = ln->chwordpos; - hwgetword = ln->hwgetword; - hgetc = ln->hgetc; - hungetc = ln->hungetc; - hwaddc = ln->hwaddc; - hwbegin = ln->hwbegin; - hwend = ln->hwend; - addtoline = ln->addtoline; - hlinesz = ln->hlinesz; - if (cmdstack) - zfree(cmdstack, CMDSTACKSZ); - cmdstack = ln->cstack; - cmdsp = ln->csp; - } - - if (parts & ZCONTEXT_PARSE) { - if (ecbuf) - zfree(ecbuf, eclen); - - hdocs = ln->hdocs; - eclen = ln->eclen; - ecused = ln->ecused; - ecnpats = ln->ecnpats; - ecbuf = ln->ecbuf; - ecstrs = ln->ecstrs; - ecsoffs = ln->ecsoffs; - ecssub = ln->ecssub; - ecnfunc = ln->ecnfunc; - - errflag &= ~ERRFLAG_ERROR; - } - - free(ln); - - unqueue_signals(); + (void)toplevel; + + ls->dbparens = dbparens; + ls->isfirstln = isfirstln; + ls->isfirstch = isfirstch; + ls->lexflags = lexflags; + + ls->tok = tok; + ls->tokstr = tokstr; + ls->zshlextext = zshlextext; + ls->bptr = bptr; + ls->bsiz = bsiz; + ls->len = len; + ls->lex_add_raw = lex_add_raw; + ls->tokstr_raw = tokstr_raw; + ls->bptr_raw = bptr_raw; + ls->bsiz_raw = bsiz_raw; + ls->len_raw = len_raw; + ls->lexstop = lexstop; + ls->toklineno = toklineno; + + tokstr = zshlextext = bptr = NULL; + bsiz = 256; + tokstr_raw = bptr_raw = NULL; + bsiz_raw = len_raw = lex_add_raw = 0; } -/* complete restore context */ +/* restore lexical context */ /**/ mod_export void -lexrestore(void) +lex_context_restore(const struct lex_stack *ls, int toplevel) { - lexrestore_partial(ZCONTEXT_HIST|ZCONTEXT_LEX|ZCONTEXT_PARSE); + (void)toplevel; + + dbparens = ls->dbparens; + isfirstln = ls->isfirstln; + isfirstch = ls->isfirstch; + lexflags = ls->lexflags; + tok = ls->tok; + tokstr = ls->tokstr; + zshlextext = ls->zshlextext; + bptr = ls->bptr; + bsiz = ls->bsiz; + len = ls->len; + lex_add_raw = ls->lex_add_raw; + tokstr_raw = ls->tokstr_raw; + bptr_raw = ls->bptr_raw; + bsiz_raw = ls->bsiz_raw; + len_raw = ls->len_raw; + lexstop = ls->lexstop; + toklineno = ls->toklineno; } /**/ @@ -634,9 +435,7 @@ initlextabs(void) void lexinit(void) { - incond = incasepat = nocorrect = - infor = dbparens = lexstop = 0; - incmdpos = 1; + nocorrect = dbparens = lexstop = 0; tok = ENDINPUT; } @@ -1725,7 +1524,7 @@ parsestrnoerr(char *s) { int l = strlen(s), err; - lexsave(); + zcontext_save(); untokenize(s); inpush(dupstring(s), 0, NULL); strinbeg(0); @@ -1737,7 +1536,7 @@ parsestrnoerr(char *s) strinend(); inpop(); DPUTS(cmdsp, "BUG: parsestr: cmdstack not empty."); - lexrestore(); + zcontext_restore(); return err; } @@ -1756,7 +1555,7 @@ parse_subscript(char *s, int sub, int endchar) if (!*s || *s == endchar) return 0; - lexsave(); + zcontext_save(); untokenize(t = dupstring(s)); inpush(t, 0, NULL); strinbeg(0); @@ -1776,7 +1575,7 @@ parse_subscript(char *s, int sub, int endchar) strinend(); inpop(); DPUTS(cmdsp, "BUG: parse_subscript: cmdstack not empty."); - lexrestore(); + zcontext_restore(); return s; } @@ -1794,7 +1593,7 @@ parse_subst_string(char *s) if (!*s || !strcmp(s, nulstring)) return 0; - lexsave(); + zcontext_save(); untokenize(s); inpush(dupstring(s), 0, NULL); strinbeg(0); @@ -1807,7 +1606,7 @@ parse_subst_string(char *s) strinend(); inpop(); DPUTS(cmdsp, "BUG: parse_subst_string: cmdstack not empty."); - lexrestore(); + zcontext_restore(); /* Keep any interrupt error status */ errflag = err | (errflag & ERRFLAG_INT); if (ctok == LEXERR) { @@ -1817,7 +1616,7 @@ parse_subst_string(char *s) #ifdef DEBUG /* * Historical note: we used to check here for olen (the value of len - * before lexrestore()) == l, but that's not necessarily the case if + * before zcontext_restore()) == l, but that's not necessarily the case if * we stripped an RCQUOTE. */ if (ctok != STRING || (errflag && !noerrs)) { @@ -2047,7 +1846,7 @@ skipcomm(void) new_len = len; new_bsiz = bsiz; - lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); + zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); } else { /* * Set up for nested command subsitution, however @@ -2063,7 +1862,7 @@ skipcomm(void) new_len = len_raw; new_bsiz = bsiz_raw; - lexsave_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); + zcontext_save_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); } tokstr_raw = new_tokstr; bsiz_raw = new_bsiz; @@ -2090,7 +1889,7 @@ skipcomm(void) */ new_lexstop = lexstop; - lexrestore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); + zcontext_restore_partial(ZCONTEXT_LEX|ZCONTEXT_PARSE); if (lex_add_raw) { /* diff --git a/Src/parse.c b/Src/parse.c index fa37ca3d9..0b54a904d 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -31,7 +31,7 @@ #include "parse.pro" /* != 0 if we are about to read a command word */ - + /**/ mod_export int incmdpos; @@ -242,6 +242,67 @@ int ecsoffs, ecssub, ecnfunc; #define EC_DOUBLE_THRESHOLD 32768 #define EC_INCREMENT 1024 +/* save parse context */ + +/**/ +void +parse_context_save(struct parse_stack *ps, int toplevel) +{ + (void)toplevel; + + ps->incmdpos = incmdpos; + ps->aliasspaceflag = aliasspaceflag; + ps->incond = incond; + ps->inredir = inredir; + ps->incasepat = incasepat; + ps->isnewlin = isnewlin; + ps->infor = infor; + + ps->hdocs = hdocs; + ps->eclen = eclen; + ps->ecused = ecused; + ps->ecnpats = ecnpats; + ps->ecbuf = ecbuf; + ps->ecstrs = ecstrs; + ps->ecsoffs = ecsoffs; + ps->ecssub = ecssub; + ps->ecnfunc = ecnfunc; + ecbuf = NULL; + hdocs = NULL; +} + +/* restore parse context */ + +/**/ +void +parse_context_restore(const struct parse_stack *ps, int toplevel) +{ + (void)toplevel; + + if (ecbuf) + zfree(ecbuf, eclen); + + incmdpos = ps->incmdpos; + aliasspaceflag = ps->aliasspaceflag; + incond = ps->incond; + inredir = ps->inredir; + incasepat = ps->incasepat; + incasepat = ps->incasepat; + isnewlin = ps->isnewlin; + infor = ps->infor; + + hdocs = ps->hdocs; + eclen = ps->eclen; + ecused = ps->ecused; + ecnpats = ps->ecnpats; + ecbuf = ps->ecbuf; + ecstrs = ps->ecstrs; + ecsoffs = ps->ecsoffs; + ecssub = ps->ecssub; + ecnfunc = ps->ecnfunc; + + errflag &= ~ERRFLAG_ERROR; +} /* Adjust pointers in here-doc structs. */ @@ -359,6 +420,21 @@ ecstrcode(char *s) } while (0) +/**/ +mod_export void +init_parse_status(void) +{ + /* + * These variables are currently declared by the parser, so we + * initialise them here. Possibly they are more naturally declared + * by the lexical anaylser; however, as they are used for signalling + * between the two it's a bit ambiguous. We clear them when + * using the lexical analyser for strings as well as here. + */ + incasepat = incond = inredir = infor = 0; + incmdpos = 1; +} + /* Initialise wordcode buffer. */ /**/ @@ -373,6 +449,8 @@ init_parse(void) ecsoffs = ecnpats = 0; ecssub = 0; ecnfunc = 0; + + init_parse_status(); } /* Build eprog. */ @@ -539,9 +617,8 @@ parse_list(void) int c = 0; tok = ENDINPUT; - incmdpos = 1; - zshlex(); init_parse(); + zshlex(); par_list(&c); if (tok != ENDINPUT) { clear_hdocs(); diff --git a/Src/signals.c b/Src/signals.c index 899f1217b..3950ad1a2 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -1210,7 +1210,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) intrap++; *sigtr |= ZSIG_IGNORED; - lexsave(); + zcontext_save(); /* execsave will save the old trap_return and trap_state */ execsave(); breaks = retflag = 0; @@ -1265,7 +1265,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn) new_trap_return = trap_return; execrestore(); - lexrestore(); + zcontext_restore(); if (new_trap_state == TRAP_STATE_FORCE_RETURN && /* zero return from function isn't special */ diff --git a/Src/zsh.h b/Src/zsh.h index 475b7824f..8fb4f977a 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2691,6 +2691,71 @@ struct sortelt { typedef struct sortelt *SortElt; +/*********************************************************/ +/* Structures to save and restore for individual modules */ +/*********************************************************/ + +/* History */ +struct hist_stack { + int histactive; + int histdone; + int stophist; + int hlinesz; + char *hline; + char *hptr; + short *chwords; + int chwordlen; + int chwordpos; + int hwgetword; + int (*hgetc) _((void)); + void (*hungetc) _((int)); + void (*hwaddc) _((int)); + void (*hwbegin) _((int)); + void (*hwend) _((void)); + void (*addtoline) _((int)); + unsigned char *cstack; + int csp; +}; + +/* Lexical analyser */ +struct lex_stack { + int dbparens; + int isfirstln; + int isfirstch; + int lexflags; + enum lextok tok; + char *tokstr; + char *zshlextext; + char *bptr; + int bsiz; + int len; + int lex_add_raw; + char *tokstr_raw; + char *bptr_raw; + int bsiz_raw; + int len_raw; + int lexstop; + zlong toklineno; +}; + +/* Parser */ +struct parse_stack { + struct heredocs *hdocs; + + int incmdpos; + int aliasspaceflag; + int incond; + int inredir; + int incasepat; + int isnewlin; + int infor; + + int eclen, ecused, ecnpats; + Wordcode ecbuf; + Eccstr ecstrs; + int ecsoffs, ecssub, ecnfunc; +}; + /************************/ /* Flags to casemodifiy */ /************************/ diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 9a8c923f9..f0379d2d1 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -9,7 +9,8 @@ alwayslink=1 # autobins not specified because of alwayslink -objects="builtin.o compat.o cond.o exec.o glob.o hashtable.o hashnameddir.o \ +objects="builtin.o compat.o cond.o context.o \ +exec.o glob.o hashtable.o hashnameddir.o \ hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ signames.o sort.o string.o subst.o text.o utils.o watch.o" -- cgit v1.2.3 From c6c9f5daf2e196e6ab7346dfbf5f5166a1d87f0c Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 18 Jan 2015 22:38:57 +0000 Subject: 34322: bug with interface to parsestr() etc. Was showing up in places like ${(e)...} where command substitution could reallocate the token string, but actually there was never any guarantee that the lexer wouldn't do that, so this was always a bit iffy. --- ChangeLog | 6 ++++++ Src/Zle/compctl.c | 4 ++-- Src/Zle/compresult.c | 3 ++- Src/exec.c | 9 +++++---- Src/init.c | 11 +++++++---- Src/lex.c | 30 +++++++++++++++++++++--------- Src/params.c | 3 ++- Src/prompt.c | 2 +- Src/subst.c | 8 +++++--- Src/utils.c | 2 +- Test/D04parameter.ztst | 7 +++++++ 11 files changed, 59 insertions(+), 26 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 30068eb8b..5d52f6693 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2015-01-18 Peter Stephenson + * 34322: Src/Zle/compctl.c, Src/Zle/compresult.c, Src/exec.c, + Src/init.c, Src/lex.c, Src/params.c, Src/prompt.c, Src/subst.c, + Src/utils.c, Test/D04parameter.ztst: update interface to + parsestr()/parsestrnoerr() to ensure correct token string + is passed back. + * 34320: Src/hist.c, Src/lex.c: alias expansion in history of command substitution. diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 43dd4e2a9..189582d22 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -3853,7 +3853,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) yaptr = get_user_var(uv); if ((tt = cc->explain)) { tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) { singsub(&tt); untokenize(tt); } @@ -3873,7 +3873,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } } else if ((tt = cc->explain)) { tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) { singsub(&tt); untokenize(tt); } diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index dbef7f841..9f383f4b8 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1090,7 +1090,8 @@ do_single(Cmatch m) } if (tryit) { noerrs = 1; - parsestr(p); + p = dupstring(p); + parsestr(&p); singsub(&p); errflag &= ~ERRFLAG_ERROR; noerrs = ne; diff --git a/Src/exec.c b/Src/exec.c index 7b6495113..f42fb2b9b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3772,19 +3772,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) { /* Retain any user interrupt error */ errflag = ef | (errflag & ERRFLAG_INT); } } - s = dupstring(buf); - zfree(buf, bsiz); - return s; + return buf; } /* open here string fd */ diff --git a/Src/init.c b/Src/init.c index e7d86feac..3e41fb9dd 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1197,10 +1197,13 @@ run_init_scripts(void) if (islogin) sourcehome(".profile"); noerrs = 2; - if (s && !parsestr(s)) { - singsub(&s); - noerrs = 0; - source(s); + if (s) { + s = dupstring(s); + if (!parsestr(&s)) { + singsub(&s); + noerrs = 0; + source(s); + } } noerrs = 0; } else diff --git a/Src/lex.c b/Src/lex.c index e4dfdfaca..433c27fbb 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1503,17 +1503,27 @@ dquote_parse(char endchar, int sub) return err; } -/* Tokenize a string given in s. Parsing is done as in double * - * quotes. This is usually called before singsub(). */ +/* + * Tokenize a string given in s. Parsing is done as in double + * quotes. This is usually called before singsub(). + * + * parsestr() is noisier, reporting an error if the parse failed. + * + * On entry, *s must point to a string allocated from the stack of + * exactly the right length, i.e. strlen(*s) + 1, as the string + * is used as the lexical token string whose memory management + * demands this. Usually the input string will therefore be + * the result of an immediately preceding dupstring(). + */ /**/ mod_export int -parsestr(char *s) +parsestr(char **s) { int err; if ((err = parsestrnoerr(s))) { - untokenize(s); + untokenize(*s); if (err > 32 && err < 127) zerr("parse error near `%c'", err); else @@ -1524,18 +1534,20 @@ parsestr(char *s) /**/ mod_export int -parsestrnoerr(char *s) +parsestrnoerr(char **s) { - int l = strlen(s), err; + int l = strlen(*s), err; zcontext_save(); - untokenize(s); - inpush(dupstring(s), 0, NULL); + untokenize(*s); + inpush(dupstring(*s), 0, NULL); strinbeg(0); lexbuf.len = 0; - lexbuf.ptr = tokstr = s; + lexbuf.ptr = tokstr = *s; lexbuf.siz = l + 1; err = dquote_parse('\0', 1); + if (tokstr) + *s = tokstr; *lexbuf.ptr = '\0'; strinend(); inpop(); diff --git a/Src/params.c b/Src/params.c index b8e0c429b..64c78bd63 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1260,7 +1260,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, if (ishash && (keymatch || !rev)) remnulargs(s); if (needtok) { - if (parsestr(s)) + s = dupstring(s); + if (parsestr(&s)) return 0; singsub(&s); } else if (rev) diff --git a/Src/prompt.c b/Src/prompt.c index 3552575f3..ffc1d0df2 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -183,7 +183,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) int oldval = lastval; s = dupstring(s); - if (!parsestr(s)) + if (!parsestr(&s)) singsub(&s); /* * We don't need the special Nularg hack here and we're diff --git a/Src/subst.c b/Src/subst.c index 5f993d6fd..a2bb6483a 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1306,7 +1306,7 @@ get_intarg(char **s, int *delmatchp) p = dupstring(*s + arglen); *s = t + arglen; *t = sav; - if (parsestr(p)) + if (parsestr(&p)) return -1; singsub(&p); if (errflag) @@ -1329,7 +1329,8 @@ subst_parse_str(char **sp, int single, int err) *sp = s = dupstring(*sp); - if (!(err ? parsestr(s) : parsestrnoerr(s))) { + if (!(err ? parsestr(&s) : parsestrnoerr(&s))) { + *sp = s; if (!single) { int qt = 0; @@ -1439,7 +1440,8 @@ check_colon_subscript(char *str, char **endp) } sav = **endp; **endp = '\0'; - if (parsestr(str = dupstring(str))) + str = dupstring(str); + if (parsestr(&str)) return NULL; singsub(&str); remnulargs(str); diff --git a/Src/utils.c b/Src/utils.c index f8d239458..4561b5e9a 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1515,7 +1515,7 @@ checkmailpath(char **s) setunderscore(*s); u = dupstring(u); - if (! parsestr(u)) { + if (!parsestr(&u)) { singsub(&u); zputs(u, shout); fputc('\n', shout); diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 94d15f205..cf639fa8c 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1663,3 +1663,10 @@ 0:SHLVL appears sensible when about to exit shell >2 >2 + +# The following tests the return behaviour of parsestr/parsestrnoerr + alias param-test-alias='print $'\''\x45xpanded in substitution'\' + param='$(param-test-alias)' + print ${(e)param} +0:Alias expansion in command substitution in parameter evaluation +>Expanded in substitution -- cgit v1.2.3 From e6d964246700581fe22ea834b2ea12dd301e8c3d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 20 Jan 2015 09:29:22 +0000 Subject: users/19751: remove error on failure to close file descriptor by number. Keep it when closing file descriptor stored in a variable, i.e. explicitly opened by the user. --- ChangeLog | 6 ++++++ Src/exec.c | 7 ++++++- Test/A04redirect.ztst | 10 ++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 5d52f6693..8751e93f7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-01-20 Peter Stephenson + + * users/19751: Src/exec.c, Test/A04redirect.ztst: remove error + on closing file descriptors via number but keep it for those + controlled by variable. + 2015-01-18 Peter Stephenson * 34322: Src/Zle/compctl.c, Src/Zle/compresult.c, Src/exec.c, diff --git a/Src/exec.c b/Src/exec.c index f42fb2b9b..3b0e936b4 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3203,7 +3203,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); } diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index a39ce46c8..cb6778874 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -152,11 +152,13 @@ >hello >goodbye - ({ exec 3<&- } 2>/dev/null - exec 3<&- - read foo <&-) + (exec {varid}<&0 + exec {varid}<&- + print About to close a second time >&2 + read {varid}<&-) 1:'<&-' redirection -*?\(eval\):*: failed to close file descriptor 3:* +?About to close a second time +*?\(eval\):*: failed to close file descriptor * print foo >&- 0:'>&-' redirection -- cgit v1.2.3 From da86d6b4f2c3eef5b1f0860c9dae433f3a540951 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 9 Feb 2015 16:39:29 +0000 Subject: 34485: More rationalisation for anonymous functions. Don't attempt to treat as "simple" case as there are too many hidden problems. Pull out some post-execution functions to a common case in execcmd(). --- ChangeLog | 8 ++++++++ Src/exec.c | 51 +++++++++++++++++++++++++-------------------------- Src/parse.c | 6 ++---- Test/E01options.ztst | 26 ++++++++++++++++++-------- 4 files changed, 53 insertions(+), 38 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 1af298bf4..6729881b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2015-02-09 Peter Stephenson + + * 34485: Src/exec.c, Src/parse.c, Test/E01options.ztst: + rationalise some more anonymous function behaviour: + don't try to handle as "simple" case as there are too + many hidden problems; pull out some post-execution + functions to common cases in execcmd(). + 2015-02-09 Mikael Magnusson * 34466: Src/utils.c: Fix double unmeta in rm verification diff --git a/Src/exec.c b/Src/exec.c index 3b0e936b4..9bbcf4979 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2427,6 +2427,7 @@ execcmd(Estate state, int input, int output, int how, int last1) wordcode code; Wordcode beg = state->pc, varspc; FILE *oxtrerr = xtrerr, *newxtrerr = NULL; + LinkList restorelist = 0, removelist = 0; doneps4 = 0; redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); @@ -3359,9 +3360,9 @@ execcmd(Estate state, int input, int output, int how, int last1) zcontext_restore(); } else redir_prog = NULL; - + lastval = execfuncdef(state, redir_prog); - } + } else if (type >= WC_CURSH) { if (last1 == 1) do_exec = 1; @@ -3374,7 +3375,6 @@ execcmd(Estate state, int input, int output, int how, int last1) } else lastval = (execfuncs[type - WC_CURSH])(state, do_exec); } else if (is_builtin || is_shfunc) { - LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ if (!forked && ((cflags & BINF_COMMAND) || @@ -3424,29 +3424,6 @@ execcmd(Estate state, int input, int output, int how, int last1) } else clearerr(stdout); } - if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && - lastval && !subsh) { -#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); - } - - if (do_exec) { - if (subsh) - _exit(lastval); - - /* If we are exec'ing a command, and we are not in a subshell, * - * then check if we should save the history file. */ - if (isset(RCS) && interact && !nohistsave) - savehistfile(NULL, 1, HFILE_USE_OPTIONS); - exit(lastval); - } - if (restorelist) - restore_params(restorelist, removelist); - } else { if (!forked) setiparam("SHLVL", --shlvl); @@ -3496,6 +3473,28 @@ execcmd(Estate state, int input, int output, int how, int last1) execlist(state, 0, 1); } } + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && + lastval && !subsh) { +#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); + } + + if (do_exec) { + if (subsh) + _exit(lastval); + + /* If we are exec'ing a command, and we are not in a subshell, * + * then check if we should save the history file. */ + if (isset(RCS) && interact && !nohistsave) + savehistfile(NULL, 1, HFILE_USE_OPTIONS); + exit(lastval); + } + if (restorelist) + restore_params(restorelist, removelist); } err: diff --git a/Src/parse.c b/Src/parse.c index 0b54a904d..ffd25de9d 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1612,8 +1612,7 @@ par_funcdef(int *cmplx) num++; zshlex(); } - if (num > 0) - *cmplx = 1; + *cmplx = 1; ecbuf[parg] = ecused - parg; /*?*/ ecbuf[parg+1] = num; } @@ -1897,8 +1896,7 @@ par_simple(int *cmplx, int nr) argc++; zshlex(); } - if (argc > 0) - *cmplx = 1; + *cmplx = 1; ecbuf[parg] = ecused - parg; /*?*/ ecbuf[parg+1] = argc; } diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 46b183776..32135344f 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -783,14 +783,24 @@ >print is a shell builtin ?(eval):8: command not found: print -# This option seems to be problematic. I don't quite know how it works. -## func() { -## setopt localoptions printexitvalue -## false -## } -## func -## 1:PRINT_EXIT_VALUE option -## ?(eval):2: exit 1 +# PRINTEXITVALUE only works if shell input is coming from standard input. +# Goodness only knows why. + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + func() { + false + } + func + ' +1:PRINT_EXIT_VALUE option +?zsh: exit 1 + + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + () { false; } + ' +1:PRINT_EXIT_VALUE option for anonymous function +?zsh: exit 1 setopt promptbang print -P ! -- cgit v1.2.3 From 7398fea05980df98a29eac6f504397ea0cb3be7a Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Thu, 12 Feb 2015 09:27:53 -0800 Subject: 34514: Back out 34485, an alternate solution needs to be worked out. (Tweaked to keep the unrelated hunk of the E01 test.) --- ChangeLog | 6 ++++++ Src/exec.c | 51 ++++++++++++++++++++++++++------------------------- Src/parse.c | 6 ++++-- Test/E01options.ztst | 12 ++++++------ 4 files changed, 42 insertions(+), 33 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 7c52b4aee..4191a9198 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-02-12 Barton E. Schaefer + + * 34514: Src/exec.c, Src/parse.c, Test/E01options.ztst: + Back out 34485, an alternate solution needs to be worked + out. (Tweaked to keep the unrelated hunk of the E01 test.) + 2015-02-11 Peter Stephenson * users/19850: Doc/Zsh/params.yo, Src/watch.c: watch variable diff --git a/Src/exec.c b/Src/exec.c index 9bbcf4979..3b0e936b4 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2427,7 +2427,6 @@ execcmd(Estate state, int input, int output, int how, int last1) wordcode code; Wordcode beg = state->pc, varspc; FILE *oxtrerr = xtrerr, *newxtrerr = NULL; - LinkList restorelist = 0, removelist = 0; doneps4 = 0; redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); @@ -3360,9 +3359,9 @@ execcmd(Estate state, int input, int output, int how, int last1) zcontext_restore(); } else redir_prog = NULL; - + lastval = execfuncdef(state, redir_prog); - } + } else if (type >= WC_CURSH) { if (last1 == 1) do_exec = 1; @@ -3375,6 +3374,7 @@ execcmd(Estate state, int input, int output, int how, int last1) } else lastval = (execfuncs[type - WC_CURSH])(state, do_exec); } else if (is_builtin || is_shfunc) { + LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ if (!forked && ((cflags & BINF_COMMAND) || @@ -3424,6 +3424,29 @@ execcmd(Estate state, int input, int output, int how, int last1) } else clearerr(stdout); } + if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && + lastval && !subsh) { +#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); + } + + if (do_exec) { + if (subsh) + _exit(lastval); + + /* If we are exec'ing a command, and we are not in a subshell, * + * then check if we should save the history file. */ + if (isset(RCS) && interact && !nohistsave) + savehistfile(NULL, 1, HFILE_USE_OPTIONS); + exit(lastval); + } + if (restorelist) + restore_params(restorelist, removelist); + } else { if (!forked) setiparam("SHLVL", --shlvl); @@ -3473,28 +3496,6 @@ execcmd(Estate state, int input, int output, int how, int last1) execlist(state, 0, 1); } } - if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && - lastval && !subsh) { -#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); - } - - if (do_exec) { - if (subsh) - _exit(lastval); - - /* If we are exec'ing a command, and we are not in a subshell, * - * then check if we should save the history file. */ - if (isset(RCS) && interact && !nohistsave) - savehistfile(NULL, 1, HFILE_USE_OPTIONS); - exit(lastval); - } - if (restorelist) - restore_params(restorelist, removelist); } err: diff --git a/Src/parse.c b/Src/parse.c index ffd25de9d..0b54a904d 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1612,7 +1612,8 @@ par_funcdef(int *cmplx) num++; zshlex(); } - *cmplx = 1; + if (num > 0) + *cmplx = 1; ecbuf[parg] = ecused - parg; /*?*/ ecbuf[parg+1] = num; } @@ -1896,7 +1897,8 @@ par_simple(int *cmplx, int nr) argc++; zshlex(); } - *cmplx = 1; + if (argc > 0) + *cmplx = 1; ecbuf[parg] = ecused - parg; /*?*/ ecbuf[parg+1] = argc; } diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 32135344f..c6af80392 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -795,12 +795,12 @@ 1:PRINT_EXIT_VALUE option ?zsh: exit 1 - $ZTST_testdir/../Src/zsh -f <<<' - setopt printexitvalue - () { false; } - ' -1:PRINT_EXIT_VALUE option for anonymous function -?zsh: exit 1 +# $ZTST_testdir/../Src/zsh -f <<<' +# setopt printexitvalue +# () { false; } +# ' +#1:PRINT_EXIT_VALUE option for anonymous function +#?zsh: exit 1 setopt promptbang print -P ! -- cgit v1.2.3 From 7e6faf7237b1862fce3ebc34f09ee9ac07940638 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 12 Feb 2015 19:48:50 +0000 Subject: 34519: $_ for arguments of anonymous function --- ChangeLog | 5 +++++ Src/exec.c | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 47c3c8d1b..2660e4f22 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-02-12 Peter Stephenson + + * 34519: Src/exec.c: handle $_ with arguments to anonymous + function. + 2015-02-12 Barton E. Schaefer * 34514: Src/exec.c, Src/parse.c, Test/E01options.ztst: diff --git a/Src/exec.c b/Src/exec.c index 3b0e936b4..302e2b510 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4456,7 +4456,7 @@ 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; @@ -4477,6 +4477,9 @@ execfuncdef(Estate state, Eprog redir_prog) } } + setunderscore((args && nonempty(args)) ? + ((char *) getdata(lastnode(args))) : ""); + if (!args) args = newlinklist(); shf->node.nam = "(anon)"; -- cgit v1.2.3 From daa7d99702b8a3580b71ee74f962719ad1d052c8 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 13 Feb 2015 21:31:31 +0000 Subject: 34546: further $_ with anon function fix. Also add tests. --- ChangeLog | 5 +++++ Src/exec.c | 17 +++++++++++++---- Test/D04parameter.ztst | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 2660e4f22..641dfa75a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-02-13 Peter Stephenson + + * 34546: Src/exec.c, Test/D04parameter.ztst: fix another + oddity with $_ and anonymous functions and add tests. + 2015-02-12 Peter Stephenson * 34519: Src/exec.c: handle $_ with arguments to anonymous diff --git a/Src/exec.c b/Src/exec.c index 302e2b510..de6b9c574 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2784,9 +2784,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) && @@ -4374,7 +4379,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; @@ -4460,6 +4465,8 @@ execfuncdef(Estate state, Eprog redir_prog) */ LinkList args; + anon_func = 1; + state->pc = end; end += *state->pc++; args = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); @@ -4515,6 +4522,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); diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index cf639fa8c..42c7b4ec6 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1670,3 +1670,32 @@ print ${(e)param} 0:Alias expansion in command substitution in parameter evaluation >Expanded in substitution + + a=1 b=2 c=3 + : One; + function { + : Two + echo $_ + print -l $argv + } $_ Three + print -l $_ Four; +0:$_ with anonymous function +>Two +>One +>Three +>Three +>Four + + a=1 b=2 c=3 + : One + function { + : Two + echo $_ + print -l $argv + } + print -l "$_" Four +0:$_ with anonymous function without arguments +>Two +> +> +>Four -- cgit v1.2.3 From 8ebe18c081a0bd2f134f5fcbf1650d5d59920c96 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 16 Feb 2015 12:37:38 +0000 Subject: 34530: PRINT_EXIT_VALUE with anonymous functions. Simpler fix, avoiding structural changes. --- ChangeLog | 5 +++++ Src/exec.c | 14 ++++++++++++-- Test/E01options.ztst | 12 ++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index c389b936d..6fb13715a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,11 @@ * 34546: Src/exec.c, Test/D04parameter.ztst: fix another oddity with $_ and anonymous functions and add tests. +2015-02-13 Peter Stephenson + + * 34530: Src/exec.c, Test/E01options.ztst: revised fix + for PRINT_EXIT_VALUE from anonymous functions. + 2015-02-12 Peter Stephenson * 34519: Src/exec.c: handle $_ with arguments to anonymous diff --git a/Src/exec.c b/Src/exec.c index de6b9c574..947b815f9 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3364,9 +3364,9 @@ execcmd(Estate state, int input, int output, int how, int last1) zcontext_restore(); } else redir_prog = NULL; - + lastval = execfuncdef(state, redir_prog); - } + } else if (type >= WC_CURSH) { if (last1 == 1) do_exec = 1; @@ -4495,6 +4495,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); diff --git a/Test/E01options.ztst b/Test/E01options.ztst index c6af80392..32135344f 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -795,12 +795,12 @@ 1:PRINT_EXIT_VALUE option ?zsh: exit 1 -# $ZTST_testdir/../Src/zsh -f <<<' -# setopt printexitvalue -# () { false; } -# ' -#1:PRINT_EXIT_VALUE option for anonymous function -#?zsh: exit 1 + $ZTST_testdir/../Src/zsh -f <<<' + setopt printexitvalue + () { false; } + ' +1:PRINT_EXIT_VALUE option for anonymous function +?zsh: exit 1 setopt promptbang print -P ! -- cgit v1.2.3 From a4ff8e69570cbdb8e7d5bf1d5cc4000ffe63e15e Mon Sep 17 00:00:00 2001 From: "Barton E. Schaefer" Date: Fri, 20 Feb 2015 18:45:36 -0800 Subject: 34590: queue_signals() around more scopes that manipulate global state --- ChangeLog | 5 +++++ Src/exec.c | 4 +++- Src/text.c | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index fbf11386b..3f8e9e0e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-02-20 Barton E. Schaefer + + * 34590: Src/exec.c, Src/text.c: queue_signals() around more + scopes that manipulate global state + 2015-02-20 Peter Stephenson * 34587: Src/utils.c, Test/D07multibyte.ztst: ensure multibyte diff --git a/Src/exec.c b/Src/exec.c index 947b815f9..1a6149ad7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2337,6 +2337,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; @@ -2354,6 +2355,7 @@ setunderscore(char *str) *zunderscore = '\0'; underscoreused = 1; } + unqueue_signals(); } /* These describe the type of expansions that need to be done on the words @@ -5319,7 +5321,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; diff --git a/Src/text.c b/Src/text.c index 75f642c12..b58c2516d 100644 --- a/Src/text.c +++ b/Src/text.c @@ -173,6 +173,8 @@ getpermtext(Eprog prog, Wordcode c, int start_indent) { struct estate s; + queue_signals(); + if (!c) c = prog->prog; @@ -193,6 +195,9 @@ getpermtext(Eprog prog, Wordcode c, int start_indent) *tptr = '\0'; freeeprog(prog); /* mark as unused */ untokenize(tbuf); + + unqueue_signals(); + return tbuf; } @@ -206,6 +211,8 @@ getjobtext(Eprog prog, Wordcode c) struct estate s; + queue_signals(); + if (!c) c = prog->prog; @@ -224,6 +231,9 @@ getjobtext(Eprog prog, Wordcode c) *tptr = '\0'; freeeprog(prog); /* mark as unused */ untokenize(jbuf); + + unqueue_signals(); + return jbuf; } @@ -883,6 +893,9 @@ getredirs(LinkList redirs) ">", ">|", ">>", ">>|", "&>", "&>|", "&>>", "&>>|", "<>", "<", "<<", "<<-", "<<<", "<&", ">&", NULL /* >&- */, "<", ">" }; + + queue_signals(); + taddchr(' '); for (n = firstnode(redirs); n; incnode(n)) { Redir f = (Redir) getdata(n); @@ -970,4 +983,6 @@ getredirs(LinkList redirs) } } tptr--; + + unqueue_signals(); } -- cgit v1.2.3 From a2c579050fbf40eb9192f043e901e6b2eff3ab50 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 15 Apr 2015 09:44:19 +0100 Subject: 34887: Fix POSIX_BUILTINS with assignment. In the form var=val command special-builtin-or-func the var is restored after execution, unlike the case where "command" is absent. Clear up case in code that handles this. Add tests. --- ChangeLog | 6 ++++++ Src/exec.c | 28 +++++++++++++++++++++------- Test/E01options.ztst | 13 +++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index da61364d5..a5d8d7e85 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-04-15 Peter Stephenson + + * 34887: Src/exec, Test/E01options.ztst: assignments before + "command special-builtin/func" in POSIX_BUILTINS mode behave as + normal command. Tidy up case handling in code and add test. + 2015-04-13 Daniel Shahaf * users/20159: Completion/Zsh/Command/_zstyle: completion: zstyle: diff --git a/Src/exec.c b/Src/exec.c index 1a6149ad7..2ee37d09f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3384,14 +3384,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, diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 32135344f..5c453c80b 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -783,6 +783,19 @@ >print is a shell builtin ?(eval):8: command not found: print + # With non-special command: original value restored + # With special builtin: new value kept + # With special builtin preceeded by "command": original value restored. + (setopt posixbuiltins + FOO=val0 + FOO=val1 true; echo $FOO + FOO=val2 times 1>/dev/null 2>&1; echo $FOO + FOO=val3 command times 1>/dev/null 2>&1; echo $FOO) +0:POSIX_BUILTINS and restoring variables +>val0 +>val2 +>val2 + # PRINTEXITVALUE only works if shell input is coming from standard input. # Goodness only knows why. $ZTST_testdir/../Src/zsh -f <<<' -- cgit v1.2.3 From 3bf8cab82e32fc6903be995a5b6d7276307b22fe Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 15 Apr 2015 21:16:17 +0100 Subject: 34900: assignment before an "exec". Without POSXIBUILTIN: restore after, so we only get side effects. With POSXIBUILTIN: keep set variable --- ChangeLog | 5 +++++ Src/exec.c | 21 ++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 0d4521453..e41d8b893 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-04-15 Peter Stephenson + + * 34900: Src/exec.c: assignment before an "exec" with + a redirection, with and without POSIXBUILTINS. + 2015-04-15 Peter Stephenson * unposted: Src/math.c: rewrite last commit to look more diff --git a/Src/exec.c b/Src/exec.c index 2ee37d09f..2a8185cbc 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3305,6 +3305,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 @@ -3315,13 +3329,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); -- cgit v1.2.3 From e0cdf39fd9cbf056bd95eb08590addf8d40c155b Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 24 Apr 2015 16:34:51 +0100 Subject: 34955: save and restore list_pipe_job with its friends This is needed to stop source() messing up job control. --- ChangeLog | 6 ++++++ Src/exec.c | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 094a35b08..78803b6ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2015-04-24 Peter Stephenson + + * 34955: Src/exec.c: list_pipe_job needs saving and restoring + with some other similar variables in execlist() in order + to stop source() messing up job control. + 2015-04-24 Mikael Magnusson * Oliver: 34940: Completion/Zsh/Command/_fc: fix typo breaking diff --git a/Src/exec.c b/Src/exec.c index 2a8185cbc..60b79c6ea 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1146,7 +1146,7 @@ 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; zlong oldlineno; /* * ERREXIT only forces the shell to exit if the last command in a && @@ -1159,10 +1159,11 @@ 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; oldlineno = lineno; if (sourcelevel && unset(SHINSTDIN)) - pline_level = list_pipe = 0; + pline_level = list_pipe = list_pipe_job = 0; /* Loop over all sets of comands separated by newline, * * semi-colon or ampersand (`sublists'). */ @@ -1397,6 +1398,7 @@ sublist_done: } pline_level = old_pline_level; list_pipe = old_list_pipe; + list_pipe_job = old_list_pipe_job; lineno = oldlineno; if (dont_change_job) thisjob = cj; -- cgit v1.2.3 From df5115a741561d251b30926ce09486737da1e8da Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 28 Apr 2015 09:22:37 +0100 Subject: 34979: Preserve job text when doing shell job fix. This handles list_pipe_text in execlist() along with other variables that are saved and restored and, in the special case of source, cleared. --- ChangeLog | 2 ++ Src/exec.c | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 2611bdd48..c85d15a5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2015-04-28 Peter Stephenson + * 34979: Src/exec.c: preserve job text in shell job fix code. + * 34977: Src/zsh.mdd: more reliable test for whether preprocessor is GNU. diff --git a/Src/exec.c b/Src/exec.c index 60b79c6ea..31c80a74e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1147,6 +1147,7 @@ execlist(Estate state, int dont_change_job, int exiting) wordcode code; int ret, cj, csp, ltype; 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 && @@ -1160,10 +1161,16 @@ execlist(Estate state, int dont_change_job, int exiting) 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)) + 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'). */ @@ -1399,6 +1406,12 @@ 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; -- cgit v1.2.3 From c96a993d51e3e5958c8161d0dbe86cb43bcc52c1 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 28 Apr 2015 20:42:26 +0100 Subject: 34989: AUTO_CD needs to call cd --. Otherwise directories looking like options do the wrong thing. --- ChangeLog | 5 +++++ Src/exec.c | 1 + 2 files changed, 6 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index d53fe38dc..a5987b764 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2015-04-28 Peter Stephenson + + * 34989: Src/exec.c: AUTOCD needs to pass -- to cd to avoid + directory being treated as option. + 2015-04-28 Peter Stephenson * Jared Ahern: 34980: Completion/Unix/Command/_make: expanding diff --git a/Src/exec.c b/Src/exec.c index 31c80a74e..6a8b35a36 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2930,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; -- cgit v1.2.3