[PATCH v6] ash: add process substitution in bash-compatibility mode (original) (raw)
Denys Vlasenko vda.linux at googlemail.com
Sat Jun 5 21:40:25 UTC 2021
- Previous message (by thread): [PATCH] run-parts: permit dot later in file name
- Next message (by thread): [PATCH 6/6] httpd_post_upload.cgi: use mktemp to avoid $RANDOM
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Sorry for a very late reply. Applied, thank you!
On Thu, Jul 23, 2020 at 9:37 AM Ron Yorston <rmy at pobox.com> wrote:
Process substitution is a Korn shell feature that's also available in bash and some other shells. This patch implements process substitution in ash when ASHBASHCOMPAT is enabled. function old new delta argstr 1386 1522 +136 strtodest - 52 +52 readtoken1 3346 3392 +46 .rodata 183206 183250 +44 unwindredir - 28 +28 cmdloop 365 372 +7 static.spclchars 10 12 +2 cmdputs 380 367 -13 exitreset 86 69 -17 evalcommand 1754 1737 -17 varvalue 675 634 -41 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 5/4 up/down: 315/-88) Total: 227 bytes text data bss dec hex filename 953967 4219 1904 960090 ea65a busyboxold 954192 4219 1904 960315 ea73b busyboxunstripped v2: Replace array of file descriptors with a linked list. Include tests that were unaccountably omitted from v1. v3: Update linked list code to the intended version. v4: Change order of conditional code in cmdputs(). v5: Use existing popredir() mechanism to manage file descriptors. v6: Rebase to latest version of BusyBox ash. Reduce code churn. Signed-off-by: Ron Yorston <rmy at pobox.com> --- include/platform.h | 2 + shell/ash.c | 160 +++++++++++++++++-- shell/ashtest/ash-psubst/bashprocsub.right | 9 ++ shell/ashtest/ash-psubst/bashprocsub.tests | 33 ++++ 4 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 shell/ashtest/ash-psubst/bashprocsub.right create mode 100755 shell/ashtest/ash-psubst/bashprocsub.tests diff --git a/include/platform.h b/include/platform.h index 43bb391bd..233cbe87c 100644 --- a/include/platform.h +++ b/include/platform.h @@ -421,6 +421,8 @@ typedef unsigned smalluint; #define HAVENETETHERNETH 1 #define HAVESYSSTATFSH 1 #define HAVEPRINTFPERCENTM 1 +#define HAVEDEVFD 1 +#define DEVFDPREFIX "/dev/fd/" #if defined(UCLIBC) # if UCLIBCVERSION < KERNELVERSION(0, 9, 32)_ _diff --git a/shell/ash.c b/shell/ash.c_ _index ecb9b132b..72bf49896 100644_ _--- a/shell/ash.c_ _+++ b/shell/ash.c_ _@@ -229,6 +229,14 @@_ _#define BASHREADD ENABLEASHBASHCOMPAT_ _#define IFBASHREADD IFASHBASHCOMPAT_ _#define BASHWAITN ENABLEASHBASHCOMPAT_ _+/* <(...) and >(...) */ +#if HAVEDEVFD +# define BASHPROCESSSUBST ENABLEASHBASHCOMPAT +# define IFBASHPROCESSSUBST IFASHBASHCOMPAT +#else +# define BASHPROCESSSUBST 0 +# define IFBASHPROCESSSUBST(...) +#endif #if defined(ANDROIDAPI) && ANDROIDAPI <= 24_ _/* Bionic at least up to version 24 has no glob() */_ _@@ -745,6 +753,12 @@ out2str(const char *p)_ _#define CTLENDARI ((unsigned char)'\207')_ _#define CTLQUOTEMARK ((unsigned char)'\210')_ _#define CTLLAST CTLQUOTEMARK_ _+#if BASHPROCESSSUBST_ _+# define CTLTOPROC ((unsigned char)'\211')_ _+# define CTLFROMPROC ((unsigned char)'\212')_ _+# undef CTLLAST_ _+# define CTLLAST CTLFROMPROC_ _+#endif_ _/* variable substitution byte (follows CTLVAR) */_ _#define VSTYPE 0x0f /* type of variable substitution */_ _@@ -1016,6 +1030,10 @@ traceputsquoted(char *s)_ _case CTLESC: c = 'e'; goto backslash;_ _case CTLVAR: c = 'v'; goto backslash;_ _case CTLBACKQ: c = 'q'; goto backslash;_ _+#if BASHPROCESSSUBST_ _+ case CTLTOPROC: c = 'p'; goto backslash;_ _+ case CTLFROMPROC: c = 'P'; goto backslash;_ _+#endif_ _backslash:_ _putc('\', tracefile);_ _putc(c, tracefile);_ _@@ -1177,8 +1195,17 @@ sharg(union node *arg, FILE *fp)_ _case CTLENDVAR:_ _putc('}', fp);_ _break;_ _+#if BASHPROCESSSUBST_ _+ case CTLTOPROC:_ _+ putc('>', fp); + goto backq; + case CTLFROMPROC: + putc('<', fp);_ _+ goto backq;_ _+#endif_ _case CTLBACKQ:_ _putc('$', fp);_ _+ IFBASHPROCESSSUBST(backq:)_ _putc('(', fp);_ _shtree(bqlist->n, -1, NULL, fp); putc(')', fp); @@ -3230,8 +3257,13 @@ static const uint8t syntaxindextable[] ALIGN1 = { /* 134 CTLARI */ CCTLCCTLCCTLCCTL, /* 135 CTLENDARI */ CCTLCCTLCCTLCCTL, /* 136 CTLQUOTEMARK */ CCTLCCTLCCTLCCTL, +#if BASHPROCESSSUBST + /* 137 CTLTOPROC */ CCTLCCTLCCTLCCTL, + /* 138 CTLFROMPROC */ CCTLCCTLCCTLCCTL, +#else /* 137 */ CWORDCWORDCWORDCWORD, /* 138 */ CWORDCWORDCWORDCWORD, +#endif /* 139 */ CWORDCWORDCWORDCWORD, /* 140 */ CWORDCWORDCWORDCWORD, /* 141 */ CWORDCWORDCWORDCWORD, @@ -4857,9 +4889,24 @@ cmdputs(const char *s) quoted >>= 1; subtype = 0; goto dostr; +#if BASHPROCESSSUBST + case CTLBACKQ: + c = '$'; + str = "(...)"; + break; + case CTLTOPROC: + c = '>'; + str = "(...)"; + break; + case CTLFROMPROC: + c = '<';_ _+ str = "(...)";_ _+ break;_ _+#else_ _case CTLBACKQ:_ _str = "$(...)";_ _goto dostr;_ _+#endif_ _#if ENABLEFEATURESHMATH_ _case CTLARI:_ _str = "$((";_ _@@ -5899,6 +5946,21 @@ redirectsafe(union node *redir, int flags)_ _return err;_ _}_ _+#if BASHPROCESSSUBST_ _+static void_ _+pushfd(int fd)_ _+{_ _+ struct redirtab *sv;_ _+_ _+ sv = ckzalloc(sizeof(*sv) + sizeof(sv->twofd[0])); + sv->paircount = 1; + sv->twofd[0].origfd = fd; + sv->twofd[0].movedto = CLOSED; + sv->next = redirlist; + redirlist = sv; +} +#endif + static struct redirtab* pushredir(union node *redir) { @@ -6537,10 +6599,20 @@ evaltreenr(union node *n, int flags) } static void FASTFUNC -evalbackcmd(union node *n, struct backcmd *result) +evalbackcmd(union node *n, struct backcmd *result + IFBASHPROCESSSUBST(, int ctl)) { int pip[2]; struct job *jp; +#if BASHPROCESSSUBST + /* determine end of pipe used by parent (ip) and child (ic) */ + const int ip = (ctl == CTLTOPROC); + const int ic = !(ctl == CTLTOPROC); +#else + const int ctl = CTLBACKQ; + const int ip = 0; + const int ic = 1; +#endif result->fd = -1; result->buf = NULL; @@ -6552,15 +6624,17 @@ evalbackcmd(union node *n, struct backcmd *result) if (pipe(pip) < 0)_ _ashmsgandraiseperror("can't create pipe");_ _- jp = makejob(/*n,*/ 1);_ _- if (forkshell(jp, n, FORKNOJOB) == 0) {_ _+ /* process substitution uses NULL job/node, like openhere() */_ _+ jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;_ _+ if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORKNOJOB) == 0) {_ _/* child */_ _FORCEINTON;_ _- close(pip[0]);_ _- if (pip[1] != 1) {_ _- /*close(1);*/_ _- dup2orraise(pip[1], 1);_ _- close(pip[1]);_ _+ close(pip[ip]);_ _+ /* ic is index of child end of pipe *and* fd to connect it to */_ _+ if (pip[ic] != ic) {_ _+ /*close(ic);*/_ _+ dup2orraise(pip[ic], ic);_ _+ close(pip[ic]);_ _}_ _/* TODO: eflag clearing makes the following not abort:_ _* ash -c 'set -e; z=$(false;echo foo); echo $z'_ _@@ -6576,8 +6650,18 @@ evalbackcmd(union node *n, struct backcmd *result)_ _/* NOTREACHED */_ _}_ _/* parent */_ _- close(pip[1]);_ _- result->fd = pip[0]; +#if BASHPROCESSSUBST + if (ctl != CTLBACKQ) { + int fd = fcntl(pip[ip], FDUPFD, 64); + if (fd > 0) { + close(pip[ip]); + pip[ip] = fd; + } + pushfd(pip[ip]); + } +#endif + close(pip[ic]); + result->fd = pip[ip]; result->jp = jp; out: @@ -6589,8 +6673,11 @@ evalbackcmd(union node *n, struct backcmd *result) * Expand stuff in backwards quotes. */ static void -expbackq(union node *cmd, int flag) +expbackq(union node *cmd, int flag IFBASHPROCESSSUBST(, int ctl)) { +#if !BASHPROCESSSUBST + const int ctl = CTLBACKQ; +#endif struct backcmd in; int i; char buf[128]; @@ -6605,9 +6692,15 @@ expbackq(union node *cmd, int flag) INTOFF; startloc = expdest - (char *)stackblock(); pushstackmark(&smark, startloc); - evalbackcmd(cmd, &in); + evalbackcmd(cmd, &in IFBASHPROCESSSUBST(, ctl)); popstackmark(&smark); + if (ctl != CTLBACKQ) { + sprintf(buf, DEVFDPREFIX"%d", in.fd); + strtodest(buf, BASESYNTAX); + goto done; + } + p = in.buf; i = in.nleft; if (i == 0) @@ -6629,6 +6722,7 @@ expbackq(union node *cmd, int flag) close(in.fd); backexitstatus = waitforjob(in.jp); } + IFBASHPROCESSSUBST(done:) INTON; /* Eat all trailing newlines */ @@ -6716,6 +6810,10 @@ argstr(char *p, int flag) CTLESC, CTLVAR, CTLBACKQ, +#if BASHPROCESSSUBST + CTLTOPROC, + CTLFROMPROC, +#endif #if ENABLEFEATURESHMATH CTLARI, CTLENDARI, @@ -6815,8 +6913,12 @@ argstr(char *p, int flag) p = evalvar(p, flag | inquotes); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); goto start; +#if BASHPROCESSSUBST + case CTLTOPROC: + case CTLFROMPROC: +#endif case CTLBACKQ: - expbackq(argbackq->n, flag | inquotes); + expbackq(argbackq->n, flag | inquotes IFBASHPROCESSSUBST(, c)); goto start; #if ENABLEFEATURESHMATH case CTLARI: @@ -12203,8 +12305,9 @@ realeofmark(const char *eofmark) #define CHECKEND() {goto checkend; checkendreturn:;} #define PARSEREDIR() {goto parseredir; parseredirreturn:;} #define PARSESUB() {goto parsesub; parsesubreturn:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackqoldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackqnewreturn:;} +#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackqoldreturn:;} +#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackqnewreturn:;} +#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackqpsreturn:;} #define PARSEARITH() {goto parsearith; parsearithreturn:;} static int readtoken1(int c, int syntax, char *eofmark, int striptabs) @@ -12215,7 +12318,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) sizet len; struct nodelist *bqlist; smallint quotef; - smallint oldstyle; + smallint style; + enum { OLD, NEW, PSUB }; +#define oldstyle style == OLD smallint pssyntax; /* we are expanding a prompt string */ IFBASHDOLLARSQUOTE(smallint bashdollarsquote = 0;) /* syntax stack */ @@ -12396,6 +12501,15 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) c = 0x100 + '>'; /* flag &> */ pungetc(); } +#endif +#if BASHPROCESSSUBST + if (c == '<' || c == '>') { + if (pgetc() == '(') { + PARSEPROCSUB(); + break; + } + pungetc(); + } #endif goto endword; /* exit outer loop */ } @@ -12881,9 +12995,18 @@ parsebackq: { memcpy(out, str, savelen); STADJUST(savelen, out); } - USTPUTC(CTLBACKQ, out); +#if BASHPROCESSSUBST + if (style == PSUB) + USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out);_ _+ else_ _+#endif_ _+ USTPUTC(CTLBACKQ, out);_ _if (oldstyle)_ _goto parsebackqoldreturn;_ _+#if BASHPROCESSSUBST_ _+ else if (style == PSUB)_ _+ goto parsebackqpsreturn;_ _+#endif_ _goto parsebackqnewreturn;_ _}_ _@@ -13334,6 +13457,9 @@ cmdloop(int top)_ _#if JOBS_ _if (doingjobctl)_ _showjobs(SHOWCHANGED|SHOWSTDERR);_ _+#endif_ _+#if BASHPROCESSSUBST_ _+ unwindredir(NULL);_ _#endif_ _inter = 0;_ _if (iflag && top) {_ _diff --git a/shell/ashtest/ash-psubst/bashprocsub.right b/shell/ashtest/ash-psubst/bashprocsub.right_ _new file mode 100644_ _index 000000000..aa16a96be_ _--- /dev/null_ _+++ b/shell/ashtest/ash-psubst/bashprocsub.right_ _@@ -0,0 +1,9 @@_ _+hello 1_ _+hello 2_ _+hello 3_ _+<(echo "hello 0")_ _+hello 4_ _+HI THERE_ _+hello error_ _+hello error_ _+hello stderr_ _diff --git a/shell/ashtest/ash-psubst/bashprocsub.tests b/shell/ashtest/ash-psubst/bashprocsub.tests_ _new file mode 100755_ _index 000000000..63b836782_ _--- /dev/null_ _+++ b/shell/ashtest/ash-psubst/bashprocsub.tests_ _@@ -0,0 +1,33 @@_ _+# simplest case_ _+cat <(echo "hello 1")_ _+_ _+# can have more than one_ _+cat <(echo "hello 2") <(echo "hello 3")_ _+_ _+# doesn't work in quotes_ _+echo "<(echo "hello 0")"_ _+_ _+# process substitution can be nested inside command substitution_ _+echo $(cat <(echo "hello 4"))_ _+_ _+# example from http://wiki.bash-hackers.org/syntax/expansion/procsubst_ _+# process substitutions can be passed to a function as parameters or_ _+# variables_ _+f() {_ _+ cat "$1" >"$x" +} +x=>(tr '[:lower:]' '[:upper:]') f <(echo 'hi there')_ _+_ _+# process substitution can be combined with redirection on exec_ _+rm -f err_ _+# save stderr_ _+exec 4>&2 +# copy stderr to a file +exec 2> >(tee err) +echo "hello error" >&2 +sync +# restore stderr +exec 2>&4 +cat err +rm -f err +echo "hello stderr" >&2 -- 2.26.2
busybox mailing list busybox at busybox.net http://lists.busybox.net/mailman/listinfo/busybox
- Previous message (by thread): [PATCH] run-parts: permit dot later in file name
- Next message (by thread): [PATCH 6/6] httpd_post_upload.cgi: use mktemp to avoid $RANDOM
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]