diff options
author | Ron Yorston <rmy@pobox.com> | 2018-04-02 09:24:14 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2018-04-02 09:24:14 +0100 |
commit | 34a68d327b42c3c700e84cd475496985782290b1 (patch) | |
tree | 99bfe59cca420d26f01e81a7f41763f71b44d22c /shell | |
parent | aff3c5bd7b6bdcfb97f63153ab839c5f55f16a12 (diff) | |
parent | e84212f8346741a2d4a04b40639c44fe519cf5a7 (diff) | |
download | busybox-w32-34a68d327b42c3c700e84cd475496985782290b1.tar.gz busybox-w32-34a68d327b42c3c700e84cd475496985782290b1.tar.bz2 busybox-w32-34a68d327b42c3c700e84cd475496985782290b1.zip |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
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 | } |
5817 | static int | 5825 | static int |
5818 | fcntl_F_DUPFD(int fd, int avoid_fd) | 5826 | dup_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 @@ | |||
1 | heredoc1 | ||
2 | Ok1:0 | ||
3 | heredoc2 | ||
4 | EO`false`F | ||
5 | Ok2: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 @@ | |||
1 | cat <<EO`true`F | ||
2 | heredoc1 | ||
3 | EO`true`F | ||
4 | echo Ok1:$? | ||
5 | |||
6 | cat <<EO`true`F | ||
7 | heredoc2 | ||
8 | EO`false`F | ||
9 | EO`true`F | ||
10 | echo 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 @@ | |||
1 | Quoted heredoc: | ||
2 | a\ | ||
3 | b | ||
4 | a\\ | ||
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-\*-\?-') | ||
10 | c\ | ||
11 | |||
12 | Unquoted heredoc: | ||
13 | a b | ||
14 | a\ | ||
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-\*-\?- | ||
20 | cEOF2 | ||
21 | |||
22 | Quoted -heredoc: | ||
23 | a\ | ||
24 | b | ||
25 | a\\ | ||
26 | b | ||
27 | 123456 -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- | ||
28 | -$a-\t-\\-\"-\'-\`-\--\z-\*-\?- | ||
29 | 123456 `echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-'` | ||
30 | 123456 $(echo v'-$a-\t-\\-\"-\'-\`-\--\z-\*-\?-') | ||
31 | c\ | ||
32 | |||
33 | Unquoted -heredoc: | ||
34 | a b | ||
35 | a\ | ||
36 | b | ||
37 | 123456 -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- | ||
38 | -qwerty-\t-\-\"-\'-`-\--\z-\*-\?- | ||
39 | 123456 v-$a-\t-\-\"-\x-`-\--\z-\*-\?- | ||
40 | 123456 v-$a-\t-\\-\"-\x-\`-\--\z-\*-\?- | ||
41 | cEOF4 | ||
42 | |||
43 | Done: 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 | |||
4 | a=qwerty | ||
5 | |||
6 | echo Quoted heredoc: | ||
7 | cat <<"EOF1" | ||
8 | a\ | ||
9 | b | ||
10 | a\\ | ||
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-\*-\?-') | ||
16 | c\ | ||
17 | EOF1 | ||
18 | echo | ||
19 | |||
20 | echo Unquoted heredoc: | ||
21 | cat <<EOF2 | ||
22 | a\ | ||
23 | b | ||
24 | a\\ | ||
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-\*-\?-') | ||
30 | c\ | ||
31 | EOF2 | ||
32 | EOF2 | ||
33 | echo | ||
34 | |||
35 | echo Quoted -heredoc: | ||
36 | cat <<-"EOF3" | ||
37 | a\ | ||
38 | b | ||
39 | a\\ | ||
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-\*-\?-') | ||
45 | c\ | ||
46 | EOF3 | ||
47 | # In -heredoc case the marker is detected even if it is indented. | ||
48 | echo | ||
49 | |||
50 | echo Unquoted -heredoc: | ||
51 | cat <<-EOF4 | ||
52 | a\ | ||
53 | b | ||
54 | a\\ | ||
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-\*-\?-') | ||
60 | c\ | ||
61 | EOF4 | ||
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.) | ||
68 | echo | ||
69 | |||
70 | echo "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 @@ | |||
1 | heredoc0 | ||
2 | Ok0:0 | ||
3 | heredoc1 | ||
4 | Ok1:0 | ||
5 | heredoc2 | ||
6 | Ok2:0 | ||
7 | heredoc3 | ||
8 | Ok4: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 @@ | |||
1 | cat <\ | ||
2 | <\ | ||
3 | EOF | ||
4 | heredoc0 | ||
5 | EOF | ||
6 | echo Ok0:$? | ||
7 | |||
8 | cat <<\ | ||
9 | EOF | ||
10 | heredoc1 | ||
11 | EOF | ||
12 | echo Ok1:$? | ||
13 | |||
14 | cat <<\ | ||
15 | - EOF | ||
16 | heredoc2 | ||
17 | EOF | ||
18 | echo Ok2:$? | ||
19 | |||
20 | cat <\ | ||
21 | <\ | ||
22 | - EOF | ||
23 | heredoc3 | ||
24 | EOF | ||
25 | echo 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 @@ | |||
1 | and1 | ||
2 | and2 | ||
3 | or1 | ||
4 | ok | ||
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 @@ | |||
1 | echo and1 &\ | ||
2 | & echo and2 | ||
3 | |||
4 | echo or1 |\ | ||
5 | | echo NOT SHOWN | ||
6 | |||
7 | case 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 @@ | |||
1 | Line with one backslash: | ||
2 | \ | ||
3 | |||
4 | Ok: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 @@ | |||
1 | echo Line with one backslash: | ||
2 | echo '\ | ||
3 | ' | ||
4 | echo 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 @@ | |||
1 | var="/a/bc/def/file" | ||
2 | echo "${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. | ||
4 | busybox find /proc/self/fd >tmp_$$.out | ||
5 | cat tmp_$$.out | ||
6 | rm -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 | ||
5 | cat tmp_$$.out | ||
6 | rm -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 | ||
5 | cat tmp_$$.out | ||
6 | rm -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 @@ | |||
1 | 2:2 | ||
2 | 3:3 | ||
3 | 4:4 | ||
4 | 5:5 | ||
5 | 2:2 | ||
6 | 3:3 | ||
7 | 4:4 | ||
8 | 5: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 @@ | |||
1 | env | grep ^LINENO | ||
2 | echo 2:$LINENO | ||
3 | echo 3:$LINENO >&2 \ | ||
4 | | { sleep 0.1; echo 4:$LINENO; } | ||
5 | echo 5:$LINENO | ||
6 | test "$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 | ||
1504 | static int fcntl_F_DUPFD(int fd, int avoid_fd) | 1504 | static 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 | ||
2697 | static 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 | */ | ||
2716 | static 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 | |||
2694 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2733 | static 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 <<- */ |
4243 | bb_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 | ||
4343 | static 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 | |||
4359 | static 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 */ |
4378 | static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote); | 4397 | static 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 @@ | |||
1 | heredoc0 | ||
2 | Ok0:0 | ||
3 | heredoc1 | ||
4 | Ok1:0 | ||
5 | heredoc2 | ||
6 | Ok2:0 | ||
7 | heredoc3 | ||
8 | Ok4: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 @@ | |||
1 | cat <\ | ||
2 | <\ | ||
3 | EOF | ||
4 | heredoc0 | ||
5 | EOF | ||
6 | echo Ok0:$? | ||
7 | |||
8 | cat <<\ | ||
9 | EOF | ||
10 | heredoc1 | ||
11 | EOF | ||
12 | echo Ok1:$? | ||
13 | |||
14 | cat <<\ | ||
15 | - EOF | ||
16 | heredoc2 | ||
17 | EOF | ||
18 | echo Ok2:$? | ||
19 | |||
20 | cat <\ | ||
21 | <\ | ||
22 | - EOF | ||
23 | heredoc3 | ||
24 | EOF | ||
25 | echo 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 @@ | |||
1 | and1 | ||
2 | and2 | ||
3 | or1 | ||
4 | ok | ||
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 @@ | |||
1 | echo and1 &\ | ||
2 | & echo and2 | ||
3 | |||
4 | echo or1 |\ | ||
5 | | echo NOT SHOWN | ||
6 | |||
7 | case 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 @@ | |||
1 | Line with one backslash: | ||
2 | \ | ||
3 | |||
4 | Ok: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 @@ | |||
1 | echo Line with one backslash: | ||
2 | echo '\ | ||
3 | ' | ||
4 | echo 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 @@ | |||
1 | var="/a/bc/def/file" | ||
2 | echo "${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. | ||
4 | busybox find /proc/self/fd >tmp_$$.out | ||
5 | cat tmp_$$.out | ||
6 | rm -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 | ||
5 | cat tmp_$$.out | ||
6 | rm -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 | ||
5 | cat tmp_$$.out | ||
6 | rm -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 @@ | |||
1 | env | grep LINENO | 1 | env | grep ^LINENO |
2 | echo 2:$LINENO | 2 | echo 2:$LINENO |
3 | echo 3:$LINENO >&2 \ | 3 | echo 3:$LINENO >&2 \ |
4 | | { sleep 0.1; echo 4:$LINENO; } | 4 | | { sleep 0.1; echo 4:$LINENO; } |