ref: 4ac3a0224ed9d54818f858fba69c8e94f38f2c12
dir: /9front/675bcd62992e59002c834ef445d515b346f4374d.patch/
From: Romano <me+git@fallglow.com>
Date: Fri, 12 Jul 2024 19:24:48 +0000
Subject: [PATCH] rc: do not share certain special vars; fix $status bug for most cases
Certain special vars (namely $*, $pid, $apid, and $status)
are used internally and should not be set after a child
returns.
There has been a long-standing bug where checking the value
of $status with ~ overwrites the value of $status. $status
is now restored in most cases, other than running it
as a standalone built-in command.
Man page for rc is updated
---
diff 7637f8f5fd6490d07ba64bcf404321028e698ed9 675bcd62992e59002c834ef445d515b346f4374d
--- a/sys/src/cmd/rc/code.c
+++ b/sys/src/cmd/rc/code.c
@@ -275,6 +275,8 @@
codeswitch(t, eflag);
break;
case TWIDDLE:
+ if(c0 && c0->type=='$' && c0->child[0] && (c0->child[0]->type==WORD && strcmp(c0->child[0]->str, "status")==0))
+ emitf(Xstashstatus);
emitf(Xmark);
noglobs(c1, 1);
outcode(c1, eflag);
--- a/sys/src/cmd/rc/exec.c
+++ b/sys/src/cmd/rc/exec.c
@@ -282,6 +282,16 @@
dotrap();
}
}
+
+void
+unstashstatus(void)
+{
+ var *oldstatus = vlook("status-");
+ if(oldstatus->val && oldstatus->val->word){
+ setstatus(oldstatus->val->word);
+ }
+}
+
/*
* Opcode routines
* Arguments on stack (...)
@@ -415,6 +425,7 @@
void
Xifnot(void)
{
+ unstashstatus();
if(ifnot)
runq->pc++;
else
@@ -597,7 +608,9 @@
void
Xtrue(void)
{
- if(truestatus()) runq->pc++;
+ int stat = truestatus();
+ unstashstatus();
+ if(stat) runq->pc++;
else runq->pc = runq->code[runq->pc].i;
}
@@ -605,7 +618,9 @@
Xif(void)
{
ifnot = 1;
- if(truestatus()) runq->pc++;
+ int stat = truestatus();
+ unstashstatus();
+ if(stat) runq->pc++;
else runq->pc = runq->code[runq->pc].i;
}
@@ -661,6 +676,12 @@
}
poplist();
poplist();
+}
+
+void
+Xstashstatus(void)
+{
+ setvar("status-", Newword(estrdup(getstatus()), (word *)0));
}
void
--- a/sys/src/cmd/rc/exec.h
+++ b/sys/src/cmd/rc/exec.h
@@ -4,7 +4,7 @@
extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void);
extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqw(void), Xdup(void);
extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void);
-extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void), Xhere(void), Xhereq(void);
+extern void Xjump(void), Xmark(void), Xmatch(void), Xstashstatus(void), Xpipe(void), Xread(void), Xhere(void), Xhereq(void);
extern void Xrdwr(void), Xsrcline(void);
extern void Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void);
extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void);
--- a/sys/src/cmd/rc/pfnc.c
+++ b/sys/src/cmd/rc/pfnc.c
@@ -30,6 +30,7 @@
Xword, "Xword",
Xwrite, "Xwrite",
Xmatch, "Xmatch",
+ Xstashstatus, "Xstashstatus",
Xcase, "Xcase",
Xconc, "Xconc",
Xassign, "Xassign",
--- a/sys/src/cmd/rc/plan9.c
+++ b/sys/src/cmd/rc/plan9.c
@@ -144,7 +144,12 @@
if(n <= 0)
break;
for(i = 0; i<n; i++){
- if(ent[i].length<=0 || strncmp(ent[i].name, "fn#", 3)==0)
+ if(ent[i].length<=0
+ || strncmp(ent[i].name, "fn#", 3)==0
+ || strcmp(ent[i].name,"*")==0
+ || strcmp(ent[i].name,"apid")==0
+ || strcmp(ent[i].name,"pid")==0
+ || strcmp(ent[i].name,"status")==0)
continue;
if((fd = Open(Env(ent[i].name, 0), 0))>=0){
io *f = openiofd(fd);
--- a/sys/src/cmd/rc/test/updenvall.file
+++ b/sys/src/cmd/rc/test/updenvall.file
@@ -78,3 +78,40 @@
echo d
}
' '/env/fn#a is echo d'
+
+echo testing special vars
+*=b
+echo -n a > /env/'*'
+ok $"* 'b' '* not set'
+echo -n $"pid >/tmp/pid.rc.test
+rc -c ''
+ok $"pid `{cat /tmp/pid.rc.test} 'pid not set'
+rm /tmp/pid.rc.test
+echo -n `{apid=foobar; echo $apid} >/dev/null
+ok $"apid '' 'apid not set'
+echo -n foo > /env/status
+ok $"status '' 'status not set'
+status=foo
+~ $status foo
+ok $"status '' 'naked ~ sets status to '''''
+status=`{echo foo}
+ok $"status foo 'backtick sets status'
+if(~ $status foo)
+ ok $"status foo '$status'
+if(! ~ $status a)
+ ok $"status foo '$status for ! ~'
+if(~ $status a)
+ ;
+if not
+ ok $status foo '$status for if not'
+if(~ $"status foo)
+ ok $"status foo '$"status'
+if(echo a | grep -s a || ls)
+ ok $"status foo '||'
+while(! ~ $status foo)
+ status=bar
+ok $status foo 'after while not match'
+while(~ $status foo)
+ status=bar;
+ok $status bar 'after while match'
+status=''
--- a/sys/src/cmd/rc/test/updenvall.rc
+++ b/sys/src/cmd/rc/test/updenvall.rc
@@ -9,7 +9,7 @@
shift
desc=$1
if(~ $"actual $"expected) echo ok $desc
- if not echo 'not ok '^$"desc^', got '$"actual
+ if not echo 'NOT OK '^$"desc^', got '$"actual
}
# now let's run the test with our build