zsh-workers
 help / color / mirror / code / Atom feed
From: Peter Stephenson <p.w.stephenson@ntlworld.com>
To: Carl Agrell <caagr98@gmail.com>, zsh-workers <zsh-workers@zsh.org>
Subject: Re: [Bug] $jobstates does not work in $()
Date: Tue, 1 Mar 2022 10:40:50 +0000 (GMT)	[thread overview]
Message-ID: <1912988517.1110378.1646131250968@mail2.virginmedia.com> (raw)
In-Reply-To: <CALd=4svJDyZFBFwqWjW=exP9bxmUdNaepz3Tk7aRrHxhYUPx8A@mail.gmail.com>


> On 01 March 2022 at 00:46 Carl Agrell <caagr98@gmail.com> wrote:
> 
> 
> It appears that zsh/parameter's $jobstates is always empty inside $(),
> probably because it's a subshell of some sort. `jobs` still works
> however, making it rather incongruous. Having access to $jobstates
> would be useful for prompts for example, rather than having to parse
> the output of `jobs`.
> 
> To reproduce:
> ❯ sleep 1000 &
> [1] 210460
> ❯ jobs
> [1]  + running    sleep 1000
> ❯ echo $jobstates
> running:+:210460=running
> ❯ echo $(jobs)
> [1] + running sleep 1000
> ❯ echo $(echo $jobstates)
> (empty)

Yes, we store the old job table for use with the builtin, but not with
parameters, which is inconsistent.

What is probably worse is that some parameters (not, as it happens,
jobstates) call getjob(), which does have the logic to switch to the old
table, but then make no attempt to switch to the right table for the
remainder of the processing, which could result in undefined behaviour.

This fixes it up with a function selectjobtab() used to query job states
in the parameter functions as well as getjob().

pws

--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -1244,19 +1244,19 @@ histwgetfn(UNUSED(Param pm))
 
 /**/
 static char *
-pmjobtext(int job)
+pmjobtext(Job jtab, int job)
 {
     Process pn;
     int len = 1;
     char *ret;
 
-    for (pn = jobtab[job].procs; pn; pn = pn->next)
+    for (pn = jtab[job].procs; pn; pn = pn->next)
 	len += strlen(pn->text) + 3;
 
     ret = (char *) zhalloc(len);
     ret[0] = '\0';
 
-    for (pn = jobtab[job].procs; pn; pn = pn->next) {
+    for (pn = jtab[job].procs; pn; pn = pn->next) {
 	strcat(ret, pn->text);
 	if (pn->next)
 	    strcat(ret, " | ");
@@ -1269,22 +1269,25 @@ static HashNode
 getpmjobtext(UNUSED(HashTable ht), const char *name)
 {
     Param pm = NULL;
-    int job;
+    int job, jmax;
     char *pend;
+    Job jtab;
 
     pm = (Param) hcalloc(sizeof(struct param));
     pm->node.nam = dupstring(name);
     pm->node.flags = PM_SCALAR | PM_READONLY;
     pm->gsu.s = &nullsetscalar_gsu;
 
+    selectjobtab(&jtab, &jmax);
+
     job = strtod(name, &pend);
     /* Non-numeric keys are looked up by job name */
     if (*pend)
 	job = getjob(name, NULL);
-    if (job >= 1 && job <= maxjob &&
-	jobtab[job].stat && jobtab[job].procs &&
-	!(jobtab[job].stat & STAT_NOPRINT))
-	pm->u.str = pmjobtext(job);
+    if (job >= 1 && job <= jmax &&
+	jtab[job].stat && jtab[job].procs &&
+	!(jtab[job].stat & STAT_NOPRINT))
+	pm->u.str = pmjobtext(jtab, job);
     else {
 	pm->u.str = dupstring("");
 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1297,22 +1300,25 @@ static void
 scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
 {
     struct param pm;
-    int job;
+    int job, jmax;
     char buf[40];
+    Job jtab;
 
     memset((void *)&pm, 0, sizeof(struct param));
     pm.node.flags = PM_SCALAR | PM_READONLY;
     pm.gsu.s = &nullsetscalar_gsu;
 
-    for (job = 1; job <= maxjob; job++) {
-	if (jobtab[job].stat && jobtab[job].procs &&
-	    !(jobtab[job].stat & STAT_NOPRINT)) {
+    selectjobtab(&jtab, &jmax);
+
+    for (job = 1; job <= jmax; job++) {
+	if (jtab[job].stat && jtab[job].procs &&
+	    !(jtab[job].stat & STAT_NOPRINT)) {
 	    if (func != scancountparams) {
 		sprintf(buf, "%d", job);
 		pm.node.nam = dupstring(buf);
 		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 		    !(flags & SCANPM_WANTKEYS))
-		    pm.u.str = pmjobtext(job);
+		    pm.u.str = pmjobtext(jtab, job);
 	    }
 	    func(&pm.node, flags);
 	}
@@ -1323,7 +1329,7 @@ scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags)
 
 /**/
 static char *
-pmjobstate(int job)
+pmjobstate(Job jtab, int job)
 {
     Process pn;
     char buf[256], buf2[128], *ret, *state, *cp;
@@ -1335,14 +1341,14 @@ pmjobstate(int job)
     else
 	cp = ":";
 
-    if (jobtab[job].stat & STAT_DONE)
+    if (jtab[job].stat & STAT_DONE)
 	ret = dyncat("done", cp);
-    else if (jobtab[job].stat & STAT_STOPPED)
+    else if (jtab[job].stat & STAT_STOPPED)
 	ret = dyncat("suspended", cp);
     else
 	ret = dyncat("running", cp);
 
-    for (pn = jobtab[job].procs; pn; pn = pn->next) {
+    for (pn = jtab[job].procs; pn; pn = pn->next) {
 
 	if (pn->status == SP_RUNNING)
 	    state = "running";
@@ -1371,21 +1377,24 @@ static HashNode
 getpmjobstate(UNUSED(HashTable ht), const char *name)
 {
     Param pm = NULL;
-    int job;
+    int job, jmax;
     char *pend;
+    Job jtab;
 
     pm = (Param) hcalloc(sizeof(struct param));
     pm->node.nam = dupstring(name);
     pm->node.flags = PM_SCALAR | PM_READONLY;
     pm->gsu.s = &nullsetscalar_gsu;
 
+    selectjobtab(&jtab, &jmax);
+
     job = strtod(name, &pend);
     if (*pend)
 	job = getjob(name, NULL);
-    if (job >= 1 && job <= maxjob &&
-	jobtab[job].stat && jobtab[job].procs &&
-	!(jobtab[job].stat & STAT_NOPRINT))
-	pm->u.str = pmjobstate(job);
+    if (job >= 1 && job <= jmax &&
+	jtab[job].stat && jtab[job].procs &&
+	!(jtab[job].stat & STAT_NOPRINT))
+	pm->u.str = pmjobstate(jtab, job);
     else {
 	pm->u.str = dupstring("");
 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1398,22 +1407,25 @@ static void
 scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
 {
     struct param pm;
-    int job;
+    int job, jmax;
+    Job jtab;
     char buf[40];
 
+    selectjobtab(&jtab, &jmax);
+
     memset((void *)&pm, 0, sizeof(struct param));
     pm.node.flags = PM_SCALAR | PM_READONLY;
     pm.gsu.s = &nullsetscalar_gsu;
 
-    for (job = 1; job <= maxjob; job++) {
-	if (jobtab[job].stat && jobtab[job].procs &&
-	    !(jobtab[job].stat & STAT_NOPRINT)) {
+    for (job = 1; job <= jmax; job++) {
+	if (jtab[job].stat && jtab[job].procs &&
+	    !(jtab[job].stat & STAT_NOPRINT)) {
 	    if (func != scancountparams) {
 		sprintf(buf, "%d", job);
 		pm.node.nam = dupstring(buf);
 		if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 		    !(flags & SCANPM_WANTKEYS))
-		    pm.u.str = pmjobstate(job);
+		    pm.u.str = pmjobstate(jtab, job);
 	    }
 	    func(&pm.node, flags);
 	}
@@ -1424,11 +1436,11 @@ scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags)
 
 /**/
 static char *
-pmjobdir(int job)
+pmjobdir(Job jtab, int job)
 {
     char *ret;
 
-    ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd);
+    ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd);
     return ret;
 }
 
@@ -1437,21 +1449,24 @@ static HashNode
 getpmjobdir(UNUSED(HashTable ht), const char *name)
 {
     Param pm = NULL;
-    int job;
+    int job, jmax;
     char *pend;
+    Job jtab;
 
     pm = (Param) hcalloc(sizeof(struct param));
     pm->node.nam = dupstring(name);
     pm->node.flags = PM_SCALAR | PM_READONLY;
     pm->gsu.s = &nullsetscalar_gsu;
 
+    selectjobtab(&jtab, &jmax);
+
     job = strtod(name, &pend);
     if (*pend)
 	job = getjob(name, NULL);
-    if (job >= 1 && job <= maxjob &&
-	jobtab[job].stat && jobtab[job].procs &&
-	!(jobtab[job].stat & STAT_NOPRINT))
-	pm->u.str = pmjobdir(job);
+    if (job >= 1 && job <= jmax &&
+	jtab[job].stat && jtab[job].procs &&
+	!(jtab[job].stat & STAT_NOPRINT))
+	pm->u.str = pmjobdir(jtab, job);
     else {
 	pm->u.str = dupstring("");
 	pm->node.flags |= (PM_UNSET|PM_SPECIAL);
@@ -1464,22 +1479,25 @@ static void
 scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags)
 {
     struct param pm;
-    int job;
+    int job, jmax;
     char buf[40];
+    Job jtab;
 
     memset((void *)&pm, 0, sizeof(struct param));
     pm.node.flags = PM_SCALAR | PM_READONLY;
     pm.gsu.s = &nullsetscalar_gsu;
 
-    for (job = 1; job <= maxjob; job++) {
-       if (jobtab[job].stat && jobtab[job].procs &&
-           !(jobtab[job].stat & STAT_NOPRINT)) {
+    selectjobtab(&jtab, &jmax);
+
+    for (job = 1; job <= jmax; job++) {
+       if (jtab[job].stat && jtab[job].procs &&
+           !(jtab[job].stat & STAT_NOPRINT)) {
            if (func != scancountparams) {
 	       sprintf(buf, "%d", job);
 	       pm.node.nam = dupstring(buf);
                if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) ||
 		   !(flags & SCANPM_WANTKEYS))
-		   pm.u.str = pmjobdir(job);
+		   pm.u.str = pmjobdir(jtab, job);
 	   }
            func(&pm.node, flags);
        }
diff --git a/Src/jobs.c b/Src/jobs.c
index f0b337110..18e43f03c 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -98,10 +98,12 @@ mod_export int jobtabsize;
 mod_export int maxjob;
 
 /* If we have entered a subshell, the original shell's job table. */
-static struct job *oldjobtab;
+/**/
+mod_export struct job *oldjobtab;
 
 /* The size of that. */
-static int oldmaxjob;
+/**/
+mod_export int oldmaxjob;
 
 /* shell timings */
  
@@ -1894,6 +1896,26 @@ setcurjob(void)
     }
 }
 
+/* Find the job table for reporting jobs */
+
+/**/
+mod_export void
+selectjobtab(Job *jtabp, int *jmaxp)
+{
+    if (oldjobtab)
+    {
+	/* In subshell --- use saved job table to report */
+	*jtabp = oldjobtab;
+	*jmaxp = oldmaxjob;
+    }
+    else
+    {
+	/* Use main job table */
+	*jtabp = jobtab;
+	*jmaxp = maxjob;
+    }
+}
+
 /* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) *
  * to a job number.                                             */
 
@@ -1904,13 +1926,7 @@ getjob(const char *s, const char *prog)
     int jobnum, returnval, mymaxjob;
     Job myjobtab;
 
-    if (oldjobtab) {
-	myjobtab = oldjobtab;
-	mymaxjob = oldmaxjob;
-    } else {
-	myjobtab= jobtab;
-	mymaxjob = maxjob;
-    }
+    selectjobtab(&myjobtab, &mymaxjob);
 
     /* if there is no %, treat as a name */
     if (*s != '%')


  reply	other threads:[~2022-03-01 10:41 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-01  0:46 Carl Agrell
2022-03-01 10:40 ` Peter Stephenson [this message]
2022-03-02  9:44   ` Daniel Shahaf
2022-03-02 20:18     ` Peter Stephenson
2022-03-03 22:04       ` Vin Shelton
2022-03-03 23:07         ` Bart Schaefer
2022-03-04  2:14           ` Jun T
2022-03-04 13:04             ` Daniel Shahaf
2022-03-04 13:13               ` Vin Shelton
2022-03-06  4:18               ` Jun T
2022-03-07 14:22                 ` Daniel Shahaf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1912988517.1110378.1646131250968@mail2.virginmedia.com \
    --to=p.w.stephenson@ntlworld.com \
    --cc=caagr98@gmail.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).