aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2018-04-02 09:24:14 +0100
committerRon Yorston <rmy@pobox.com>2018-04-02 09:24:14 +0100
commit34a68d327b42c3c700e84cd475496985782290b1 (patch)
tree99bfe59cca420d26f01e81a7f41763f71b44d22c /shell
parentaff3c5bd7b6bdcfb97f63153ab839c5f55f16a12 (diff)
parente84212f8346741a2d4a04b40639c44fe519cf5a7 (diff)
downloadbusybox-w32-34a68d327b42c3c700e84cd475496985782290b1.tar.gz
busybox-w32-34a68d327b42c3c700e84cd475496985782290b1.tar.bz2
busybox-w32-34a68d327b42c3c700e84cd475496985782290b1.zip
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c102
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_backquote1.right5
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_backquote1.tests10
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_backslash1.right43
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_backslash1.tests70
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.right8
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_bkslash_newline1.tests25
-rw-r--r--shell/ash_test/ash-parsing/bkslash_newline1.right4
-rwxr-xr-xshell/ash_test/ash-parsing/bkslash_newline1.tests8
-rw-r--r--shell/ash_test/ash-parsing/bkslash_newline2.right4
-rwxr-xr-xshell/ash_test/ash-parsing/bkslash_newline2.tests4
-rw-r--r--shell/ash_test/ash-quoting/dollar_repl_slash_bash2.right1
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_slash_bash2.tests2
-rw-r--r--shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.right5
-rwxr-xr-xshell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.tests6
-rw-r--r--shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.right5
-rwxr-xr-xshell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.tests6
-rw-r--r--shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.right5
-rwxr-xr-xshell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.tests6
-rw-r--r--shell/ash_test/ash-vars/var_LINENO1.right8
-rwxr-xr-xshell/ash_test/ash-vars/var_LINENO1.tests6
-rw-r--r--shell/hush.c149
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.right8
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_bkslash_newline1.tests25
-rw-r--r--shell/hush_test/hush-parsing/bkslash_newline1.right4
-rwxr-xr-xshell/hush_test/hush-parsing/bkslash_newline1.tests8
-rw-r--r--shell/hush_test/hush-parsing/bkslash_newline2.right4
-rwxr-xr-xshell/hush_test/hush-parsing/bkslash_newline2.tests4
-rw-r--r--shell/hush_test/hush-quoting/dollar_repl_slash_bash2.right1
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_repl_slash_bash2.tests2
-rw-r--r--shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.right5
-rwxr-xr-xshell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.tests6
-rw-r--r--shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.right5
-rwxr-xr-xshell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.tests6
-rw-r--r--shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.right5
-rwxr-xr-xshell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.tests6
-rwxr-xr-xshell/hush_test/hush-vars/var_LINENO1.tests2
37 files changed, 466 insertions, 107 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 2ab46576c..7448b668a 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -282,6 +282,12 @@ typedef long arith_t;
282# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__ 282# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
283#endif 283#endif
284 284
285#ifndef F_DUPFD_CLOEXEC
286# define F_DUPFD_CLOEXEC F_DUPFD
287#endif
288#ifndef O_CLOEXEC
289# define O_CLOEXEC 0
290#endif
285#ifndef PIPE_BUF 291#ifndef PIPE_BUF
286# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 292# define PIPE_BUF 4096 /* amount of buffering in a pipe */
287#endif 293#endif
@@ -4196,12 +4202,13 @@ setjobctl(int on)
4196 goto out; 4202 goto out;
4197 } 4203 }
4198 /* fd is a tty at this point */ 4204 /* fd is a tty at this point */
4199 fd = fcntl(fd, F_DUPFD, 10); 4205 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
4200 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */ 4206 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
4201 close(ofd); 4207 close(ofd);
4202 if (fd < 0) 4208 if (fd < 0)
4203 goto out; /* F_DUPFD failed */ 4209 goto out; /* F_DUPFD failed */
4204 close_on_exec_on(fd); 4210 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4211 close_on_exec_on(fd);
4205 while (1) { /* while we are in the background */ 4212 while (1) { /* while we are in the background */
4206 pgrp = tcgetpgrp(fd); 4213 pgrp = tcgetpgrp(fd);
4207 if (pgrp < 0) { 4214 if (pgrp < 0) {
@@ -5791,13 +5798,14 @@ savefd(int from)
5791 int newfd; 5798 int newfd;
5792 int err; 5799 int err;
5793 5800
5794 newfd = fcntl(from, F_DUPFD, 10); 5801 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
5795 err = newfd < 0 ? errno : 0; 5802 err = newfd < 0 ? errno : 0;
5796 if (err != EBADF) { 5803 if (err != EBADF) {
5797 if (err) 5804 if (err)
5798 ash_msg_and_raise_perror("%d", from); 5805 ash_msg_and_raise_perror("%d", from);
5799 close(from); 5806 close(from);
5800 fcntl(newfd, F_SETFD, FD_CLOEXEC); 5807 if (F_DUPFD_CLOEXEC == F_DUPFD)
5808 close_on_exec_on(newfd);
5801 } 5809 }
5802 5810
5803 return newfd; 5811 return newfd;
@@ -5815,12 +5823,15 @@ dup2_or_raise(int from, int to)
5815 return newfd; 5823 return newfd;
5816} 5824}
5817static int 5825static int
5818fcntl_F_DUPFD(int fd, int avoid_fd) 5826dup_CLOEXEC(int fd, int avoid_fd)
5819{ 5827{
5820 int newfd; 5828 int newfd;
5821 repeat: 5829 repeat:
5822 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); 5830 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5823 if (newfd < 0) { 5831 if (newfd >= 0) {
5832 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
5833 close_on_exec_on(newfd);
5834 } else { /* newfd < 0 */
5824 if (errno == EBUSY) 5835 if (errno == EBUSY)
5825 goto repeat; 5836 goto repeat;
5826 if (errno == EINTR) 5837 if (errno == EINTR)
@@ -5833,7 +5844,7 @@ xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5833{ 5844{
5834 int newfd; 5845 int newfd;
5835 repeat: 5846 repeat:
5836 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); 5847 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5837 if (newfd < 0) { 5848 if (newfd < 0) {
5838 if (errno == EBUSY) 5849 if (errno == EBUSY)
5839 goto repeat; 5850 goto repeat;
@@ -5844,7 +5855,8 @@ xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5844 return fd; 5855 return fd;
5845 ash_msg_and_raise_perror("%d", newfd); 5856 ash_msg_and_raise_perror("%d", newfd);
5846 } 5857 }
5847 fcntl(newfd, F_SETFD, FD_CLOEXEC); 5858 if (F_DUPFD_CLOEXEC == F_DUPFD)
5859 close_on_exec_on(newfd);
5848 close(fd); 5860 close(fd);
5849 return newfd; 5861 return newfd;
5850} 5862}
@@ -5936,7 +5948,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5936 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) { 5948 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5937 /* If we collide with an already moved fd... */ 5949 /* If we collide with an already moved fd... */
5938 if (fd == sq->two_fd[i].moved_to) { 5950 if (fd == sq->two_fd[i].moved_to) {
5939 new_fd = fcntl_F_DUPFD(fd, avoid_fd); 5951 new_fd = dup_CLOEXEC(fd, avoid_fd);
5940 sq->two_fd[i].moved_to = new_fd; 5952 sq->two_fd[i].moved_to = new_fd;
5941 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); 5953 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5942 if (new_fd < 0) /* what? */ 5954 if (new_fd < 0) /* what? */
@@ -5951,7 +5963,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5951 } 5963 }
5952 5964
5953 /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */ 5965 /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */
5954 new_fd = fcntl_F_DUPFD(fd, avoid_fd); 5966 new_fd = dup_CLOEXEC(fd, avoid_fd);
5955 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); 5967 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5956 if (new_fd < 0) { 5968 if (new_fd < 0) {
5957 if (errno != EBADF) 5969 if (errno != EBADF)
@@ -7559,7 +7571,7 @@ varvalue(char *name, int varflags, int flags, int *quotedp)
7559 case '-': 7571 case '-':
7560 expdest = makestrspace(NOPTS, expdest); 7572 expdest = makestrspace(NOPTS, expdest);
7561 for (i = NOPTS - 1; i >= 0; i--) { 7573 for (i = NOPTS - 1; i >= 0; i--) {
7562 if (optlist[i]) { 7574 if (optlist[i] && optletters(i)) {
7563 USTPUTC(optletters(i), expdest); 7575 USTPUTC(optletters(i), expdest);
7564 len++; 7576 len++;
7565 } 7577 }
@@ -7806,13 +7818,13 @@ hasmeta(const char *p)
7806 p = strpbrk(p, chars); 7818 p = strpbrk(p, chars);
7807 if (!p) 7819 if (!p)
7808 break; 7820 break;
7809 switch ((unsigned char) *p) { 7821 switch ((unsigned char)*p) {
7810 case CTLQUOTEMARK: 7822 case CTLQUOTEMARK:
7811 for (;;) { 7823 for (;;) {
7812 p++; 7824 p++;
7813 if (*p == CTLQUOTEMARK) 7825 if ((unsigned char)*p == CTLQUOTEMARK)
7814 break; 7826 break;
7815 if (*p == CTLESC) 7827 if ((unsigned char)*p == CTLESC)
7816 p++; 7828 p++;
7817 if (*p == '\0') /* huh? */ 7829 if (*p == '\0') /* huh? */
7818 return 0; 7830 return 0;
@@ -11262,7 +11274,7 @@ setinputfile(const char *fname, int flags)
11262 int fd; 11274 int fd;
11263 11275
11264 INT_OFF; 11276 INT_OFF;
11265 fd = open(fname, O_RDONLY); 11277 fd = open(fname, O_RDONLY | O_CLOEXEC);
11266 if (fd < 0) { 11278 if (fd < 0) {
11267 if (flags & INPUT_NOFILE_OK) 11279 if (flags & INPUT_NOFILE_OK)
11268 goto out; 11280 goto out;
@@ -11271,8 +11283,9 @@ setinputfile(const char *fname, int flags)
11271 } 11283 }
11272 if (fd < 10) 11284 if (fd < 10)
11273 fd = savefd(fd); 11285 fd = savefd(fd);
11274 else 11286 else if (O_CLOEXEC == 0) /* old libc */
11275 close_on_exec_on(fd); 11287 close_on_exec_on(fd);
11288
11276 setinputfd(fd, flags & INPUT_PUSH_FILE); 11289 setinputfd(fd, flags & INPUT_PUSH_FILE);
11277 out: 11290 out:
11278 INT_ON; 11291 INT_ON;
@@ -12595,6 +12608,12 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
12595 break; 12608 break;
12596#endif 12609#endif
12597 case CBQUOTE: /* '`' */ 12610 case CBQUOTE: /* '`' */
12611 if (checkkwd & CHKEOFMARK) {
12612 quotef = 1;
12613 USTPUTC('`', out);
12614 break;
12615 }
12616
12598 PARSEBACKQOLD(); 12617 PARSEBACKQOLD();
12599 break; 12618 break;
12600 case CENDFILE: 12619 case CENDFILE:
@@ -12727,7 +12746,7 @@ parseredir: {
12727 np = stzalloc(sizeof(struct nfile)); 12746 np = stzalloc(sizeof(struct nfile));
12728 if (c == '>') { 12747 if (c == '>') {
12729 np->nfile.fd = 1; 12748 np->nfile.fd = 1;
12730 c = pgetc(); 12749 c = pgetc_eatbnl();
12731 if (c == '>') 12750 if (c == '>')
12732 np->type = NAPPEND; 12751 np->type = NAPPEND;
12733 else if (c == '|') 12752 else if (c == '|')
@@ -12749,7 +12768,7 @@ parseredir: {
12749#endif 12768#endif
12750 else { /* c == '<' */ 12769 else { /* c == '<' */
12751 /*np->nfile.fd = 0; - stzalloc did it */ 12770 /*np->nfile.fd = 0; - stzalloc did it */
12752 c = pgetc(); 12771 c = pgetc_eatbnl();
12753 switch (c) { 12772 switch (c) {
12754 case '<': 12773 case '<':
12755 if (sizeof(struct nfile) != sizeof(struct nhere)) { 12774 if (sizeof(struct nfile) != sizeof(struct nhere)) {
@@ -12759,7 +12778,7 @@ parseredir: {
12759 np->type = NHERE; 12778 np->type = NHERE;
12760 heredoc = stzalloc(sizeof(struct heredoc)); 12779 heredoc = stzalloc(sizeof(struct heredoc));
12761 heredoc->here = np; 12780 heredoc->here = np;
12762 c = pgetc(); 12781 c = pgetc_eatbnl();
12763 if (c == '-') { 12782 if (c == '-') {
12764 heredoc->striptabs = 1; 12783 heredoc->striptabs = 1;
12765 } else { 12784 } else {
@@ -12989,23 +13008,13 @@ parsebackq: {
12989 int pc; 13008 int pc;
12990 13009
12991 setprompt_if(needprompt, 2); 13010 setprompt_if(needprompt, 2);
12992 pc = pgetc(); 13011 pc = pgetc_eatbnl();
12993 switch (pc) { 13012 switch (pc) {
12994 case '`': 13013 case '`':
12995 goto done; 13014 goto done;
12996 13015
12997 case '\\': 13016 case '\\':
12998 pc = pgetc(); 13017 pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */
12999 if (pc == '\n') {
13000 nlprompt();
13001 /*
13002 * If eating a newline, avoid putting
13003 * the newline into the new character
13004 * stream (via the STPUTC after the
13005 * switch).
13006 */
13007 continue;
13008 }
13009 if (pc != '\\' && pc != '`' && pc != '$' 13018 if (pc != '\\' && pc != '`' && pc != '$'
13010 && (!dblquote || pc != '"') 13019 && (!dblquote || pc != '"')
13011 ) { 13020 ) {
@@ -13137,7 +13146,7 @@ xxreadtoken(void)
13137 } 13146 }
13138 setprompt_if(needprompt, 2); 13147 setprompt_if(needprompt, 2);
13139 for (;;) { /* until token or start of word found */ 13148 for (;;) { /* until token or start of word found */
13140 c = pgetc(); 13149 c = pgetc_eatbnl();
13141 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) 13150 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
13142 continue; 13151 continue;
13143 13152
@@ -13146,11 +13155,7 @@ xxreadtoken(void)
13146 continue; 13155 continue;
13147 pungetc(); 13156 pungetc();
13148 } else if (c == '\\') { 13157 } else if (c == '\\') {
13149 if (pgetc() != '\n') { 13158 break; /* return readtoken1(...) */
13150 pungetc();
13151 break; /* return readtoken1(...) */
13152 }
13153 nlprompt();
13154 } else { 13159 } else {
13155 const char *p; 13160 const char *p;
13156 13161
@@ -13165,7 +13170,7 @@ xxreadtoken(void)
13165 break; /* return readtoken1(...) */ 13170 break; /* return readtoken1(...) */
13166 13171
13167 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) { 13172 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
13168 int cc = pgetc(); 13173 int cc = pgetc_eatbnl();
13169 if (cc == c) { /* double occurrence? */ 13174 if (cc == c) { /* double occurrence? */
13170 p += xxreadtoken_doubles + 1; 13175 p += xxreadtoken_doubles + 1;
13171 } else { 13176 } else {
@@ -13197,7 +13202,7 @@ xxreadtoken(void)
13197 } 13202 }
13198 setprompt_if(needprompt, 2); 13203 setprompt_if(needprompt, 2);
13199 for (;;) { /* until token or start of word found */ 13204 for (;;) { /* until token or start of word found */
13200 c = pgetc(); 13205 c = pgetc_eatbnl();
13201 switch (c) { 13206 switch (c) {
13202 case ' ': case '\t': 13207 case ' ': case '\t':
13203 IF_ASH_ALIAS(case PEOA:) 13208 IF_ASH_ALIAS(case PEOA:)
@@ -13207,30 +13212,23 @@ xxreadtoken(void)
13207 continue; 13212 continue;
13208 pungetc(); 13213 pungetc();
13209 continue; 13214 continue;
13210 case '\\':
13211 if (pgetc() == '\n') {
13212 nlprompt();
13213 continue;
13214 }
13215 pungetc();
13216 goto breakloop;
13217 case '\n': 13215 case '\n':
13218 nlnoprompt(); 13216 nlnoprompt();
13219 RETURN(TNL); 13217 RETURN(TNL);
13220 case PEOF: 13218 case PEOF:
13221 RETURN(TEOF); 13219 RETURN(TEOF);
13222 case '&': 13220 case '&':
13223 if (pgetc() == '&') 13221 if (pgetc_eatbnl() == '&')
13224 RETURN(TAND); 13222 RETURN(TAND);
13225 pungetc(); 13223 pungetc();
13226 RETURN(TBACKGND); 13224 RETURN(TBACKGND);
13227 case '|': 13225 case '|':
13228 if (pgetc() == '|') 13226 if (pgetc_eatbnl() == '|')
13229 RETURN(TOR); 13227 RETURN(TOR);
13230 pungetc(); 13228 pungetc();
13231 RETURN(TPIPE); 13229 RETURN(TPIPE);
13232 case ';': 13230 case ';':
13233 if (pgetc() == ';') 13231 if (pgetc_eatbnl() == ';')
13234 RETURN(TENDCASE); 13232 RETURN(TENDCASE);
13235 pungetc(); 13233 pungetc();
13236 RETURN(TSEMI); 13234 RETURN(TSEMI);
@@ -13238,11 +13236,9 @@ xxreadtoken(void)
13238 RETURN(TLP); 13236 RETURN(TLP);
13239 case ')': 13237 case ')':
13240 RETURN(TRP); 13238 RETURN(TRP);
13241 default:
13242 goto breakloop;
13243 } 13239 }
13240 break;
13244 } 13241 }
13245 breakloop:
13246 return readtoken1(c, BASESYNTAX, (char *)NULL, 0); 13242 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13247#undef RETURN 13243#undef RETURN
13248} 13244}
diff --git a/shell/ash_test/ash-heredoc/heredoc_backquote1.right b/shell/ash_test/ash-heredoc/heredoc_backquote1.right
new file mode 100644
index 000000000..0be2a3296
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_backquote1.right
@@ -0,0 +1,5 @@
1heredoc1
2Ok1:0
3heredoc2
4EO`false`F
5Ok2:0
diff --git a/shell/ash_test/ash-heredoc/heredoc_backquote1.tests b/shell/ash_test/ash-heredoc/heredoc_backquote1.tests
new file mode 100755
index 000000000..ec3d8fe1d
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_backquote1.tests
@@ -0,0 +1,10 @@
1cat <<EO`true`F
2heredoc1
3EO`true`F
4echo Ok1:$?
5
6cat <<EO`true`F
7heredoc2
8EO`false`F
9EO`true`F
10echo Ok2:$?
diff --git a/shell/ash_test/ash-heredoc/heredoc_backslash1.right b/shell/ash_test/ash-heredoc/heredoc_backslash1.right
new file mode 100644
index 000000000..6a6114821
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_backslash1.right
@@ -0,0 +1,43 @@
1Quoted heredoc:
2a\
3 b
4a\\
5 b
6 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
7 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
8 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
9 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
10c\
11
12Unquoted heredoc:
13a b
14a\
15 b
16 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
17 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
18 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
19 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
20cEOF2
21
22Quoted -heredoc:
23a\
24b
25a\\
26b
27 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
29 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
30 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
31c\
32
33Unquoted -heredoc:
34a b
35a\
36b
37 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
38-qwerty-\t-\-\"-\'-`-\--\z-\*-\?-
39 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?-
40 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-
41cEOF4
42
43Done: 0
diff --git a/shell/ash_test/ash-heredoc/heredoc_backslash1.tests b/shell/ash_test/ash-heredoc/heredoc_backslash1.tests
new file mode 100755
index 000000000..501af5490
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_backslash1.tests
@@ -0,0 +1,70 @@
1# Test for correct handling of backslashes.
2# Note that some lines in each heredoc start with a tab.
3
4a=qwerty
5
6echo Quoted heredoc:
7cat <<"EOF1"
8a\
9 b
10a\\
11 b
12 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
13 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
14 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
15 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
16c\
17EOF1
18echo
19
20echo Unquoted heredoc:
21cat <<EOF2
22a\
23 b
24a\\
25 b
26 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
27 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
28 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
29 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
30c\
31EOF2
32EOF2
33echo
34
35echo Quoted -heredoc:
36cat <<-"EOF3"
37a\
38 b
39a\\
40 b
41 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
42 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
43 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'`
44 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-')
45c\
46 EOF3
47# In -heredoc case the marker is detected even if it is indented.
48echo
49
50echo Unquoted -heredoc:
51cat <<-EOF4
52a\
53 b
54a\\
55 b
56 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
57 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?-
58 123456 `echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`
59 123456 $(echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-')
60c\
61EOF4
62 EOF4
63# The marker is not detected if preceding line ends in backslash.
64# TODO: marker should be detected even if it is split by line continuation:
65# EOF\
66# 4
67# but currently hush doesn't do it. (Tab before "4" is not allowed, though.)
68echo
69
70echo "Done: $?"
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.right b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.right
new file mode 100644
index 000000000..fdb7ebd03
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.right
@@ -0,0 +1,8 @@
1heredoc0
2Ok0:0
3heredoc1
4Ok1:0
5heredoc2
6Ok2:0
7heredoc3
8Ok4:0
diff --git a/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.tests b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.tests
new file mode 100755
index 000000000..584edd0e4
--- /dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc_bkslash_newline1.tests
@@ -0,0 +1,25 @@
1cat <\
2<\
3EOF
4heredoc0
5EOF
6echo Ok0:$?
7
8cat <<\
9 EOF
10heredoc1
11EOF
12echo Ok1:$?
13
14cat <<\
15- EOF
16heredoc2
17 EOF
18echo Ok2:$?
19
20cat <\
21<\
22- EOF
23heredoc3
24 EOF
25echo Ok4:$?
diff --git a/shell/ash_test/ash-parsing/bkslash_newline1.right b/shell/ash_test/ash-parsing/bkslash_newline1.right
new file mode 100644
index 000000000..97ea0c197
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline1.right
@@ -0,0 +1,4 @@
1and1
2and2
3or1
4ok
diff --git a/shell/ash_test/ash-parsing/bkslash_newline1.tests b/shell/ash_test/ash-parsing/bkslash_newline1.tests
new file mode 100755
index 000000000..6e374c4fb
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline1.tests
@@ -0,0 +1,8 @@
1echo and1 &\
2& echo and2
3
4echo or1 |\
5| echo NOT SHOWN
6
7case w in a) echo SKIP;\
8; w) echo ok;; esac;
diff --git a/shell/ash_test/ash-parsing/bkslash_newline2.right b/shell/ash_test/ash-parsing/bkslash_newline2.right
new file mode 100644
index 000000000..c863c5453
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline2.right
@@ -0,0 +1,4 @@
1Line with one backslash:
2\
3
4Ok:0
diff --git a/shell/ash_test/ash-parsing/bkslash_newline2.tests b/shell/ash_test/ash-parsing/bkslash_newline2.tests
new file mode 100755
index 000000000..47d63042d
--- /dev/null
+++ b/shell/ash_test/ash-parsing/bkslash_newline2.tests
@@ -0,0 +1,4 @@
1echo Line with one backslash:
2echo '\
3'
4echo Ok:$?
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.right b/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.right
new file mode 100644
index 000000000..2aead7129
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.right
@@ -0,0 +1 @@
\/a\/bc\/def\/file
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.tests
new file mode 100755
index 000000000..64ca0c170
--- /dev/null
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash2.tests
@@ -0,0 +1,2 @@
1var="/a/bc/def/file"
2echo "${var//\//\\/}"
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.right b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.tests b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.tests
new file mode 100755
index 000000000..544c810e3
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_1.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4busybox find /proc/self/fd >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.right b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.tests b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.tests
new file mode 100755
index 000000000..43777cade
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_2.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4{ busybox find /proc/self/fd; } >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.right b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.tests b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.tests
new file mode 100755
index 000000000..0a21173bd
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_children_should_not_see_saved_fd_3.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4{ busybox find /proc/self/fd; true; } >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/ash_test/ash-vars/var_LINENO1.right b/shell/ash_test/ash-vars/var_LINENO1.right
new file mode 100644
index 000000000..31e1a4478
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO1.right
@@ -0,0 +1,8 @@
12:2
23:3
34:4
45:5
52:2
63:3
74:4
85:5
diff --git a/shell/ash_test/ash-vars/var_LINENO1.tests b/shell/ash_test/ash-vars/var_LINENO1.tests
new file mode 100755
index 000000000..775861e64
--- /dev/null
+++ b/shell/ash_test/ash-vars/var_LINENO1.tests
@@ -0,0 +1,6 @@
1env | grep ^LINENO
2echo 2:$LINENO
3echo 3:$LINENO >&2 \
4| { sleep 0.1; echo 4:$LINENO; }
5echo 5:$LINENO
6test "$1" || . ./var_LINENO1.tests norepeat
diff --git a/shell/hush.c b/shell/hush.c
index 6e64efb70..06fe0e405 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1501,12 +1501,15 @@ static void free_strings(char **strings)
1501 free(strings); 1501 free(strings);
1502} 1502}
1503 1503
1504static int fcntl_F_DUPFD(int fd, int avoid_fd) 1504static int dup_CLOEXEC(int fd, int avoid_fd)
1505{ 1505{
1506 int newfd; 1506 int newfd;
1507 repeat: 1507 repeat:
1508 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1); 1508 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1509 if (newfd < 0) { 1509 if (newfd >= 0) {
1510 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1511 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1512 } else { /* newfd < 0 */
1510 if (errno == EBUSY) 1513 if (errno == EBUSY)
1511 goto repeat; 1514 goto repeat;
1512 if (errno == EINTR) 1515 if (errno == EINTR)
@@ -2691,6 +2694,42 @@ static int i_peek2(struct in_str *i)
2691 return ch; 2694 return ch;
2692} 2695}
2693 2696
2697static int i_getch_and_eat_bkslash_nl(struct in_str *input)
2698{
2699 for (;;) {
2700 int ch, ch2;
2701
2702 ch = i_getch(input);
2703 if (ch != '\\')
2704 return ch;
2705 ch2 = i_peek(input);
2706 if (ch2 != '\n')
2707 return ch;
2708 /* backslash+newline, skip it */
2709 i_getch(input);
2710 }
2711}
2712
2713/* Note: this function _eats_ \<newline> pairs, safe to use plain
2714 * i_getch() after it instead of i_getch_and_eat_bkslash_nl().
2715 */
2716static int i_peek_and_eat_bkslash_nl(struct in_str *input)
2717{
2718 for (;;) {
2719 int ch, ch2;
2720
2721 ch = i_peek(input);
2722 if (ch != '\\')
2723 return ch;
2724 ch2 = i_peek2(input);
2725 if (ch2 != '\n')
2726 return ch;
2727 /* backslash+newline, skip it */
2728 i_getch(input);
2729 i_getch(input);
2730 }
2731}
2732
2694static void setup_file_in_str(struct in_str *i, FILE *f) 2733static void setup_file_in_str(struct in_str *i, FILE *f)
2695{ 2734{
2696 memset(i, 0, sizeof(*i)); 2735 memset(i, 0, sizeof(*i));
@@ -3119,7 +3158,7 @@ static int glob_brace(char *pattern, o_string *o, int n)
3119 return o_save_ptr_helper(o, n); 3158 return o_save_ptr_helper(o, n);
3120 } 3159 }
3121 if (gr == GLOB_NOSPACE) 3160 if (gr == GLOB_NOSPACE)
3122 bb_error_msg_and_die(bb_msg_memory_exhausted); 3161 bb_die_memory_exhausted();
3123 /* GLOB_ABORTED? Only happens with GLOB_ERR flag, 3162 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3124 * but we didn't specify it. Paranoia again. */ 3163 * but we didn't specify it. Paranoia again. */
3125 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); 3164 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
@@ -3221,7 +3260,7 @@ static int perform_glob(o_string *o, int n)
3221 goto literal; 3260 goto literal;
3222 } 3261 }
3223 if (gr == GLOB_NOSPACE) 3262 if (gr == GLOB_NOSPACE)
3224 bb_error_msg_and_die(bb_msg_memory_exhausted); 3263 bb_die_memory_exhausted();
3225 /* GLOB_ABORTED? Only happens with GLOB_ERR flag, 3264 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3226 * but we didn't specify it. Paranoia again. */ 3265 * but we didn't specify it. Paranoia again. */
3227 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern); 3266 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
@@ -3812,16 +3851,28 @@ static int done_word(o_string *word, struct parse_context *ctx)
3812 if (ctx->pending_redirect) { 3851 if (ctx->pending_redirect) {
3813 /* We do not glob in e.g. >*.tmp case. bash seems to glob here 3852 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
3814 * only if run as "bash", not "sh" */ 3853 * only if run as "bash", not "sh" */
3815 /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html 3854 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
3816 * "2.7 Redirection 3855 * "2.7 Redirection
3817 * ...the word that follows the redirection operator 3856 * If the redirection operator is "<<" or "<<-", the word
3818 * shall be subjected to tilde expansion, parameter expansion, 3857 * that follows the redirection operator shall be
3819 * command substitution, arithmetic expansion, and quote 3858 * subjected to quote removal; it is unspecified whether
3820 * removal. Pathname expansion shall not be performed 3859 * any of the other expansions occur. For the other
3860 * redirection operators, the word that follows the
3861 * redirection operator shall be subjected to tilde
3862 * expansion, parameter expansion, command substitution,
3863 * arithmetic expansion, and quote removal.
3864 * Pathname expansion shall not be performed
3821 * on the word by a non-interactive shell; an interactive 3865 * on the word by a non-interactive shell; an interactive
3822 * shell may perform it, but shall do so only when 3866 * shell may perform it, but shall do so only when
3823 * the expansion would result in one word." 3867 * the expansion would result in one word."
3824 */ 3868 */
3869//bash does not do parameter/command substitution or arithmetic expansion
3870//for _heredoc_ redirection word: these constructs look for exact eof marker
3871// as written:
3872// <<EOF$t
3873// <<EOF$((1))
3874// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
3875
3825 ctx->pending_redirect->rd_filename = xstrdup(word->data); 3876 ctx->pending_redirect->rd_filename = xstrdup(word->data);
3826 /* Cater for >\file case: 3877 /* Cater for >\file case:
3827 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a 3878 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
@@ -4011,7 +4062,7 @@ static int parse_redirect(struct parse_context *ctx,
4011 if (dup_num == REDIRFD_SYNTAX_ERR) 4062 if (dup_num == REDIRFD_SYNTAX_ERR)
4012 return 1; 4063 return 1;
4013 } else { 4064 } else {
4014 int ch = i_peek(input); 4065 int ch = i_peek_and_eat_bkslash_nl(input);
4015 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ 4066 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4016 if (dup_num) { /* <<-... */ 4067 if (dup_num) { /* <<-... */
4017 ch = i_getch(input); 4068 ch = i_getch(input);
@@ -4021,7 +4072,7 @@ static int parse_redirect(struct parse_context *ctx,
4021 } 4072 }
4022 4073
4023 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { 4074 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4024 int ch = i_peek(input); 4075 int ch = i_peek_and_eat_bkslash_nl(input);
4025 if (ch == '|') { 4076 if (ch == '|') {
4026 /* >|FILE redirect ("clobbering" >). 4077 /* >|FILE redirect ("clobbering" >).
4027 * Since we do not support "set -o noclobber" yet, 4078 * Since we do not support "set -o noclobber" yet,
@@ -4189,6 +4240,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_
4189 4240
4190 redir->rd_type = REDIRECT_HEREDOC2; 4241 redir->rd_type = REDIRECT_HEREDOC2;
4191 /* redir->rd_dup is (ab)used to indicate <<- */ 4242 /* redir->rd_dup is (ab)used to indicate <<- */
4243bb_error_msg("redir->rd_filename:'%s'", redir->rd_filename);
4192 p = fetch_till_str(&ctx->as_string, input, 4244 p = fetch_till_str(&ctx->as_string, input,
4193 redir->rd_filename, redir->rd_dup); 4245 redir->rd_filename, redir->rd_dup);
4194 if (!p) { 4246 if (!p) {
@@ -4340,39 +4392,6 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
4340 /* command remains "open", available for possible redirects */ 4392 /* command remains "open", available for possible redirects */
4341} 4393}
4342 4394
4343static int i_getch_and_eat_bkslash_nl(struct in_str *input)
4344{
4345 for (;;) {
4346 int ch, ch2;
4347
4348 ch = i_getch(input);
4349 if (ch != '\\')
4350 return ch;
4351 ch2 = i_peek(input);
4352 if (ch2 != '\n')
4353 return ch;
4354 /* backslash+newline, skip it */
4355 i_getch(input);
4356 }
4357}
4358
4359static int i_peek_and_eat_bkslash_nl(struct in_str *input)
4360{
4361 for (;;) {
4362 int ch, ch2;
4363
4364 ch = i_peek(input);
4365 if (ch != '\\')
4366 return ch;
4367 ch2 = i_peek2(input);
4368 if (ch2 != '\n')
4369 return ch;
4370 /* backslash+newline, skip it */
4371 i_getch(input);
4372 i_getch(input);
4373 }
4374}
4375
4376#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS 4395#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
4377/* Subroutines for copying $(...) and `...` things */ 4396/* Subroutines for copying $(...) and `...` things */
4378static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); 4397static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
@@ -4974,8 +4993,14 @@ static struct pipe *parse_stream(char **pstring,
4974 nommu_addchr(&ctx.as_string, ch); 4993 nommu_addchr(&ctx.as_string, ch);
4975 4994
4976 next = '\0'; 4995 next = '\0';
4977 if (ch != '\n') 4996 if (ch != '\n') {
4978 next = i_peek(input); 4997 next = i_peek(input);
4998 /* Can't use i_peek_and_eat_bkslash_nl(input) here:
4999 * echo '\
5000 * '
5001 * will break.
5002 */
5003 }
4979 5004
4980 is_special = "{}<>;&|()#'" /* special outside of "str" */ 5005 is_special = "{}<>;&|()#'" /* special outside of "str" */
4981 "\\$\"" IF_HUSH_TICK("`") /* always special */ 5006 "\\$\"" IF_HUSH_TICK("`") /* always special */
@@ -5178,6 +5203,8 @@ static struct pipe *parse_stream(char **pstring,
5178 goto parse_error; 5203 goto parse_error;
5179 } 5204 }
5180 redir_style = REDIRECT_OVERWRITE; 5205 redir_style = REDIRECT_OVERWRITE;
5206 if (next == '\\')
5207 next = i_peek_and_eat_bkslash_nl(input);
5181 if (next == '>') { 5208 if (next == '>') {
5182 redir_style = REDIRECT_APPEND; 5209 redir_style = REDIRECT_APPEND;
5183 ch = i_getch(input); 5210 ch = i_getch(input);
@@ -5198,6 +5225,8 @@ static struct pipe *parse_stream(char **pstring,
5198 goto parse_error; 5225 goto parse_error;
5199 } 5226 }
5200 redir_style = REDIRECT_INPUT; 5227 redir_style = REDIRECT_INPUT;
5228 if (next == '\\')
5229 next = i_peek_and_eat_bkslash_nl(input);
5201 if (next == '<') { 5230 if (next == '<') {
5202 redir_style = REDIRECT_HEREDOC; 5231 redir_style = REDIRECT_HEREDOC;
5203 heredoc_cnt++; 5232 heredoc_cnt++;
@@ -5365,7 +5394,7 @@ static struct pipe *parse_stream(char **pstring,
5365 /* Eat multiple semicolons, detect 5394 /* Eat multiple semicolons, detect
5366 * whether it means something special */ 5395 * whether it means something special */
5367 while (1) { 5396 while (1) {
5368 ch = i_peek(input); 5397 ch = i_peek_and_eat_bkslash_nl(input);
5369 if (ch != ';') 5398 if (ch != ';')
5370 break; 5399 break;
5371 ch = i_getch(input); 5400 ch = i_getch(input);
@@ -5387,6 +5416,8 @@ static struct pipe *parse_stream(char **pstring,
5387 if (done_word(&dest, &ctx)) { 5416 if (done_word(&dest, &ctx)) {
5388 goto parse_error; 5417 goto parse_error;
5389 } 5418 }
5419 if (next == '\\')
5420 next = i_peek_and_eat_bkslash_nl(input);
5390 if (next == '&') { 5421 if (next == '&') {
5391 ch = i_getch(input); 5422 ch = i_getch(input);
5392 nommu_addchr(&ctx.as_string, ch); 5423 nommu_addchr(&ctx.as_string, ch);
@@ -5403,6 +5434,8 @@ static struct pipe *parse_stream(char **pstring,
5403 if (ctx.ctx_res_w == RES_MATCH) 5434 if (ctx.ctx_res_w == RES_MATCH)
5404 break; /* we are in case's "word | word)" */ 5435 break; /* we are in case's "word | word)" */
5405#endif 5436#endif
5437 if (next == '\\')
5438 next = i_peek_and_eat_bkslash_nl(input);
5406 if (next == '|') { /* || */ 5439 if (next == '|') { /* || */
5407 ch = i_getch(input); 5440 ch = i_getch(input);
5408 nommu_addchr(&ctx.as_string, ch); 5441 nommu_addchr(&ctx.as_string, ch);
@@ -6123,7 +6156,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
6123 } else 6156 } else
6124 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....' 6157 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
6125 * and in this case should treat it like '$*' - see 'else...' below */ 6158 * and in this case should treat it like '$*' - see 'else...' below */
6126 if (first_ch == ('@'|0x80) /* quoted $@ */ 6159 if (first_ch == (char)('@'|0x80) /* quoted $@ */
6127 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */ 6160 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
6128 ) { 6161 ) {
6129 while (1) { 6162 while (1) {
@@ -6890,7 +6923,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6890 if (sq) for (; sq[i].orig_fd >= 0; i++) { 6923 if (sq) for (; sq[i].orig_fd >= 0; i++) {
6891 /* If we collide with an already moved fd... */ 6924 /* If we collide with an already moved fd... */
6892 if (fd == sq[i].moved_to) { 6925 if (fd == sq[i].moved_to) {
6893 sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); 6926 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
6894 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); 6927 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
6895 if (sq[i].moved_to < 0) /* what? */ 6928 if (sq[i].moved_to < 0) /* what? */
6896 xfunc_die(); 6929 xfunc_die();
@@ -6904,7 +6937,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
6904 } 6937 }
6905 6938
6906 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */ 6939 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
6907 moved_to = fcntl_F_DUPFD(fd, avoid_fd); 6940 moved_to = dup_CLOEXEC(fd, avoid_fd);
6908 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); 6941 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
6909 if (moved_to < 0 && errno != EBADF) 6942 if (moved_to < 0 && errno != EBADF)
6910 xfunc_die(); 6943 xfunc_die();
@@ -7622,6 +7655,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7622 */ 7655 */
7623 close_saved_fds_and_FILE_fds(); 7656 close_saved_fds_and_FILE_fds();
7624//FIXME: should also close saved redir fds 7657//FIXME: should also close saved redir fds
7658//This casuses test failures in
7659//redir_children_should_not_see_saved_fd_2.tests
7660//redir_children_should_not_see_saved_fd_3.tests
7661//if you replace "busybox find" with just "find" in them
7625 /* Without this, "rm -i FILE" can't be ^C'ed: */ 7662 /* Without this, "rm -i FILE" can't be ^C'ed: */
7626 switch_off_special_sigs(G.special_sig_mask); 7663 switch_off_special_sigs(G.special_sig_mask);
7627 debug_printf_exec("running applet '%s'\n", argv[0]); 7664 debug_printf_exec("running applet '%s'\n", argv[0]);
@@ -8166,6 +8203,12 @@ static NOINLINE int run_pipe(struct pipe *pi)
8166 rcode = 1; /* exitcode if redir failed */ 8203 rcode = 1; /* exitcode if redir failed */
8167 if (setup_redirects(command, &squirrel) == 0) { 8204 if (setup_redirects(command, &squirrel) == 0) {
8168 debug_printf_exec(": run_list\n"); 8205 debug_printf_exec(": run_list\n");
8206//FIXME: we need to pass squirrel down into run_list()
8207//for SH_STANDALONE case, or else this construct:
8208// { find /proc/self/fd; true; } >FILE; cmd2
8209//has no way of closing saved fd#1 for "find",
8210//and in SH_STANDALONE mode, "find" is not execed,
8211//therefore CLOEXEC on saved fd does not help.
8169 rcode = run_list(command->group) & 0xff; 8212 rcode = run_list(command->group) & 0xff;
8170 } 8213 }
8171 restore_redirects(squirrel); 8214 restore_redirects(squirrel);
@@ -9347,7 +9390,7 @@ int hush_main(int argc, char **argv)
9347 G_saved_tty_pgrp = 0; 9390 G_saved_tty_pgrp = 0;
9348 9391
9349 /* try to dup stdin to high fd#, >= 255 */ 9392 /* try to dup stdin to high fd#, >= 255 */
9350 G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); 9393 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
9351 if (G_interactive_fd < 0) { 9394 if (G_interactive_fd < 0) {
9352 /* try to dup to any fd */ 9395 /* try to dup to any fd */
9353 G_interactive_fd = dup(STDIN_FILENO); 9396 G_interactive_fd = dup(STDIN_FILENO);
@@ -9420,10 +9463,10 @@ int hush_main(int argc, char **argv)
9420#elif ENABLE_HUSH_INTERACTIVE 9463#elif ENABLE_HUSH_INTERACTIVE
9421 /* No job control compiled in, only prompt/line editing */ 9464 /* No job control compiled in, only prompt/line editing */
9422 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { 9465 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
9423 G_interactive_fd = fcntl_F_DUPFD(STDIN_FILENO, 254); 9466 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
9424 if (G_interactive_fd < 0) { 9467 if (G_interactive_fd < 0) {
9425 /* try to dup to any fd */ 9468 /* try to dup to any fd */
9426 G_interactive_fd = dup(STDIN_FILENO); 9469 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO);
9427 if (G_interactive_fd < 0) 9470 if (G_interactive_fd < 0)
9428 /* give up */ 9471 /* give up */
9429 G_interactive_fd = 0; 9472 G_interactive_fd = 0;
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.right b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.right
new file mode 100644
index 000000000..fdb7ebd03
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.right
@@ -0,0 +1,8 @@
1heredoc0
2Ok0:0
3heredoc1
4Ok1:0
5heredoc2
6Ok2:0
7heredoc3
8Ok4:0
diff --git a/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.tests b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.tests
new file mode 100755
index 000000000..584edd0e4
--- /dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc_bkslash_newline1.tests
@@ -0,0 +1,25 @@
1cat <\
2<\
3EOF
4heredoc0
5EOF
6echo Ok0:$?
7
8cat <<\
9 EOF
10heredoc1
11EOF
12echo Ok1:$?
13
14cat <<\
15- EOF
16heredoc2
17 EOF
18echo Ok2:$?
19
20cat <\
21<\
22- EOF
23heredoc3
24 EOF
25echo Ok4:$?
diff --git a/shell/hush_test/hush-parsing/bkslash_newline1.right b/shell/hush_test/hush-parsing/bkslash_newline1.right
new file mode 100644
index 000000000..97ea0c197
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline1.right
@@ -0,0 +1,4 @@
1and1
2and2
3or1
4ok
diff --git a/shell/hush_test/hush-parsing/bkslash_newline1.tests b/shell/hush_test/hush-parsing/bkslash_newline1.tests
new file mode 100755
index 000000000..6e374c4fb
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline1.tests
@@ -0,0 +1,8 @@
1echo and1 &\
2& echo and2
3
4echo or1 |\
5| echo NOT SHOWN
6
7case w in a) echo SKIP;\
8; w) echo ok;; esac;
diff --git a/shell/hush_test/hush-parsing/bkslash_newline2.right b/shell/hush_test/hush-parsing/bkslash_newline2.right
new file mode 100644
index 000000000..c863c5453
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline2.right
@@ -0,0 +1,4 @@
1Line with one backslash:
2\
3
4Ok:0
diff --git a/shell/hush_test/hush-parsing/bkslash_newline2.tests b/shell/hush_test/hush-parsing/bkslash_newline2.tests
new file mode 100755
index 000000000..47d63042d
--- /dev/null
+++ b/shell/hush_test/hush-parsing/bkslash_newline2.tests
@@ -0,0 +1,4 @@
1echo Line with one backslash:
2echo '\
3'
4echo Ok:$?
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.right b/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.right
new file mode 100644
index 000000000..2aead7129
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.right
@@ -0,0 +1 @@
\/a\/bc\/def\/file
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.tests b/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.tests
new file mode 100755
index 000000000..64ca0c170
--- /dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash2.tests
@@ -0,0 +1,2 @@
1var="/a/bc/def/file"
2echo "${var//\//\\/}"
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.right b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.tests b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.tests
new file mode 100755
index 000000000..544c810e3
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_1.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4busybox find /proc/self/fd >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.right b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.tests b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.tests
new file mode 100755
index 000000000..43777cade
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_2.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4{ busybox find /proc/self/fd; } >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.right b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.right
new file mode 100644
index 000000000..46ab7f5d1
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.right
@@ -0,0 +1,5 @@
1/proc/self/fd
2/proc/self/fd/0
3/proc/self/fd/1
4/proc/self/fd/2
5/proc/self/fd/3
diff --git a/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.tests b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.tests
new file mode 100755
index 000000000..0a21173bd
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_children_should_not_see_saved_fd_3.tests
@@ -0,0 +1,6 @@
1# The "find" should not see "saved" (duplicated) fd #1
2# Explicitly use bbox find, since other implementations of "find"
3# may open other descriptors as well.
4{ busybox find /proc/self/fd; true; } >tmp_$$.out
5cat tmp_$$.out
6rm -f tmp_$$.out
diff --git a/shell/hush_test/hush-vars/var_LINENO1.tests b/shell/hush_test/hush-vars/var_LINENO1.tests
index 851b52cf5..775861e64 100755
--- a/shell/hush_test/hush-vars/var_LINENO1.tests
+++ b/shell/hush_test/hush-vars/var_LINENO1.tests
@@ -1,4 +1,4 @@
1env | grep LINENO 1env | grep ^LINENO
2echo 2:$LINENO 2echo 2:$LINENO
3echo 3:$LINENO >&2 \ 3echo 3:$LINENO >&2 \
4| { sleep 0.1; echo 4:$LINENO; } 4| { sleep 0.1; echo 4:$LINENO; }