From 0cd86463619ef45569c5d9b6faf5cd9caff9ab00 Mon Sep 17 00:00:00 2001 From: Johnothan King Date: Thu, 8 Apr 2021 05:24:17 -0700 Subject: [PATCH] Backport bugfix for BUG_CSUBSTDO from ksh93v- 2012-08-24 (#259) This commit fixes BUG_CSUBSTDO, which could break stdout inside of non-forking command substitutions. The breakage only occurred when stdout was closed outside of the command substitution and a file descriptor other than stdout was redirected in the command substitution (such as stderr). Thanks to the ast-open-history repo, I was able to identify and backport the bugfix from ksh93v- 2012-08-24. This backport may fix other bugs as well. On 93v- 2012-08-24 it fixed the regression below, though it was not triggered on 93u+(m). src/cmd/ksh93/tests/heredoc.sh 487 print foo > $tmp/foofile 488 x=$( $SHELL 2> /dev/null 'read <<< $(<'"$tmp"'/foofile) 2> /dev/null;print -r "$REPLY"') 489 [[ $x == foo ]] || err_exit '<<< $(= 10. src/cmd/ksh93/tests/io.sh: - Add a regression test for BUG_CSUBSTDO, adapted from the one in modernish. --- NEWS | 4 ++++ TODO | 6 ------ src/cmd/ksh93/sh/io.c | 11 +++++++++++ src/cmd/ksh93/tests/io.sh | 10 ++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 61f4c716a..3619dfef3 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ Any uppercase BUG_* names are modernish shell bug IDs. - The $LC_TIME variable is now recognized by ksh and if set to an invalid locale will show an error. +- Fixed BUG_CSUBSTDO: If standard output is closed before running a command + substitution, redirecting any other file descriptor no longer closes standard + output inside of the command substitution. + 2021-04-05: - Fixed a regression, introduced in ksh 93t+ 2009-07-31, that caused a command diff --git a/TODO b/TODO index 5719ff099..f6de0d4fe 100644 --- a/TODO +++ b/TODO @@ -23,12 +23,6 @@ https://github.com/modernish/modernish/tree/0.16/lib/modernish/cap/ initial ! (and, on some shells, ^) retains the meaning of negation, even in quoted strings within bracket patterns, including quoted variables. -- BUG_CSUBSTDO: If standard output (file descriptor 1) is closed before - entering a $(command substitution), and any other file descriptors are - redirected within the command substitution, commands such as 'echo' will - not work within the command substitution, acting as if standard output is - still closed. - - BUG_IFSGLOBS: In glob pattern matching (as in case or parameter substitution with # and %), if IFS starts with ? or * and the "$*" parameter expansion inserts any IFS separator characters, those characters diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c index 436c8e0d8..249c3f51d 100644 --- a/src/cmd/ksh93/sh/io.c +++ b/src/cmd/ksh93/sh/io.c @@ -755,6 +755,7 @@ onintr(struct addrinfo* addr, void* handle) int sh_open(register const char *path, int flags, ...) { Shell_t *shp = sh_getinterp(); + Sfio_t *sp; register int fd = -1; mode_t mode; char *e; @@ -869,6 +870,16 @@ int sh_open(register const char *path, int flags, ...) mode = IOREAD; if(fd >= shp->gd->lim.open_max) sh_iovalidfd(shp,fd); + if((sp = shp->sftable[fd]) && (sfset(sp,0,0) & SF_STRING)) + { + int n,err=errno; + if((n = sh_fcntl(fd,F_DUPFD,10)) >= 10) + { + while(close(fd) < 0 && errno == EINTR) + errno = err; + fd = n; + } + } shp->fdstatus[fd] = mode; return(fd); } diff --git a/src/cmd/ksh93/tests/io.sh b/src/cmd/ksh93/tests/io.sh index 5bf72564f..44aee85e8 100755 --- a/src/cmd/ksh93/tests/io.sh +++ b/src/cmd/ksh93/tests/io.sh @@ -785,5 +785,15 @@ then "(expected $(printf %q "$expect"), got $(printf %q "$actual"))" fi +# ====== +# Test for BUG_CSUBSTDO: If stdout is closed before running a command substitution, +# redirecting any file descriptor in the command substitution would break stdout +# inside of the command substitution. This only occurred when redirecting any other +# file descriptor inside of the command substitution. +exp='Foo bar' +{ got=$(echo 'Foo bar' 2>/dev/null); } >&- +[[ $exp == $got ]] || err_exit "BUG_CSUBSTDO: Closing stdout outside of command substitution breaks stdout inside of command substitution" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" + # ====== exit $((Errors<125?Errors:125))