From ca6f4466e661f185d083e09c55fb93d16e0736cc Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sun, 22 Dec 2019 04:52:38 +0000 Subject: 45131: Make a function that redefines itself preserve its tracedness. This makes it easy to apply local tracing ('functions -T') to autoloadable functions that redefines themselves when first loaded. --- Src/exec.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Src/exec.c') diff --git a/Src/exec.c b/Src/exec.c index fac095d64..356e2974b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5320,6 +5320,12 @@ execfuncdef(Estate state, Eprog redir_prog) */ removetrapnode(signum); } + /* Is this function traced and redefining itself? */ + if (funcstack && funcstack->tp == FS_FUNC && + !strcmp(s, funcstack->name)) { + Shfunc old = ((Shfunc)shfunctab->getnode(shfunctab, s)); + shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL); + } shfunctab->addnode(shfunctab, ztrdup(s), shf); } } -- cgit v1.2.3 From 14ea665a903b26a658ef1d2ca974a9b48e09eff1 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 27 Feb 2020 20:42:16 +0000 Subject: users/24710: Fix job control problem with sudo. If we use kill to test for continued existence of a process group, we should check on failure that the error is ESRCH, as EPERM indicates the group still has memebers but running privileged so should be left alone. --- ChangeLog | 7 +++++++ Src/exec.c | 3 ++- Src/jobs.c | 14 ++++++++++---- Src/signals.c | 3 ++- 4 files changed, 21 insertions(+), 6 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 218970ff6..0b42a7cf8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2020-02-27 Peter Stephenson + + * users/24710: Src/exec.c, Src/jobs.c, Src/signals.c: when using + kill or killpg to test for continued existince of a process + group, check errono is ESRCH on failure as EPERM indicates + processes exist but under a different UID. + 2020-02-27 Jun-ichi Takimoto * 45492: Test/D02glob.ztst: skip test added by users/24633 diff --git a/Src/exec.c b/Src/exec.c index 50027654a..cf99051f0 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1036,7 +1036,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) } else if (thisjob != -1 && (flags & ESUB_PGRP)) { if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 || - killpg(jobtab[list_pipe_job].gleader, 0) == -1) { + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 && + errno == ESRCH)) { jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); setpgrp(0L, jobtab[list_pipe_job].gleader); diff --git a/Src/jobs.c b/Src/jobs.c index 0485f2c7c..8353f1152 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -283,7 +283,8 @@ handle_sub(int job, int fg) if ((cp = ((WIFEXITED(jn->procs->status) || WIFSIGNALED(jn->procs->status)) && - killpg(jn->gleader, 0) == -1))) { + (killpg(jn->gleader, 0) == -1 && + errno == ESRCH)))) { Process p; for (p = jn->procs; p->next; p = p->next); jn->gleader = p->pid; @@ -541,9 +542,13 @@ update_job(Job jn) /* is this job in the foreground of an interactive shell? */ if (mypgrp != pgrp && inforeground && - (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { + (jn->gleader == pgrp || + (pgrp > 1 && + (kill(-pgrp, 0) == -1 && errno == ESRCH)))) { if (list_pipe) { - if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) { + if (somestopped || (pgrp > 1 && + kill(-pgrp, 0) == -1 && + errno == ESRCH)) { attachtty(mypgrp); /* check window size and adjust if necessary */ adjustwinsize(0); @@ -2470,7 +2475,8 @@ bin_fg(char *name, char **argv, Options ops, int func) if ((jobtab[job].stat & STAT_SUPERJOB) && ((!jobtab[job].procs->next || (jobtab[job].stat & STAT_SUBLEADER) || - killpg(jobtab[job].gleader, 0) == -1)) && + (killpg(jobtab[job].gleader, 0) == -1 && + errno == ESRCH))) && jobtab[jobtab[job].other].gleader) attachtty(jobtab[jobtab[job].other].gleader); else diff --git a/Src/signals.c b/Src/signals.c index 96ff9e9b3..4adf03202 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -539,7 +539,8 @@ wait_for_processes(void) #endif if (WIFEXITED(status) && pn->pid == jn->gleader && - killpg(pn->pid, 0) == -1) { + killpg(pn->pid, 0) == -1 && + errno == ESRCH) { if (last_attached_pgrp == jn->gleader && !(jn->stat & STAT_NOSTTY)) { /* -- cgit v1.2.3 From e94e828efd4835a45bab4e37f9e3b16fd09b3f78 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 18 Mar 2020 19:42:08 +0000 Subject: 45583/0001: internal: Remove a redundant assignment. The value is overwritten five lines below, without being read in the interim. --- ChangeLog | 5 +++++ Src/exec.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index bea520140..3a28744c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-03-22 Daniel Shahaf + + * 45583/0001: Src/exec.c: internal: Remove a redundant + assignment. + 2020-03-20 Daniel Shahaf * unposted: Src/Builtins/rlimits.c: Deconfuse $EDITOR's balanced diff --git a/Src/exec.c b/Src/exec.c index bca051d4f..cd014ff38 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5166,7 +5166,6 @@ execfuncdef(Estate state, Eprog redir_prog) end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); - nprg = end - beg; sbeg = *state->pc++; nstrs = *state->pc++; npats = *state->pc++; -- cgit v1.2.3 From a3c6c5513dcfc25d952735449bf6da476d905184 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 18 Mar 2020 19:57:49 +0000 Subject: 45583/0007: WC_FUNCDEF: Add a placeholder element. --- ChangeLog | 3 +++ Config/version.mk | 4 ++-- Src/exec.c | 3 ++- Src/parse.c | 5 +++++ Src/text.c | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 6d40cc049..fe4102045 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2020-03-22 Daniel Shahaf + * 45583/0007: Config/version.mk, Src/exec.c, Src/parse.c, + Src/text.c: WC_FUNCDEF: Add a placeholder element. + * 45583/0006: Src/parse.c: internal: Add some comments for orientation. No functional change. diff --git a/Config/version.mk b/Config/version.mk index 6540e4b98..7ecfd35ba 100644 --- a/Config/version.mk +++ b/Config/version.mk @@ -27,5 +27,5 @@ # This must also serve as a shell script, so do not add spaces around the # `=' signs. -VERSION=5.8.0.1-dev -VERSION_DATE='February 15, 2020' +VERSION=5.8.0.2-dev +VERSION_DATE='March 19, 2020' diff --git a/Src/exec.c b/Src/exec.c index cd014ff38..3c3fcfa3e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5169,6 +5169,7 @@ execfuncdef(Estate state, Eprog redir_prog) sbeg = *state->pc++; nstrs = *state->pc++; npats = *state->pc++; + (void) *state->pc++; nprg = (end - state->pc); plen = nprg * sizeof(wordcode); @@ -6138,7 +6139,7 @@ stripkshdef(Eprog prog, char *name) int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; Patprog *pp; - pc += 5; + pc += 6; nprg = end - pc; plen = nprg * sizeof(wordcode); diff --git a/Src/parse.c b/Src/parse.c index 6ca6f33be..0111c25b6 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -173,6 +173,7 @@ struct heredocs *hdocs; * - followed by offset to first string * - followed by length of string table * - followed by number of patterns for body + * - followed by a placeholder * - followed by codes for body * - followed by strings for body * - if number of names is 0, followed by: @@ -1691,6 +1692,7 @@ par_funcdef(int *cmplx) ecadd(0); /* p + num + 2 */ ecadd(0); /* p + num + 3 */ ecadd(0); /* p + num + 4 */ + ecadd(0); /* p + num + 5 */ nocorrect = 0; incmdpos = 1; @@ -1730,6 +1732,7 @@ par_funcdef(int *cmplx) ecbuf[p + num + 2] = so - oecssub; ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */ ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */ + ecbuf[p + num + 5] = 0; ecbuf[p + 1] = num; /* "number of names" */ ecnpats = onp; @@ -2053,6 +2056,7 @@ par_simple(int *cmplx, int nr) ecadd(0); ecadd(0); ecadd(0); + ecadd(0); ecnfunc++; ecssub = so = ecsoffs; @@ -2108,6 +2112,7 @@ par_simple(int *cmplx, int nr) ecbuf[p + argc + 2] = so - oecssub; ecbuf[p + argc + 3] = ecsoffs - so; ecbuf[p + argc + 4] = ecnpats; + ecbuf[p + argc + 5] = 0; ecnpats = onp; ecssub = oecssub; diff --git a/Src/text.c b/Src/text.c index 69530ae79..4bf88f2e2 100644 --- a/Src/text.c +++ b/Src/text.c @@ -600,7 +600,7 @@ gettext2(Estate state) n->u._funcdef.end = end; n->u._funcdef.nargs = nargs; state->strs += *state->pc; - state->pc += 3; + state->pc += 4; } } else { state->strs = s->u._funcdef.strs; -- cgit v1.2.3 From 386d9ac8ff961b8f0333d09511e927ab31011658 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Thu, 19 Mar 2020 18:00:16 +0000 Subject: 45583/0008: Add the 'function -T' syntax. Config/version.mk was bumped in the previous commit. --- ChangeLog | 3 +++ Doc/Zsh/grammar.yo | 13 ++++++++++++- README | 7 +++++++ Src/exec.c | 8 +++++--- Src/parse.c | 11 +++++++++-- Test/E02xtrace.ztst | 25 +++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 6 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index fe4102045..8f39d7263 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2020-03-22 Daniel Shahaf + * 45583/0008: Doc/Zsh/grammar.yo, README, Src/exec.c, + Src/parse.c, Test/E02xtrace.ztst: Add the 'function -T' syntax. + * 45583/0007: Config/version.mk, Src/exec.c, Src/parse.c, Src/text.c: WC_FUNCDEF: Add a placeholder element. diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index e028c8512..fa0d72ff5 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -357,7 +357,7 @@ deliberately left unspecified, because historically there was a mismatch between the documented and implemented behaviours. Cf. 20076, 21734/21735, 45075.) ) findex(function) -xitem(tt(function) var(word) ... [ tt(()) ] [ var(term) ] tt({) var(list) tt(})) +xitem(tt(function) [ tt(-T) ] var(word) ... [ tt(()) ] [ var(term) ] tt({) var(list) tt(})) xitem(var(word) ... tt(()) [ var(term) ] tt({) var(list) tt(})) item(var(word) ... tt(()) [ var(term) ] var(command))( where var(term) is one or more newline or tt(;). @@ -367,6 +367,17 @@ are usually only useful for setting traps. The body of the function is the var(list) between the tt({) and tt(}). See noderef(Functions). +The options of tt(function) have the following meanings: + +startitem() +item(-T)( +Enable tracing for this function, as though with tt(functions -T). See the +documentation of the tt(-f) option to the tt(typeset) builtin, in +ifzman(zmanref(zshbuiltins))\ +ifnzman(noderef(Shell Builtin Commands)). +) +enditem() + If the option tt(SH_GLOB) is set for compatibility with other shells, then whitespace may appear between the left and right parentheses when there is a single var(word); otherwise, the parentheses will be treated as diff --git a/README b/README index 2bd5c2179..ae4f788bc 100644 --- a/README +++ b/README @@ -43,6 +43,13 @@ name of an external command. Now it may also be a shell function. Normal command word precedece rules apply, so if you have a function and a command with the same name, the function will be used. +The syntax "function -T { ... }" used to define a function named "-T". +It now defines an anonymous function with single-level tracing enabled --- +same as "function f { ... }; functions -T f; f", but without naming the +function. The syntax "function -T foo { ... }" is similarly affected: it +now defines a function "foo" with tracing enabled; previously it defined +two functions, named "-T" and "foo" (see the MULTI_FUNC_DEF option). + Incompatibilities since 5.7.1 ----------------------------- diff --git a/Src/exec.c b/Src/exec.c index 3c3fcfa3e..2b8e2167f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5157,23 +5157,25 @@ 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 signum, nprg, sbeg, nstrs, npats, do_tracing, len, plen, i, htok = 0, ret = 0; int anon_func = 0; Wordcode beg = state->pc, end; Eprog prog; Patprog *pp; LinkList names; + int tracing_flags; end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); sbeg = *state->pc++; nstrs = *state->pc++; npats = *state->pc++; - (void) *state->pc++; + do_tracing = *state->pc++; nprg = (end - state->pc); plen = nprg * sizeof(wordcode); len = plen + (npats * sizeof(Patprog)) + nstrs; + tracing_flags = do_tracing ? PM_TAGGED_LOCAL : 0; if (htok && names) { execsubst(names); @@ -5223,7 +5225,7 @@ execfuncdef(Estate state, Eprog redir_prog) shf = (Shfunc) zalloc(sizeof(*shf)); shf->funcdef = prog; - shf->node.flags = 0; + shf->node.flags = tracing_flags; /* No dircache here, not a directory */ shf->filename = ztrdup(scriptfilename); shf->lineno = diff --git a/Src/parse.c b/Src/parse.c index 0111c25b6..0342ee1f8 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -173,7 +173,7 @@ struct heredocs *hdocs; * - followed by offset to first string * - followed by length of string table * - followed by number of patterns for body - * - followed by a placeholder + * - followed by an integer indicating tracing status * - followed by codes for body * - followed by strings for body * - if number of names is 0, followed by: @@ -1670,6 +1670,7 @@ par_funcdef(int *cmplx) int oecused = ecused, num = 0, onp, p, c = 0; int so, oecssub = ecssub; zlong oldlineno = lineno; + int do_tracing = 0; lineno = 0; nocorrect = 1; @@ -1679,6 +1680,12 @@ par_funcdef(int *cmplx) p = ecadd(0); ecadd(0); /* p + 1 */ + if (tok == STRING && tokstr[0] == Dash && + tokstr[1] == 'T' && !tokstr[2]) { + ++do_tracing; + zshlex(); + } + while (tok == STRING) { if ((*tokstr == Inbrace || *tokstr == '{') && !tokstr[1]) { @@ -1732,7 +1739,7 @@ par_funcdef(int *cmplx) ecbuf[p + num + 2] = so - oecssub; ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */ ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */ - ecbuf[p + num + 5] = 0; + ecbuf[p + num + 5] = do_tracing; ecbuf[p + 1] = num; /* "number of names" */ ecnpats = onp; diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst index 795f7e616..d72b2d000 100644 --- a/Test/E02xtrace.ztst +++ b/Test/E02xtrace.ztst @@ -180,3 +180,28 @@ > # traced > echo inner >} + + function -T { echo traced anonymous function } + functions -- -T # no output +1:define traced function: anonymous function +?+(anon):0> echo traced anonymous function +>traced anonymous function + + function -T f { echo traced named function } + functions -- -T # no output + functions f + f +0:define traced function: named function +>f () { +> # traced +> echo traced named function +>} +?+f:0> echo traced named function +>traced named function + + function -T -T { echo trace function literally named "-T" } + -T +0:define traced function: parse test +?+-T:0> echo trace function literally named -T +>trace function literally named -T + -- cgit v1.2.3 From fb1aa3fe1e78ee1f521b64db062addf74ea9d263 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 26 May 2020 22:06:39 +0000 Subject: 45923 (with memory leak fixed, cf. 45924): zprof: Don't tally all anonymous functions as though they were a single function named "(anon)". Before: % zmodload zsh/zprof % () : % () : % zprof num calls time self name ----------------------------------------------------------------------------------- 1) 2 0.08 0.04 100.00% 0.08 0.04 100.00% (anon) After: % zmodload zsh/zprof % () : % () : % zprof num calls time self name ----------------------------------------------------------------------------------- 1) 1 0.04 0.04 50.45% 0.04 0.04 50.45% (anon) [:3] 2) 1 0.04 0.04 49.55% 0.04 0.04 49.55% (anon) [:2] --- ChangeLog | 6 ++++++ Src/Modules/zprof.c | 31 ++++++++++++++++++++++++++++--- Src/exec.c | 20 ++++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index d1927a089..57635bea7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2020-05-28 Daniel Shahaf + + * 45923 (with memory leak fixed, cf. 45924): Src/Modules/zprof.c, + Src/exec.c: zprof: Don't tally all anonymous functions as though + they were a single function named "(anon)". + 2020-05-23 Peter Stephenson * 45900: Src/lex.c, Test/D04parameter.ztst: Fix issues with diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index bc97771c0..56cdab888 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -213,7 +213,25 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) return 0; } -/**/ +static char * +name_for_anonymous_function(char *name) +{ + char lineno[DIGBUFSIZE]; + char *parts[7]; + + convbase(lineno, funcstack[0].flineno, 10); + + parts[0] = name; + parts[1] = " ["; + parts[2] = funcstack[0].filename ? funcstack[0].filename : ""; + parts[3] = ":"; + parts[4] = lineno; + parts[5] = "]"; + parts[6] = NULL; + + return sepjoin(parts, "", 1); +} + static int zprof_wrapper(Eprog prog, FuncWrap w, char *name) { @@ -224,12 +242,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) struct timeval tv; struct timezone dummy; double prev = 0, now; + char *name_for_lookups; + + if (is_anonymous_function_name(name)) { + name_for_lookups = name_for_anonymous_function(name); + } else { + name_for_lookups = name; + } if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) { active = 1; - if (!(f = findpfunc(name))) { + if (!(f = findpfunc(name_for_lookups))) { f = (Pfunc) zalloc(sizeof(*f)); - f->name = ztrdup(name); + f->name = ztrdup(name_for_lookups); f->calls = 0; f->time = f->self = 0.0; f->next = calls; diff --git a/Src/exec.c b/Src/exec.c index 2b8e2167f..29f4fc5ca 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5147,10 +5147,26 @@ exectime(Estate state, UNUSED(int do_exec)) return lastval; } -/* Define a shell function */ - +/* The string displayed in lieu of the name of an anonymous function (in PS4, + * zprof output, etc) + */ static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; +/* + * Take a function name argument and return true iff it is equal to the string + * used for the names of anonymous functions, "(anon)". + * + * Note that it's possible to define a named function literally called "(anon)" + * (though I doubt anyone would ever do that). + */ +/**/ +int is_anonymous_function_name(const char *name) +{ + return !strcmp(name, ANONYMOUS_FUNCTION_NAME); +} + +/* Define a shell function */ + /**/ static int execfuncdef(Estate state, Eprog redir_prog) -- cgit v1.2.3 From d7e90f1c7c08ab88c6e0e7c68e64f0e1ea51893d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 8 Jun 2020 20:52:53 +0100 Subject: users/24909: Don't clean up special file list too early. When running a function, remove special files used for substitution after the function has run rather than before. --- ChangeLog | 5 +++++ Src/exec.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 42060024b..979fa729b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-06-08 Peter Stephenson + + * uwers/24909: Src/exec.c: Don't clean up files used for + substitution until after function has run. + 2020-06-08 Doron Behar * gitlab !14 (fixup): Completion/Linux/Command/_modutils: diff --git a/Src/exec.c b/Src/exec.c index 29f4fc5ca..c72d485b2 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3969,8 +3969,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (is_shfunc) { /* It's a shell function */ - pipecleanfilelist(filelist, 0); execshfunc((Shfunc) hn, args); + pipecleanfilelist(filelist, 0); } else { /* It's a builtin */ LinkList assigns = (LinkList)0; -- cgit v1.2.3 From 3df604a4be76db6ccf285eda6b4b3f5f6ec7497d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 9 Jun 2020 18:04:46 +0100 Subject: 46026: Add CLOBBER_EMPTY option. --- ChangeLog | 5 +++++ Doc/Zsh/options.yo | 16 ++++++++++++++++ Src/exec.c | 27 ++++++++++++++++++++++----- Src/options.c | 1 + Src/zsh.h | 1 + Test/A04redirect.ztst | 14 ++++++++++++++ 6 files changed, 59 insertions(+), 5 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 979fa729b..3addf253f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-06-09 Peter Stephenson + + * 46026: Doc/Zsh/options.yo, Src/exec.c, Src/options.c, + Src/zsh.h, Test/A04redirect.ztst: Add CLOBBER_EMPTY option. + 2020-06-08 Peter Stephenson * uwers/24909: Src/exec.c: Don't clean up files used for diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 2b7637ff4..6da68308f 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -1168,6 +1168,22 @@ If the option is not set, and the option tt(APPEND_CREATE) is also not set, `tt(>>!)' or `tt(>>|)' must be used to create a file. If either option is set, `tt(>>)' may be used. ) +pindex(CLOBBER_EMPTY) +pindex(NO_CLOBBER_EMPTY) +pindex(CLOBBEREMPTY) +pindex(NOCLOBBEREMPTY) +cindex(clobbering, of empty files) +cindex(file clobbering, of empty files) +item(tt(CLOBBER_EMPTY))( +This option is only used if the option tt(CLOBBER) is not set: note that +it is set by default. + +If this option is set, then regular files of zero length may be +ovewritten (`clobbered'). Note that it is possible another process +has written to the file between this test and use of the file by +the current process. This option should therefore not be used in +cases where files to be clobbered may be written to asynchronously. +) pindex(CORRECT) pindex(NO_CORRECT) pindex(NOCORRECT) diff --git a/Src/exec.c b/Src/exec.c index c72d485b2..045b5d2b9 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2143,14 +2143,15 @@ clobber_open(struct redir *f) { struct stat buf; int fd, oerrno; + char *ufname = unmeta(f->name); /* If clobbering, just open. */ if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) - return open(unmeta(f->name), + return open(ufname, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); /* If not clobbering, attempt to create file exclusively. */ - if ((fd = open(unmeta(f->name), + if ((fd = open(ufname, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) return fd; @@ -2158,11 +2159,27 @@ clobber_open(struct redir *f) * Try opening, and if it's a regular file then close it again * * because we weren't supposed to open it. */ oerrno = errno; - if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { - if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) - return fd; + if ((fd = open(ufname, O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf)) { + if (!S_ISREG(buf.st_mode)) + return fd; + /* + * If CLOBBER_EMPTY is in effect and the file is empty, + * we are allowed to re-use it. + * + * Note: there is an intrinsic race here because another + * process can write to this file at any time. The only fix + * would be file locking, which we wish to avoid in basic + * file operations at this level. This would not be + * fixed. just additionally complicated, by re-opening the + * file and truncating. + */ + if (isset(CLOBBEREMPTY) && buf.st_size == 0) + return fd; + } close(fd); } + errno = oerrno; return -1; } diff --git a/Src/options.c b/Src/options.c index 7586d21d2..fba021e7d 100644 --- a/Src/options.c +++ b/Src/options.c @@ -114,6 +114,7 @@ static struct optname optns[] = { {{NULL, "checkjobs", OPT_EMULATE|OPT_ZSH}, CHECKJOBS}, {{NULL, "checkrunningjobs", OPT_EMULATE|OPT_ZSH}, CHECKRUNNINGJOBS}, {{NULL, "clobber", OPT_EMULATE|OPT_ALL}, CLOBBER}, +{{NULL, "clobberempty", 0}, CLOBBEREMPTY}, {{NULL, "combiningchars", 0}, COMBININGCHARS}, {{NULL, "completealiases", 0}, COMPLETEALIASES}, {{NULL, "completeinword", 0}, COMPLETEINWORD}, diff --git a/Src/zsh.h b/Src/zsh.h index 1f2d774a1..ed123f2b9 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2378,6 +2378,7 @@ enum { CHECKJOBS, CHECKRUNNINGJOBS, CLOBBER, + CLOBBEREMPTY, APPENDCREATE, COMBININGCHARS, COMPLETEALIASES, diff --git a/Test/A04redirect.ztst b/Test/A04redirect.ztst index d60519064..993138e7d 100644 --- a/Test/A04redirect.ztst +++ b/Test/A04redirect.ztst @@ -708,3 +708,17 @@ cat <&$testfd 0:Regression test for here document with fd declarator > This is, in some sense, a here document. + + (setopt noclobber clobberempty + rm -f foo + touch foo + print Works >foo + cat foo + print Works not >foo + # Make sure the file was not harmed + cat foo + ) +0:CLOBBER_EMPTY +>Works +>Works +?(eval):6: file exists: foo -- cgit v1.2.3 From cf134c15a09c9be7fac7628372a8f897ec84f9fa Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 3 Jul 2020 13:38:58 +0000 Subject: 46175/0003: Fix the RM_STAR_SILENT bug from the parent commit. --- ChangeLog | 3 +++ Src/exec.c | 2 +- Test/E01options.ztst | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 51c7fc00b..f0c4a9f5f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2020-07-05 Daniel Shahaf + * 46175/0003: Src/exec.c, Test/E01options.ztst: Fix the + RM_STAR_SILENT bug from the parent commit. + * 46175/0002: Test/E01options.ztst: Add a regression test for 46169: the RM_STAR_SILENT logic processes the current directory rather than the root directory. diff --git a/Src/exec.c b/Src/exec.c index 045b5d2b9..7120a2c34 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3401,7 +3401,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, int rmall; s[l - 2] = 0; - rmall = checkrmall(s); + rmall = checkrmall(l == 2 ? "/" : s); s[l - 2] = t; if (!rmall) { diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 053affeed..c59509f2e 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -1447,6 +1447,6 @@ F:If this test fails at the first unsetopt, refer to P01privileged.ztst. ZTST_skip="the zsh/zpty module is not available" fi BEL=$'\a' -0qf:RM_STAR_SILENT +0q:RM_STAR_SILENT *>zsh: sure you want to delete all 15 files in ${PWD:h}/options.tmp \[yn\]\? ${BEL} *>zsh: sure you want to delete all <-> files in / \[yn\]\? ${BEL} -- cgit v1.2.3 From 41e318727e6fdca70b28431a10a60a73aa6f43bf Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Mon, 27 Jul 2020 14:56:09 +0200 Subject: 46268: suppress a useless compiler warning around nice() From nice(2): To detect an error, set errno to 0 before the call, and check whether it is nonzero after nice() returns -1. --- Src/exec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Src/exec.c') diff --git a/Src/exec.c b/Src/exec.c index 7120a2c34..ecad923de 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2789,8 +2789,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, /* Check if we should run background jobs at a lower priority. */ if ((how & Z_ASYNC) && isset(BGNICE)) { errno = 0; - nice(5); - if (errno) + if (nice(5) == -1 && errno) zwarn("nice(5) failed: %e", errno); } #endif /* HAVE_NICE */ -- cgit v1.2.3 From 326d9c203b3980c0f841bc62b06e37134c6e51ea Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 25 Jan 2021 21:34:50 -0800 Subject: Allow more scripts without #! This change modifies the zsh binary safety check surrounding execve() so it can run shell scripts having concatenated binary content. We're using the same safety check as FreeBSD /bin/sh [1]. POSIX was recently revised to require this behavior: "The input file may be of any type, but the initial portion of the file intended to be parsed according to the shell grammar (XREF to XSH 2.10.2 Shell Grammar Rules) shall consist of characters and shall not contain the NUL character. The shell shall not enforce any line length limits." "Earlier versions of this standard required that input files to the shell be text files except that line lengths were unlimited. However, that was overly restrictive in relation to the fact that shells can parse a script without a trailing newline, and in relation to a common practice of concatenating a shell script ending with an 'exit' or 'exec $command' with a binary data payload to form a single-file self-extracting archive." [2] [3] One example use case of such scripts, is the Cosmopolitan C Library [4] which configuse the GNU Linker to output a polyglot shell+binary format that runs on Linux / Mac / Windows / FreeBSD / OpenBSD. [1] https://github.com/freebsd/freebsd-src/commit/9a1cd363318b7e9e70ef6af27d1675b371c16b1a [2] http://austingroupbugs.net/view.php?id=1250 [3] http://austingroupbugs.net/view.php?id=1226#c4394 [4] https://justine.lol/cosmopolitan/index.html --- ChangeLog | 6 ++++++ Src/exec.c | 27 +++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 1648ea243..c89fce748 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-02-16 Peter Stephenson + + * 47876: Justtine Tunney: Src/exec.c: Add more cases where + shell scripts can be recognised from the first line as + described by POSIX. + 2021-02-16 Lawrence Velázquez * 47830: Doc/Zsh/contrib.yo, README: Fix some documentation typos diff --git a/Src/exec.c b/Src/exec.c index ecad923de..2301f85ad 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -547,10 +547,29 @@ zexecve(char *pth, char **argv, char **newenvp) } } } else if (eno == ENOEXEC) { - for (t0 = 0; t0 != ct; t0++) - if (!execvebuf[t0]) - break; - if (t0 == ct) { + /* Perform binary safety check on classic shell * + * scripts (shebang wasn't introduced until UNIX * + * Seventh Edition). POSIX says we shall allow * + * execution of scripts with concatenated binary * + * and suggests checking a line exists before the * + * first NUL character with a lowercase letter or * + * expansion. This is consistent with FreeBSD sh. */ + int isbinary, hasletter; + if (!(ptr2 = memchr(execvebuf, '\0', ct))) { + isbinary = 0; + } else { + isbinary = 1; + hasletter = 0; + for (ptr = execvebuf; ptr < ptr2; ptr++) { + if (islower(*ptr) || *ptr == '$' || *ptr == '`') + hasletter = 1; + if (hasletter && *ptr == '\n') { + isbinary = 0; + break; + } + } + } + if (!isbinary) { argv[-1] = "sh"; winch_unblock(); execve("/bin/sh", argv - 1, newenvp); -- cgit v1.2.3 From f7a417388c73e7cfefb8e93fa8beba193fb1dd1f Mon Sep 17 00:00:00 2001 From: "brian m. carlson" Date: Sun, 3 Jan 2021 18:23:00 -0600 Subject: 47794: exec: run final pipeline command in a subshell in sh mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zsh typically runs the final command in a pipeline in the main shell instead of a subshell. However, POSIX specifies that all commands in a pipeline run in a subshell, but permits zsh's behavior as an extension. The default /bin/sh implementations on various Linux distros and the BSDs always use a subshell for all components of a pipeline. Since zsh may be used as /bin/sh in some cases (such as macOS Catalina), it makes sense to have the common sh behavior when emulating sh, so do that by checking for being the final item of a multi-item pipeline and creating a subshell in that case. From the comment above execpline(), we know the following: last1 is a flag that this command is the last command in a shell that is about to exit, so we can exec instead of forking. It gets passed all the way down to execcmd() which actually makes the decision. A 0 is always passed if the command is not the last in the pipeline. […] If last1 is zero but the command is at the end of a pipeline, we pass 2 down to execcmd(). So there are three cases to consider in this code: • last1 is 0, which means we are not at the end of a pipeline, in which case we should not change behavior. • last1 is 1, which means we are effectively running in a subshell, because nothing that happens due to the exec is going to affect the actual shell, since it will have been replaced. So there is nothing to do here. • last1 is 2, which means our command is at the end of the pipeline, so in sh mode we should create a subshell by forking. input is nonzero if the input to this process is a pipe that we've opened. At the end of a multi-stage pipeline, it will necessarily be nonzero. Note that several of the tests may appear bizarre, since most developers do not place useless variable assignments directly at the end of a pipeline. However, as the function tests demonstrate, there are cases where assignments may occur when a shell function is used at the end of a command. The remaining assignment tests simply test additional cases, such as the use of local, that would otherwise be untested. --- ChangeLog | 4 ++++ README | 4 ++++ Src/exec.c | 10 ++++++---- Test/B07emulate.ztst | 22 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 0e8a33f7c..c98923055 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,10 @@ 2021-04-10 dana + * brian m. carlson: 47794: README, Src/exec.c, + Test/B07emulate.ztst: exec: run final pipeline command in a + subshell in sh mode + * unposted (see 48415): README: Document incompatibility caused by workers/43928 diff --git a/README b/README index f47adfe7c..5e3b8cc59 100644 --- a/README +++ b/README @@ -95,6 +95,10 @@ right thing, but this should be viewed as an unsupported hack.) The XTRACE option is now disabled while running user-defined completion widgets. See NEWS. +emulate sh: When zsh emulates sh, the final command in a pipeline is now run in +a subshell. This differs from the behavior in the native (zsh) mode, but is +consistent with most other sh implementations. + Incompatibilities between 5.7.1 and 5.8 --------------------------------------- diff --git a/Src/exec.c b/Src/exec.c index 2301f85ad..6f09e0d9f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2901,11 +2901,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, pushnode(args, dupstring("fg")); } - if ((how & Z_ASYNC) || output) { + if ((how & Z_ASYNC) || output || + (last1 == 2 && input && EMULATION(EMULATE_SH))) { /* - * If running in the background, or not the last command in a - * pipeline, we don't need any of the rest of this function to - * affect the state in the main shell, so fork immediately. + * If running in the background, not the last command in a + * pipeline, or the last command in a multi-stage pipeline + * in sh mode, we don't need any of the rest of this function + * to affect the state in the main shell, so fork immediately. * * In other cases we may need to process the command line * a bit further before we make the decision. diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst index 7b1592fa9..45c39b51d 100644 --- a/Test/B07emulate.ztst +++ b/Test/B07emulate.ztst @@ -276,3 +276,25 @@ F:Some reserved tokens are handled in alias expansion 0:--emulate followed by other options >yes >no + + emulate sh -c ' + foo () { + VAR=foo && + echo $VAR | bar && + echo "$VAR" + } + bar () { + tr f b && + VAR="$(echo bar | tr r z)" && + echo "$VAR" + } + foo + ' + emulate sh -c 'func() { echo | local def="abc"; echo $def;}; func' + emulate sh -c 'abc="def"; echo | abc="ghi"; echo $abc' +0:emulate sh uses subshell for last pipe entry +>boo +>baz +>foo +> +>def -- cgit v1.2.3 From cf5c4828d1cdfd79e369a6b3323466bc961851c4 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 16 May 2021 19:51:11 -0700 Subject: 48857: declare "volatile" all globals that may be modified by signal handlers --- ChangeLog | 6 ++++++ Src/builtin.c | 16 +++++++--------- Src/exec.c | 8 +++++--- Src/loop.c | 2 +- Src/makepro.awk | 2 +- Src/params.c | 6 ++++-- Src/signals.c | 16 ++++++++-------- 7 files changed, 32 insertions(+), 24 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 6f8520db7..89b469c0f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-05-16 Bart Schaefer + + * 48857: Src/builtin.c, Src/exec.c, Src/loop.c, Src/makepro.awk, + Src/params.c, Src/signals.c: declare as "volatile" all globals + that may be modified by signal handlers; recognize in makepro.awk + 2021-05-16 Oliver Kiddle * Jörg Sommer: users/26649: Completion/Unix/Command/_rake: diff --git a/Src/builtin.c b/Src/builtin.c index a29eb49e4..a16fddcb7 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5635,13 +5635,16 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun */ /**/ -mod_export int -exit_pending; +mod_export volatile int exit_pending; /* Shell level at which we exit if exit_pending */ /**/ -mod_export int -exit_level; +mod_export volatile int exit_level; + +/* we have printed a 'you have stopped (running) jobs.' message */ + +/**/ +mod_export volatile int stopmsg; /* break, bye, continue, exit, logout, return -- most of these take * * one numeric argument, and the other (logout) is related to return. * @@ -5733,11 +5736,6 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) return 0; } -/* we have printed a 'you have stopped (running) jobs.' message */ - -/**/ -mod_export int stopmsg; - /* check to see if user has jobs running/stopped */ /**/ diff --git a/Src/exec.c b/Src/exec.c index 6f09e0d9f..49ff88b80 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -84,7 +84,7 @@ int nohistsave; /* error flag: bits from enum errflag_bits */ /**/ -mod_export int errflag; +mod_export volatile int errflag; /* * State of trap return value. Value is from enum trap_state. @@ -122,7 +122,7 @@ int subsh; /* != 0 if we have a return pending */ /**/ -mod_export int retflag; +mod_export volatile int retflag; /**/ long lastval2; @@ -1268,7 +1268,9 @@ execsimple(Estate state) } else { int q = queue_signal_level(); dont_queue_signals(); - if (code == WC_FUNCDEF) + if (errflag) + lv = errflag; + else if (code == WC_FUNCDEF) lv = execfuncdef(state, NULL); else lv = (execfuncs[code - WC_CURSH])(state, 0); diff --git a/Src/loop.c b/Src/loop.c index aa733a2cb..db5b3e097 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -43,7 +43,7 @@ mod_export int contflag; /* # of break levels */ /**/ -mod_export int breaks; +mod_export volatile int breaks; /**/ int diff --git a/Src/makepro.awk b/Src/makepro.awk index 226d3f96b..f69660531 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -79,7 +79,7 @@ BEGIN { break } sub(/^ */, "", line) - match(line, /^((const|enum|mod_export|static|struct|union) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/) + match(line, /^((const|enum|mod_export|static|struct|union|volatile) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/) dtype = substr(line, 1, RLENGTH) sub(/ *$/, "", dtype) if(" " dtype " " ~ / static /) diff --git a/Src/params.c b/Src/params.c index 20dfb5b5f..4f6b361f9 100644 --- a/Src/params.c +++ b/Src/params.c @@ -98,8 +98,10 @@ char *ifs, /* $IFS */ *pwd; /* $PWD */ /**/ -mod_export -zlong lastval, /* $? */ +mod_export volatile zlong + lastval; /* $? */ +/**/ +mod_export zlong mypid, /* $$ */ lastpid, /* $! */ zterm_columns, /* $COLUMNS */ diff --git a/Src/signals.c b/Src/signals.c index 4adf03202..2c540f38f 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -53,7 +53,7 @@ mod_export Eprog siglists[VSIGCOUNT]; /* Total count of trapped signals */ /**/ -mod_export int nsigtrapped; +mod_export volatile int nsigtrapped; /* Running an exit trap? */ @@ -72,20 +72,20 @@ static int exit_trap_posix; /* Variables used by signal queueing */ /**/ -mod_export int queueing_enabled, queue_front, queue_rear; +mod_export volatile int queueing_enabled, queue_front, queue_rear; /**/ mod_export int signal_queue[MAX_QUEUE_SIZE]; /**/ mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; #ifdef DEBUG /**/ -mod_export int queue_in; +mod_export volatile int queue_in; #endif /* Variables used by trap queueing */ /**/ -mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear; +mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear; /**/ mod_export int trap_queue[MAX_QUEUE_SIZE]; @@ -672,9 +672,9 @@ zhandler(int sig) if ((isset(PRIVILEGED) || isset(RESTRICTED)) && isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL)) zexit(SIGINT, ZEXIT_SIGNAL); + errflag |= ERRFLAG_INT; if (list_pipe || chline || simple_pline) { breaks = loops; - errflag |= ERRFLAG_INT; inerrflush(); check_cursh_sig(SIGINT); } @@ -1266,19 +1266,19 @@ unqueue_traps(void) /* Are we already executing a trap? */ /**/ -int intrap; +volatile int intrap; /* Is the current trap a function? */ /**/ -int trapisfunc; +volatile int trapisfunc; /* * If the current trap is not a function, at what function depth * did the trap get called? */ /**/ -int traplocallevel; +volatile int traplocallevel; /* * sig is the signal number. -- cgit v1.2.3 From 0a80579ed18b4004a99b5dc062ce874c0bdc3201 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 26 Aug 2021 09:46:39 +0100 Subject: 49307 with doc update: POSIX_TRAPS fix. With POSIX_TRAPS set, an ignored signal stays ignored when entering a subshell. --- ChangeLog | 6 ++++++ Doc/Zsh/grammar.yo | 4 +++- Doc/Zsh/options.yo | 13 +++++++++---- Src/exec.c | 3 ++- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 428998717..f558bd47c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-08-26 Peter Stephenson + + * 49307 with documentation updated: Src/exec, + Doc/Zsh/grammar.yo, Doc/Zsh/options.yo: With POSIX_TRAPS, + ignored signals stay that way in subshell. + 2021-08-24 Peter Stephenson * 49297 with quoting updated: Src/Modules/files.c: check diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo index 2eb2018d2..f8f4ada86 100644 --- a/Doc/Zsh/grammar.yo +++ b/Doc/Zsh/grammar.yo @@ -288,7 +288,9 @@ for each selection until a break or end-of-file is encountered. cindex(subshell) item(tt(LPAR()) var(list) tt(RPAR()))( Execute var(list) in a subshell. Traps set by the tt(trap) builtin -are reset to their default values while executing var(list). +are reset to their default values while executing var(list); an +exception is that ignored signals will continue to be ignored +if the option tt(POSIXTRAPS) is set. ) item(tt({) var(list) tt(}))( Execute var(list). diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 546b16b65..4a8b85e08 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -2330,10 +2330,15 @@ When this option is set, the usual zsh behaviour of executing traps for tt(EXIT) on exit from shell functions is suppressed. In that case, manipulating tt(EXIT) traps always alters the global trap for exiting the shell; the tt(LOCAL_TRAPS) option is -ignored for the tt(EXIT) trap. Furthermore, a tt(return) statement -executed in a trap with no argument passes back from the function the -value from the surrounding context, not from code executed within the -trap. +ignored for the tt(EXIT) trap. + +Also, a tt(return) statement executed in a trap with no argument passes +back from the function the value from the surrounding context, not from +code executed within the trap. + +Furthermore, if a trap is set to be ignored, this state persists when +a subshell is entered. Without the option, the trap would be reset to +its default state at this point. ) pindex(SH_FILE_EXPANSION) pindex(NO_SH_FILE_EXPANSION) diff --git a/Src/exec.c b/Src/exec.c index 49ff88b80..79d8064b6 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1033,7 +1033,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) if (!(flags & ESUB_KEEPTRAP)) for (sig = 0; sig < SIGCOUNT; sig++) - if (!(sigtrapped[sig] & ZSIG_FUNC)) + if (!(sigtrapped[sig] & ZSIG_FUNC) && + !(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED))) unsettrap(sig); monitor = isset(MONITOR); job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS); -- cgit v1.2.3 From db46c9cd5844240fb6015666c8e2a12a0a3a6ead Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 9 Sep 2021 20:05:39 +0100 Subject: 49353: Fix comments in sourced file. If the file was sourced from an interactive shell with INTERACTIVE_COMMENTS not set, comments were not parsed. Note there is a remaining edge case where the sourced file is in fact entered at the comment line. --- ChangeLog | 5 +++++ Src/exec.c | 2 +- Test/A01grammar.ztst | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index b295af2c9..878299d12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-09-09 Peter Stephenson + + * 49353: Src/exe.c, Test/A01grammar.ztst: In sourced file, + always parse comments normally, ignoring INTERACTIVE_COMMENTS. + 2021-09-08 Bart Schaefer * 49391: Doc/Zsh/zle.yo: document default bracketed-paste bindings diff --git a/Src/exec.c b/Src/exec.c index 79d8064b6..1f23a862d 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4622,7 +4622,7 @@ getoutput(char *cmd, int qt) char *s; int onc = nocomments; - nocomments = (interact && unset(INTERACTIVECOMMENTS)); + nocomments = (interact && !sourcelevel && unset(INTERACTIVECOMMENTS)); prog = parse_string(cmd, 0); nocomments = onc; diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 88fc8606e..c114ff103 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -944,3 +944,12 @@ F:Note that the behaviour of 'exit' inside try-list inside a function is unspeci if : ${(e)a}; then echo x; fi 1:Status on bad substitution in if without else ?(eval):2: bad substitution + + echo 'echo foo # comment + echo $( + echo bar # comment + )' >source_comments.zsh + $ZTST_testdir/../Src/zsh -f -o extendedglob -is -c '. ./source_comments.zsh' +0:Comments should be handled in command subst in interactively sourced files +>foo +>bar -- cgit v1.2.3 From 356dcb20cef387a5eea5f8fcbfe123b24e3bb928 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Fri, 12 Nov 2021 23:33:37 +0200 Subject: github #82: Fix typos --- ChangeLog | 11 +++++++++++ Completion/BSD/Command/_kdump | 2 +- Completion/BSD/Command/_ktrace | 2 +- Completion/Debian/Command/_aptitude | 2 +- Completion/Linux/Command/_modutils | 2 +- Completion/Linux/Command/_sysstat | 2 +- Completion/Mandriva/Command/_urpmi | 2 +- Completion/Redhat/Command/_dnf | 2 +- Completion/Unix/Command/_ansible | 2 +- Completion/Unix/Command/_gcc | 4 ++-- Etc/FAQ.yo | 4 ++-- Etc/NEWS-4.3 | 2 +- Functions/Chpwd/cdr | 2 +- Functions/Misc/regexp-replace | 2 +- Functions/Newuser/zsh-newuser-install | 2 +- NEWS | 2 +- Src/Zle/compmatch.c | 8 ++++---- Src/exec.c | 2 +- Src/math.c | 4 ++-- Test/A01grammar.ztst | 2 +- Test/B12limit.ztst | 2 +- 21 files changed, 37 insertions(+), 26 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index de6bbb08b..1dfe2e39a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2021-11-12 Oliver Kiddle + * github #82: Dimitris Apostolou: Completion/BSD/Command/_kdump, + Completion/Redhat/Command/_dnf, Completion/BSD/Command/_ktrace, + Completion/Linux/Command/_modutils, Test/A01grammar.ztst, + Completion/Linux/Command/_sysstat, Functions/Chpwd/cdr, + Completion/Unix/Command/_ansible, Completion/Unix/Command/_gcc, + Completion/Mandriva/Command/_urpmi, Etc/NEWS-4.3, + Completion/Debian/Command/_aptitude, Etc/FAQ.yo, + Functions/Newuser/zsh-newuser-install, NEWS, + Functions/Misc/regexp-replace, Src/Zle/compmatch.c, + Src/exec.c, Src/math.c, Test/B12limit.ztst: fix typos + * Marlon: 49572: Completion/Base/Completer/_expand, Test/Y01completion.ztst: Let _expand preserve array form w/out zstyle glob diff --git a/Completion/BSD/Command/_kdump b/Completion/BSD/Command/_kdump index 946296a75..e5c7c4cce 100644 --- a/Completion/BSD/Command/_kdump +++ b/Completion/BSD/Command/_kdump @@ -30,7 +30,7 @@ local args=( '-f+[use the specified file (- for stdin)]:dump file:_files' '-l[loop reading the trace file]' '-m+[maximum I/O bytes to display]:max data bytes:' - '-n[supress ad hoc translations]' + '-n[suppress ad hoc translations]' '-p+[show output only for the specified pid]: :_kdump_pid' '(-E -T)-R[display relative timestamps]' '(-E -R )-T[display absolute timestamps]' diff --git a/Completion/BSD/Command/_ktrace b/Completion/BSD/Command/_ktrace index 13c11f15d..9613ba2bf 100644 --- a/Completion/BSD/Command/_ktrace +++ b/Completion/BSD/Command/_ktrace @@ -4,7 +4,7 @@ local args=( '-a[append to the trace file]' '(*)-C[disable tracing on all user owned processes or all processes if executed by root]' '-c[clear the trace points]' - '-d[trace current decendants]' + '-d[trace current descendants]' '-f+[log trace to specified file]:trace file:_files' '(-p *)-g+[enable/disable tracing on specified process group]:pgid:_pgids' '-i[inherit trace flags on future children]' diff --git a/Completion/Debian/Command/_aptitude b/Completion/Debian/Command/_aptitude index 91d233f11..5b10adb80 100644 --- a/Completion/Debian/Command/_aptitude +++ b/Completion/Debian/Command/_aptitude @@ -15,7 +15,7 @@ _arguments -C \ '(-F --display-format)'{-F,--display-format}'[specify output format for search command]:format:->format-strings' \ '--group-by=[control how the versions command groups its output]:grouping:(archive auto none package source-package source-version)' \ '--log-file=[specify output log file]:file:_files' \ - '*--log-level=[specify mimimum message level to log]:level:compadd -o nosort off fatal error warn info debug trace' \ + '*--log-level=[specify minimum message level to log]:level:compadd -o nosort off fatal error warn info debug trace' \ '--log-resolver[set some standard log levels related to the resolver]' \ '(--allow-new-installs)--no-new-installs[prevent safe-upgrade from installing any new packages]' \ '(--allow-new-upgrades)--no-new-upgrades[prevent safe-upgrade from upgrading packages regardless]' \ diff --git a/Completion/Linux/Command/_modutils b/Completion/Linux/Command/_modutils index 1205f2506..3e46130a2 100644 --- a/Completion/Linux/Command/_modutils +++ b/Completion/Linux/Command/_modutils @@ -105,7 +105,7 @@ _modutils() { loaded-modules|loadable-modules) if [[ -r /proc/modules ]]; then loaded_modules=(${${(f)"$(tp != CPAT_ANY || wp->tp != CPAT_ANY) @@ -1496,7 +1496,7 @@ pattern_match_restrict(Cpattern p, Cpattern wp, convchar_t *wsc, int wsclen, * characters. We're matching two patterns against * one another to generate a character to insert. * This is a bit too psychedelic, so I'm going to - * bale out now. See you on the ground. + * bail out now. See you on the ground. */ return 0; } @@ -1564,7 +1564,7 @@ pattern_match(Cpattern p, char *s, Cpattern wp, char *ws) c = unmeta_one(s, &len); /* * If either is "?", they match each other; no further tests. - * Apply this even if the character wasn't convertable; + * Apply this even if the character wasn't convertible; * there's no point trying to be clever in that case. */ if (p->tp != CPAT_ANY || wp->tp != CPAT_ANY) @@ -1934,7 +1934,7 @@ bld_line(Cmatcher mp, ZLE_STRING_T line, char *mword, char *word, * This is the nightmare case: we have line and * and word matchers and some pattern which restricts * the value on the line without us knowing exactly - * what it is. Despatch to the special function + * what it is. Dispatch to the special function * for that. */ if (mp && !mp->flags && mp->wlen <= wlen && diff --git a/Src/exec.c b/Src/exec.c index 1f23a862d..1860a10ed 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3954,7 +3954,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (type == WC_AUTOFN) { /* * We pre-loaded this to get any redirs. - * So we execuate a simplified function here. + * So we execute a simplified function here. */ lastval = execautofn_basic(state, do_exec); } else diff --git a/Src/math.c b/Src/math.c index ade02d80c..4f24361a4 100644 --- a/Src/math.c +++ b/Src/math.c @@ -162,7 +162,7 @@ static int unary = 1; #define TOKCOUNT 53 /* - * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * Operator precedences: in reverse order, i.e. lower number, high precedence. * These are the C precedences. * * 0 Non-operators: NUM (numeric constant), ID (identifier), @@ -219,7 +219,7 @@ static int c_prec[TOKCOUNT] = }; /* - * Opeator recedences: in reverse order, i.e. lower number, high precedence. + * Operator precedences: in reverse order, i.e. lower number, high precedence. * These are the default zsh precedences. * * 0 Non-operators: NUM (numeric constant), ID (identifier), diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index c114ff103..ac39a4eea 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -922,7 +922,7 @@ F:Note that the behaviour of 'exit' inside try-list inside a function is unspeci x=1 x=2 | echo $x echo $x -0:Assignment-only current shell commands in LHS of pipelin +0:Assignment-only current shell commands in LHS of pipeline >1 >1 diff --git a/Test/B12limit.ztst b/Test/B12limit.ztst index 48d33e6e3..9dce59824 100644 --- a/Test/B12limit.ztst +++ b/Test/B12limit.ztst @@ -11,7 +11,7 @@ %test limit | grep UNKNOWN || print OK -0:Check if there is unknown resouce(s) in the system +0:Check if there is unknown resource(s) in the system >OK F:A failure here does not indicate any error in zsh. It just means there F:is a resource in your system that is unknown to zsh developers. Please -- cgit v1.2.3 From 8bf0f0cf45606971d1433b36ce21ab45f6226004 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Tue, 18 Jan 2022 23:20:53 +0100 Subject: 49694 + doc: Allow using empty STTY= to freeze tty for a single command Previously, doing this would just run stty with no arguments, which normally causes it to print some terminal settings to stdout. --- ChangeLog | 5 +++++ Doc/Zsh/builtins.yo | 3 ++- Doc/Zsh/params.yo | 5 +++++ Src/exec.c | 6 ++++-- 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index 57c26fb38..98dd85274 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-01-30 Mikael Magnusson + + * 49694 + doc: Doc/Zsh/builtins.yo, Doc/Zsh/params.yo, Src/exec.c: + Allow using empty STTY= to freeze tty for a single command + 2022-01-29 Daniel Shahaf * unposted: Functions/VCS_Info/test-repo-git-rebase-apply, diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 733d8f185..c7de50fd6 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1885,7 +1885,8 @@ unfreezing the tty does not guarantee settings made on the command line are preserved. Strings of commands run between editing the command line will see a consistent tty state. See also the shell variable tt(STTY) for a means of initialising -the tty before running external commands. +the tty before running external commands and/or freezing the tty +around a single command. ) findex(type) item(tt(type) [ tt(-wfpamsS) ] var(name) ...)( diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 6b52d3b1c..6d2d41b7a 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1570,6 +1570,11 @@ if it is in the environment of the shell but not explicitly assigned to in the input line. This avoids running stty at every external command by accidentally exporting it. Also note that tt(STTY) should not be used for window size specifications; these will not be local to the command. + +If the parameter is set and empty, all of the above applies except +that tt(stty) is not run. This can be useful as a way to freeze the tty +around a single command, blocking its changes to tty settings, +similar to the tt(ttyctl) builtin. ) vindex(TERM) item(tt(TERM) )( diff --git a/Src/exec.c b/Src/exec.c index 1860a10ed..f67074846 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -684,8 +684,10 @@ execute(LinkList args, int flags, int defpath) /* If the parameter STTY is set in the command's environment, * * we first run the stty command with the value of this * - * parameter as it arguments. */ - if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) { + * parameter as it arguments. If the parameter is empty, we * + * do nothing, but this causes the terminal settings to be * + * restored later which can be useful. */ + if ((s = STTYval) && *s && isatty(0) && (GETPGRP() == getpid())) { char *t = tricat("stty", " ", s); STTYval = 0; /* this prevents infinite recursion */ -- cgit v1.2.3 From d7b8619396d806d390126c2abd1c3ce099fe7f59 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Tue, 8 Mar 2022 19:34:20 +0100 Subject: 49813: <<<: Document newline behavior and fix optimization The =(<< * 49918: NEWS, README: Update for 49917 and 49911. diff --git a/Doc/Zsh/redirect.yo b/Doc/Zsh/redirect.yo index 2b48974b4..fd40ab5a4 100644 --- a/Doc/Zsh/redirect.yo +++ b/Doc/Zsh/redirect.yo @@ -86,7 +86,8 @@ item(tt(<<<) var(word))( Perform shell expansion on var(word) and pass the result to standard input. This is known as a em(here-string). Compare the use of var(word) in here-documents above, where var(word) -does not undergo shell expansion. +does not undergo shell expansion. The result will have a trailing newline +after it. ) xitem(tt(<&) var(number)) item(tt(>&) var(number))( diff --git a/Src/exec.c b/Src/exec.c index f67074846..70cbfc97f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4836,8 +4836,10 @@ getoutputfile(char *cmd, char **eptr) singsub(&s); if (errflag) s = NULL; - else + else { untokenize(s); + s = dyncat(s, "\n"); + } } if (!s) /* Unclear why we need to do this before open() */ -- cgit v1.2.3 From 98e46340867028808e71e7f3373881cb7e5b6764 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 30 Mar 2022 09:28:43 +0100 Subject: 49906 (Bart), 49911: Fixes to querying jobs in subshell. Don't attempt to query invalid job off end of table, resulting in crashes from $jobtstates. If background task started in subshell, look at tatsks within subshell instead of main shell. Document and add test. --- ChangeLog | 8 ++++++++ Doc/Zsh/builtins.yo | 18 ++++++++++++++++++ Doc/Zsh/mod_parameter.yo | 6 ++++++ Src/exec.c | 1 + Src/jobs.c | 20 ++++++++++++++++++++ Test/W03jobparameters.ztst | 28 ++++++++++++++++++++++++++++ 6 files changed, 81 insertions(+) (limited to 'Src/exec.c') diff --git a/ChangeLog b/ChangeLog index c52119fa8..e6f73e30e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2022-03-30 Peter Stephenson + + * 49906 (Bart), 49911: Doc/Zsh/builtins.yo, + Doc/Zsh/mod_parameter.yo, Src/exec.c, Src/jobs.c, + Test/W03jobparameters.ztst: Fix querying jobs in subshell. + Don't uery invalid job at end, if background job started in + subshell query jobs in subshell instead of main shell. + 2022-03-30 Mikael Magnusson * 49893: Src/Zle/comp.h, Src/Zle/compcore.c: Fix comments for diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 5649e00d4..1d74f0c17 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1114,6 +1114,24 @@ The tt(-Z) option replaces the shell's argument and environment space with the given string, truncated if necessary to fit. This will normally be visible in tt(ps) (manref(ps)(1)) listings. This feature is typically used by daemons, to indicate their state. + +Full job control is only available in the top-level interactive shell, +not in commands run in the left hand side of pipelines or within +the tt(LPAR())var(...)tt(RPAR()) construct. However, a snapshot +of the job state at that point is taken, so it is still possible +to use the tt(jobs) builtin, or any parameter providing job information. +This gives information about the state of jobs at the point the subshell +was created. If background processes are created within the subshell, +then instead information about those processes is provided. + +For example, + +example(sleep 10 & # Job in background +LPAR() # Shell forks +jobs # Shows information about "sleep 10 &" +sleep 5 & # Process in background (no job control) +jobs # Shows information about "sleep 5 &" +RPAR()) ) findex(kill) cindex(killing jobs) diff --git a/Doc/Zsh/mod_parameter.yo b/Doc/Zsh/mod_parameter.yo index 2e3011e44..f3bcd7957 100644 --- a/Doc/Zsh/mod_parameter.yo +++ b/Doc/Zsh/mod_parameter.yo @@ -165,6 +165,8 @@ The keys of the associative arrays are usually valid job numbers, and these are the values output with, for example, tt(${(k)jobdirs}). Non-numeric job references may be used when looking up a value; for example, tt(${jobdirs[%+]}) refers to the current job. + +See the tt(jobs) builtin for how job information is provided in a subshell. ) vindex(jobtexts) item(tt(jobtexts))( @@ -173,6 +175,8 @@ that were used to start the jobs. Handling of the keys of the associative array is as described for tt(jobdirs) above. + +See the tt(jobs) builtin for how job information is provided in a subshell. ) vindex(jobstates) item(tt(jobstates))( @@ -189,6 +193,8 @@ the var(state) describes the state of that process. Handling of the keys of the associative array is as described for tt(jobdirs) above. + +See the tt(jobs) builtin for how job information is provided in a subshell. ) vindex(nameddirs) item(tt(nameddirs))( diff --git a/Src/exec.c b/Src/exec.c index 70cbfc97f..27d49e005 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1689,6 +1689,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) execpline2(state, code, how, opipe[0], ipipe[1], last1); pline_level--; if (how & Z_ASYNC) { + clearoldjobtab(); lastwj = newjob; if (thisjob == list_pipe_job) diff --git a/Src/jobs.c b/Src/jobs.c index 18e43f03c..af0a1233d 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1718,8 +1718,15 @@ clearjobtab(int monitor) /* Don't report any job we're part of */ if (thisjob != -1 && thisjob < oldmaxjob) memset(oldjobtab+thisjob, 0, sizeof(struct job)); + + /* oldmaxjob is now the size of the table, but outside + * this function, it's used as a job number, which must + * be the largest index available in the table. + */ + --oldmaxjob; } + memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */ maxjob = 0; @@ -1733,6 +1740,18 @@ clearjobtab(int monitor) thisjob = initjob(); } +/* In a subshell, decide we want our own job table after all. */ + +/**/ +mod_export void +clearoldjobtab(void) +{ + if (oldjobtab) + free(oldjobtab); + oldjobtab = NULL; + oldmaxjob = 0; +} + static int initnewjob(int i) { jobtab[i].stat = STAT_INUSE; @@ -2449,6 +2468,7 @@ bin_fg(char *name, char **argv, Options ops, int func) case BIN_BG: case BIN_WAIT: if (func == BIN_BG) { + clearoldjobtab(); jobtab[job].stat |= STAT_NOSTTY; jobtab[job].stat &= ~STAT_CURSH; } diff --git a/Test/W03jobparameters.ztst b/Test/W03jobparameters.ztst index af889c6d5..a6f7a09b1 100644 --- a/Test/W03jobparameters.ztst +++ b/Test/W03jobparameters.ztst @@ -48,3 +48,31 @@ *>running:+:*=running *>running:+:*=running *>zsh:*SIGHUPed* + +# $jobstates refers to a job started in the main shell unless +# one has been started in the subshell. In the latter case, +# the subshell has no job control so the job is not marked as current. + zpty_start + zpty_input "MODULE_PATH=${(q)MODULE_PATH}" + zpty_input 'sleep 3 &' + zpty_input '(print main; print $jobstates; sleep 2& print sub; print $jobstates)' + zpty_input 'jobs -s' + zpty_stop +0:$jobstate shows one job started in main shell or one started in subshell +*>\[1] [0-9]## +>main +*>running:+:*=running +>sub +*>running::*=running +*>zsh:*SIGHUPed* + +# output from zpty removes empty lines + zpty_start + zpty_input "MODULE_PATH=${(q)MODULE_PATH}" + zpty_input '(print main; print $jobstates; sleep 2& print sub; print $jobstates)' + zpty_input 'jobs -s' + zpty_stop +0:$jobstate shows no job started in main shell but one started in subshell +>main +>sub +*>running::*=running -- cgit v1.2.3