shithub: patches

ref: 4ac3a0224ed9d54818f858fba69c8e94f38f2c12
dir: /9front/675bcd62992e59002c834ef445d515b346f4374d.patch/

View raw version
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