From 1b30c63dfdd4f31faa6deda69345bf07f23aa334 Mon Sep 17 00:00:00 2001 From: Eicke Herbertz Date: Sat, 5 Jun 2021 11:42:06 +0000 Subject: shell: also do word splitting when -d DELIM is used The original commit 3bef5d89b0 introduced an additional check for an unset `opt_d` before doing word splitting. I'm unsure why it's there in the first place, but the commit message also describes a different behaviour than what -d actually does in bash, while the code mostly does the right thing. `opt_d` sets the line delimiter for read to stop reading and should not affect word splitting. Testcase: $ echo qwe rty | { read -d Z a b; echo a:$a b:$b; } a:qwe b:rty function old new delta shell_builtin_read 1314 1304 -10 Signed-off-by: Eicke Herbertz Signed-off-by: Denys Vlasenko --- shell/shell_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/shell_common.c b/shell/shell_common.c index f95a35e8b..e3d6783b5 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -230,7 +230,7 @@ shell_builtin_read(struct builtin_read_params *params) * without variable names (bash compat). * Thus, "read" and "read REPLY" are not the same. */ - if (!params->opt_d && argv[0]) { + if (argv[0]) { /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */ const char *is_ifs = strchr(ifs, c); if (startword && is_ifs) { -- cgit v1.2.3-55-g6feb From a1b0d3856d9a0419cb74bf4c87525265871b5868 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Thu, 23 Jul 2020 08:32:27 +0100 Subject: ash: add process substitution in bash-compatibility mode 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 ASH_BASH_COMPAT 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 busybox_old 954192 4219 1904 960315 ea73b busybox_unstripped 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 Signed-off-by: Denys Vlasenko --- include/platform.h | 2 + shell/ash.c | 160 ++++++++++++++++++++++++--- shell/ash_test/ash-psubst/bash_procsub.right | 9 ++ shell/ash_test/ash-psubst/bash_procsub.tests | 33 ++++++ 4 files changed, 187 insertions(+), 17 deletions(-) create mode 100644 shell/ash_test/ash-psubst/bash_procsub.right create mode 100755 shell/ash_test/ash-psubst/bash_procsub.tests (limited to 'shell') diff --git a/include/platform.h b/include/platform.h index 4633b2507..9e1fb047d 100644 --- a/include/platform.h +++ b/include/platform.h @@ -426,6 +426,8 @@ typedef unsigned smalluint; #define HAVE_SYS_STATFS_H 1 #define HAVE_PRINTF_PERCENTM 1 #define HAVE_WAIT3 1 +#define HAVE_DEV_FD 1 +#define DEV_FD_PREFIX "/dev/fd/" #if defined(__UCLIBC__) # if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32) diff --git a/shell/ash.c b/shell/ash.c index 6a16833b1..05c47950f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -229,6 +229,14 @@ #define BASH_READ_D ENABLE_ASH_BASH_COMPAT #define IF_BASH_READ_D IF_ASH_BASH_COMPAT #define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT +/* <(...) and >(...) */ +#if HAVE_DEV_FD +# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT +# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT +#else +# define BASH_PROCESS_SUBST 0 +# define IF_BASH_PROCESS_SUBST(...) +#endif #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 /* Bionic at least up to version 24 has no glob() */ @@ -804,6 +812,12 @@ out2str(const char *p) #define CTLENDARI ((unsigned char)'\207') #define CTLQUOTEMARK ((unsigned char)'\210') #define CTL_LAST CTLQUOTEMARK +#if BASH_PROCESS_SUBST +# define CTLTOPROC ((unsigned char)'\211') +# define CTLFROMPROC ((unsigned char)'\212') +# undef CTL_LAST +# define CTL_LAST CTLFROMPROC +#endif /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ @@ -1075,6 +1089,10 @@ trace_puts_quoted(char *s) case CTLESC: c = 'e'; goto backslash; case CTLVAR: c = 'v'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash; +#if BASH_PROCESS_SUBST + case CTLTOPROC: c = 'p'; goto backslash; + case CTLFROMPROC: c = 'P'; goto backslash; +#endif backslash: putc('\\', tracefile); putc(c, tracefile); @@ -1236,8 +1254,17 @@ sharg(union node *arg, FILE *fp) case CTLENDVAR: putc('}', fp); break; +#if BASH_PROCESS_SUBST + case CTLTOPROC: + putc('>', fp); + goto backq; + case CTLFROMPROC: + putc('<', fp); + goto backq; +#endif case CTLBACKQ: putc('$', fp); + IF_BASH_PROCESS_SUBST(backq:) putc('(', fp); shtree(bqlist->n, -1, NULL, fp); putc(')', fp); @@ -3234,8 +3261,13 @@ static const uint8_t syntax_index_table[] ALIGN1 = { /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, +#if BASH_PROCESS_SUBST + /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL, + /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL, +#else /* 137 */ CWORD_CWORD_CWORD_CWORD, /* 138 */ CWORD_CWORD_CWORD_CWORD, +#endif /* 139 */ CWORD_CWORD_CWORD_CWORD, /* 140 */ CWORD_CWORD_CWORD_CWORD, /* 141 */ CWORD_CWORD_CWORD_CWORD, @@ -4849,9 +4881,24 @@ cmdputs(const char *s) quoted >>= 1; subtype = 0; goto dostr; +#if BASH_PROCESS_SUBST + case CTLBACKQ: + c = '$'; + str = "(...)"; + break; + case CTLTOPROC: + c = '>'; + str = "(...)"; + break; + case CTLFROMPROC: + c = '<'; + str = "(...)"; + break; +#else case CTLBACKQ: str = "$(...)"; goto dostr; +#endif #if ENABLE_FEATURE_SH_MATH case CTLARI: str = "$(("; @@ -5891,6 +5938,21 @@ redirectsafe(union node *redir, int flags) return err; } +#if BASH_PROCESS_SUBST +static void +pushfd(int fd) +{ + struct redirtab *sv; + + sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0])); + sv->pair_count = 1; + sv->two_fd[0].orig_fd = fd; + sv->two_fd[0].moved_to = CLOSED; + sv->next = redirlist; + redirlist = sv; +} +#endif + static struct redirtab* pushredir(union node *redir) { @@ -6529,10 +6591,20 @@ evaltreenr(union node *n, int flags) } static void FAST_FUNC -evalbackcmd(union node *n, struct backcmd *result) +evalbackcmd(union node *n, struct backcmd *result + IF_BASH_PROCESS_SUBST(, int ctl)) { int pip[2]; struct job *jp; +#if BASH_PROCESS_SUBST + /* 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; @@ -6544,15 +6616,17 @@ evalbackcmd(union node *n, struct backcmd *result) if (pipe(pip) < 0) ash_msg_and_raise_perror("can't create pipe"); - jp = makejob(/*n,*/ 1); - if (forkshell(jp, n, FORK_NOJOB) == 0) { + /* process substitution uses NULL job/node, like openhere() */ + jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; + if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) { /* child */ FORCE_INT_ON; - close(pip[0]); - if (pip[1] != 1) { - /*close(1);*/ - dup2_or_raise(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);*/ + dup2_or_raise(pip[ic], ic); + close(pip[ic]); } /* TODO: eflag clearing makes the following not abort: * ash -c 'set -e; z=$(false;echo foo); echo $z' @@ -6568,8 +6642,18 @@ evalbackcmd(union node *n, struct backcmd *result) /* NOTREACHED */ } /* parent */ - close(pip[1]); - result->fd = pip[0]; +#if BASH_PROCESS_SUBST + if (ctl != CTLBACKQ) { + int fd = fcntl(pip[ip], F_DUPFD, 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: @@ -6581,8 +6665,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 IF_BASH_PROCESS_SUBST(, int ctl)) { +#if !BASH_PROCESS_SUBST + const int ctl = CTLBACKQ; +#endif struct backcmd in; int i; char buf[128]; @@ -6597,9 +6684,15 @@ expbackq(union node *cmd, int flag) INT_OFF; startloc = expdest - (char *)stackblock(); pushstackmark(&smark, startloc); - evalbackcmd(cmd, &in); + evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl)); popstackmark(&smark); + if (ctl != CTLBACKQ) { + sprintf(buf, DEV_FD_PREFIX"%d", in.fd); + strtodest(buf, BASESYNTAX); + goto done; + } + p = in.buf; i = in.nleft; if (i == 0) @@ -6621,6 +6714,7 @@ expbackq(union node *cmd, int flag) close(in.fd); back_exitstatus = waitforjob(in.jp); } + done: INT_ON; /* Eat all trailing newlines */ @@ -6708,6 +6802,10 @@ argstr(char *p, int flag) CTLESC, CTLVAR, CTLBACKQ, +#if BASH_PROCESS_SUBST + CTLTOPROC, + CTLFROMPROC, +#endif #if ENABLE_FEATURE_SH_MATH CTLARI, CTLENDARI, @@ -6807,8 +6905,12 @@ argstr(char *p, int flag) p = evalvar(p, flag | inquotes); TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock())); goto start; +#if BASH_PROCESS_SUBST + case CTLTOPROC: + case CTLFROMPROC: +#endif case CTLBACKQ: - expbackq(argbackq->n, flag | inquotes); + expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c)); goto start; #if ENABLE_FEATURE_SH_MATH case CTLARI: @@ -12198,8 +12300,9 @@ realeofmark(const char *eofmark) #define CHECKEND() {goto checkend; checkend_return:;} #define PARSEREDIR() {goto parseredir; parseredir_return:;} #define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;} +#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} static int readtoken1(int c, int syntax, char *eofmark, int striptabs) @@ -12210,7 +12313,9 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) size_t 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 */ IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;) /* syntax stack */ @@ -12391,6 +12496,15 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) c = 0x100 + '>'; /* flag &> */ pungetc(); } +#endif +#if BASH_PROCESS_SUBST + if (c == '<' || c == '>') { + if (pgetc() == '(') { + PARSEPROCSUB(); + break; + } + pungetc(); + } #endif goto endword; /* exit outer loop */ } @@ -12876,9 +12990,18 @@ parsebackq: { memcpy(out, str, savelen); STADJUST(savelen, out); } - USTPUTC(CTLBACKQ, out); +#if BASH_PROCESS_SUBST + if (style == PSUB) + USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out); + else +#endif + USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; +#if BASH_PROCESS_SUBST + else if (style == PSUB) + goto parsebackq_psreturn; +#endif goto parsebackq_newreturn; } @@ -13329,6 +13452,9 @@ cmdloop(int top) #if JOBS if (doing_jobctl) showjobs(SHOW_CHANGED|SHOW_STDERR); +#endif +#if BASH_PROCESS_SUBST + unwindredir(NULL); #endif inter = 0; if (iflag && top) { diff --git a/shell/ash_test/ash-psubst/bash_procsub.right b/shell/ash_test/ash-psubst/bash_procsub.right new file mode 100644 index 000000000..aa16a96be --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.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/ash_test/ash-psubst/bash_procsub.tests b/shell/ash_test/ash-psubst/bash_procsub.tests new file mode 100755 index 000000000..63b836782 --- /dev/null +++ b/shell/ash_test/ash-psubst/bash_procsub.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/proc_subst +# 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 -- cgit v1.2.3-55-g6feb From 457825f77a7c7286647ee888a1000a6bb12ca8fc Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 6 Jun 2021 12:07:11 +0200 Subject: shells: do not allow bare "read" in non-bash compat configs On Sat, Feb 9, 2019 Cristian Ionescu-Idbohrn wrote: > In my case (at work), I have to watch and prevent people from doing > unportable things. For me, that's a burden. Signed-off-by: Denys Vlasenko --- libbb/getopt32.c | 11 +++++++---- shell/ash.c | 4 ++++ shell/hush.c | 13 ++++++++++--- shell/shell_common.c | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) (limited to 'shell') diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 378510063..5ab4d66f1 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c @@ -89,6 +89,12 @@ getopt32(char **argv, const char *applet_opts, ...) root:x:0:0:root:/root:/bin/bash user:x:500:500::/home/user:/bin/bash + "^" options string is "^optchars""\0""opt_complementary". + + "!" If the first character in the applet_opts string is a '!', + report bad options, missing required options, + inconsistent options with all-ones return value (instead of abort. + "+" If the first character in the applet_opts string is a plus, then option processing will stop as soon as a non-option is encountered in the argv array. Useful for applets like env @@ -96,10 +102,7 @@ getopt32(char **argv, const char *applet_opts, ...) env -i ls -d / Here we want env to process just the '-i', not the '-d'. - "!" Report bad options, missing required options, - inconsistent options with all-ones return value (instead of abort). - - "^" options string is "^optchars""\0""opt_complementary". + (The order of multiple prefixes must be "^!+...") uint32_t getopt32long(char **argv, const char *applet_opts, const char *logopts...) diff --git a/shell/ash.c b/shell/ash.c index 05c47950f..bcf7a3470 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -14161,6 +14161,10 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } } + if (!ENABLE_ASH_BASH_COMPAT && !argptr) { + bb_simple_error_msg("read: need variable name"); + return 1; + } params.argv = argptr; params.setvar = setvar0; params.ifs = bltinlookup("IFS"); /* can be NULL */ diff --git a/shell/hush.c b/shell/hush.c index 144ad3edd..77921e11c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -4251,7 +4251,7 @@ static int done_word(struct parse_context *ctx) || endofname(command->argv[0])[0] != '\0' ) { /* bash says just "not a valid identifier" */ - syntax_error("not a valid identifier in for"); + syntax_error("bad variable name in for"); return 1; } /* Force FOR to have just one word (variable name) */ @@ -10799,10 +10799,17 @@ static int FAST_FUNC builtin_read(char **argv) */ params.read_flags = getopt32(argv, # if BASH_READ_D - "!srn:p:t:u:d:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d + IF_NOT_HUSH_BASH_COMPAT("^") + "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/), + ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u, ¶ms.opt_d # else - "!srn:p:t:u:", ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u + IF_NOT_HUSH_BASH_COMPAT("^") + "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/), + ¶ms.opt_n, ¶ms.opt_p, ¶ms.opt_t, ¶ms.opt_u # endif +//TODO: print "read: need variable name" +//for the case of !BASH "read" with no args (now it fails silently) +//(or maybe extend getopt32() to emit a message if "-1" fails) ); if ((uint32_t)params.read_flags == (uint32_t)-1) return EXIT_FAILURE; diff --git a/shell/shell_common.c b/shell/shell_common.c index e3d6783b5..2e36d9208 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c @@ -59,7 +59,7 @@ shell_builtin_read(struct builtin_read_params *params) while (*pp) { if (endofname(*pp)[0] != '\0') { /* Mimic bash message */ - bb_error_msg("read: '%s': not a valid identifier", *pp); + bb_error_msg("read: '%s': bad variable name", *pp); return (const char *)(uintptr_t)1; } pp++; -- cgit v1.2.3-55-g6feb From 4c4b02c290ce8d24c467964eb25f5bfc8fc98c8b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 6 Jun 2021 13:00:20 +0200 Subject: ash: save Ron's patch from oblivion Signed-off-by: Denys Vlasenko --- ...e_unnecessary_code_in_backquote_expansion.patch | 135 +++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 shell/ash_remove_unnecessary_code_in_backquote_expansion.patch (limited to 'shell') diff --git a/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch b/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch new file mode 100644 index 000000000..06067dde0 --- /dev/null +++ b/shell/ash_remove_unnecessary_code_in_backquote_expansion.patch @@ -0,0 +1,135 @@ +From: Herbert Xu +Date: Thu, 19 Apr 2018 18:16:12 +0800 + +> ash originally had support for omitting the fork when expanding a +> builtin in backquotes. dash has gradually been removing this support, +> most recently in commit 66b614e29038e31745c4a5d296f64f8d64f5c377 +> ("[EVAL] Remove unused EV_BACKCMD flag"). +> +> Some traces still remain, however. Remove: +> +> - the buf and nleft elements of the backcmd structure; +> - a misleading comment regarding handling of builtins. +> +> Signed-off-by: Ron Yorston + +Unfortunately we may need this at some point in the future due +to changes in POSIX. So let's keep it around for now until we +get things such as `jobs -p` to work. + +************************************* + +From: Ron Yorston +Date: Thu, 19 Apr 2018 17:18:47 +0100 + +>Unfortunately we may need this at some point in the future due +>to changes in POSIX. So let's keep it around for now until we +>get things such as `jobs -p` to work. + +As you wish. + +Something even more trivial I noticed later: the TRACE at the end of +expbackq incorrectly refers to the function as evalbackq. + +************************************* + +Date: Tue, 10 Apr 2018 13:23:35 +0100 +From: Ron Yorston +To: busybox@busybox.net +Subject: [PATCH] ash: remove unnecessary code in backquote expansion + +Some traces remain of ash's ancient support for omitting the fork when +expanding a builtin command in backquotes. + +Remove: + +- the buf and nleft elements of the backcmd structure; +- a misleading comment regarding handling of builtins. + +I've submitted a similar patch to dash. + +Signed-off-by: Ron Yorston +--- + shell/ash.c | 37 +++++++++---------------------------- + 1 file changed, 9 insertions(+), 28 deletions(-) + +diff --git a/shell/ash.c b/shell/ash.c +index 45c747dbc..6f1458722 100644 +--- a/shell/ash.c ++++ b/shell/ash.c +@@ -6356,15 +6356,12 @@ exptilde(char *startp, char *p, int flags) + } + + /* +- * Execute a command inside back quotes. If it's a builtin command, we +- * want to save its output in a block obtained from malloc. Otherwise +- * we fork off a subprocess and get the output of the command via a pipe. +- * Should be called with interrupts off. ++ * Execute a command inside back quotes. We fork off a subprocess and ++ * get the output of the command via a pipe. Should be called with ++ * interrupts off. + */ + struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ +- int nleft; /* number of chars in buffer */ +- char *buf; /* buffer */ + struct job *jp; /* job structure for command */ + }; + +@@ -6394,8 +6391,6 @@ evalbackcmd(union node *n, struct backcmd *result) + struct job *jp; + + result->fd = -1; +- result->buf = NULL; +- result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + goto out; +@@ -6432,8 +6427,7 @@ evalbackcmd(union node *n, struct backcmd *result) + result->jp = jp; + + out: +- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", +- result->fd, result->buf, result->nleft, result->jp)); ++ TRACE(("evalbackcmd done: fd=%d jp=0x%x\n", result->fd, result->jp)); + } + + /* +@@ -6445,7 +6439,6 @@ expbackq(union node *cmd, int flag) + struct backcmd in; + int i; + char buf[128]; +- char *p; + char *dest; + int startloc; + int syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX; +@@ -6457,24 +6450,12 @@ expbackq(union node *cmd, int flag) + evalbackcmd(cmd, &in); + popstackmark(&smark); + +- p = in.buf; +- i = in.nleft; +- if (i == 0) +- goto read; +- for (;;) { +- memtodest(p, i, syntax, flag & QUOTES_ESC); +- read: +- if (in.fd < 0) +- break; +- i = nonblock_immune_read(in.fd, buf, sizeof(buf)); +- TRACE(("expbackq: read returns %d\n", i)); +- if (i <= 0) +- break; +- p = buf; +- } +- +- free(in.buf); + if (in.fd >= 0) { ++ while ((i = nonblock_immune_read(in.fd, buf, sizeof(buf))) > 0) { ++ TRACE(("expbackq: read returns %d\n", i)); ++ memtodest(buf, i, syntax, flag & QUOTES_ESC); ++ } ++ + close(in.fd); + back_exitstatus = waitforjob(in.jp); + } -- cgit v1.2.3-55-g6feb