diff options
-rw-r--r-- | libbb/xfuncs_printf.c | 1 | ||||
-rw-r--r-- | shell/ash_test/ash-heredoc/heredocB.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-heredoc/heredocB.tests | 12 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir_script.tests | 4 | ||||
-rw-r--r-- | shell/hush.c | 234 | ||||
-rw-r--r-- | shell/hush_test/hush-heredoc/heredocB.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-heredoc/heredocB.tests | 12 | ||||
-rwxr-xr-x | shell/hush_test/hush-redir/redir_script.tests | 4 |
8 files changed, 175 insertions, 98 deletions
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c index 7247c915b..6cc60f6c0 100644 --- a/libbb/xfuncs_printf.c +++ b/libbb/xfuncs_printf.c | |||
@@ -222,6 +222,7 @@ void FAST_FUNC xdup2(int from, int to) | |||
222 | { | 222 | { |
223 | if (dup2(from, to) != to) | 223 | if (dup2(from, to) != to) |
224 | bb_perror_msg_and_die("can't duplicate file descriptor"); | 224 | bb_perror_msg_and_die("can't duplicate file descriptor"); |
225 | // " %d to %d", from, to); | ||
225 | } | 226 | } |
226 | 227 | ||
227 | // "Renumber" opened fd | 228 | // "Renumber" opened fd |
diff --git a/shell/ash_test/ash-heredoc/heredocB.right b/shell/ash_test/ash-heredoc/heredocB.right new file mode 100644 index 000000000..43ba0b4f9 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocB.right | |||
@@ -0,0 +1,3 @@ | |||
1 | one - alpha | ||
2 | two - beta | ||
3 | three - gamma | ||
diff --git a/shell/ash_test/ash-heredoc/heredocB.tests b/shell/ash_test/ash-heredoc/heredocB.tests new file mode 100755 index 000000000..45ea4687f --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredocB.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | while read line1; do | ||
2 | read line2 <&3 | ||
3 | echo $line1 - $line2 | ||
4 | done <<EOF1 3<<EOF2 | ||
5 | one | ||
6 | two | ||
7 | three | ||
8 | EOF1 | ||
9 | alpha | ||
10 | beta | ||
11 | gamma | ||
12 | EOF2 | ||
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests index 740daa461..a8d93ce4f 100755 --- a/shell/ash_test/ash-redir/redir_script.tests +++ b/shell/ash_test/ash-redir/redir_script.tests | |||
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \ | |||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | 27 | test x"$fds1" = x" 10>&- 3>&-" && \ |
28 | test x"$fds" = x" 11>&- 3>&-" \ | 28 | test x"$fds" = x" 11>&- 3>&-" \ |
29 | && { echo "Ok: script fd is not closed"; exit 0; } | 29 | && { echo "Ok: script fd is not closed"; exit 0; } |
30 | # or we see that fd 3 moved to fd 10: | ||
31 | test x"$fds1" = x" 3>&- 4>&-" && \ | ||
32 | test x"$fds" = x" 10>&- 3>&-" \ | ||
33 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
30 | 34 | ||
31 | echo "Bug: script fd is closed" | 35 | echo "Bug: script fd is closed" |
32 | echo "fds1:$fds1" | 36 | echo "fds1:$fds1" |
diff --git a/shell/hush.c b/shell/hush.c index c26484b49..7a1513c24 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -357,6 +357,9 @@ | |||
357 | #else | 357 | #else |
358 | # define CLEAR_RANDOM_T(rnd) ((void)0) | 358 | # define CLEAR_RANDOM_T(rnd) ((void)0) |
359 | #endif | 359 | #endif |
360 | #ifndef O_CLOEXEC | ||
361 | # define O_CLOEXEC 0 | ||
362 | #endif | ||
360 | #ifndef F_DUPFD_CLOEXEC | 363 | #ifndef F_DUPFD_CLOEXEC |
361 | # define F_DUPFD_CLOEXEC F_DUPFD | 364 | # define F_DUPFD_CLOEXEC F_DUPFD |
362 | #endif | 365 | #endif |
@@ -556,11 +559,27 @@ static const char *const assignment_flag[] = { | |||
556 | }; | 559 | }; |
557 | #endif | 560 | #endif |
558 | 561 | ||
562 | /* We almost can use standard FILE api, but we need an ability to move | ||
563 | * its fd when redirects coincide with it. No api exists for that | ||
564 | * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902). | ||
565 | * HFILE is our internal alternative. Only supports reading. | ||
566 | * Since we now can, we incorporate linked list of all opened HFILEs | ||
567 | * into the struct (used to be a separate mini-list). | ||
568 | */ | ||
569 | typedef struct HFILE { | ||
570 | char *cur; | ||
571 | char *end; | ||
572 | struct HFILE *next_hfile; | ||
573 | int is_stdin; | ||
574 | int fd; | ||
575 | char buf[1024]; | ||
576 | } HFILE; | ||
577 | |||
559 | typedef struct in_str { | 578 | typedef struct in_str { |
560 | const char *p; | 579 | const char *p; |
561 | int peek_buf[2]; | 580 | int peek_buf[2]; |
562 | int last_char; | 581 | int last_char; |
563 | FILE *file; | 582 | HFILE *file; |
564 | } in_str; | 583 | } in_str; |
565 | 584 | ||
566 | /* The descrip member of this structure is only used to make | 585 | /* The descrip member of this structure is only used to make |
@@ -814,14 +833,6 @@ enum { | |||
814 | NUM_OPT_O | 833 | NUM_OPT_O |
815 | }; | 834 | }; |
816 | 835 | ||
817 | |||
818 | struct FILE_list { | ||
819 | struct FILE_list *next; | ||
820 | FILE *fp; | ||
821 | int fd; | ||
822 | }; | ||
823 | |||
824 | |||
825 | /* "Globals" within this file */ | 836 | /* "Globals" within this file */ |
826 | /* Sorted roughly by size (smaller offsets == smaller code) */ | 837 | /* Sorted roughly by size (smaller offsets == smaller code) */ |
827 | struct globals { | 838 | struct globals { |
@@ -954,7 +965,7 @@ struct globals { | |||
954 | unsigned lineno; | 965 | unsigned lineno; |
955 | char *lineno_var; | 966 | char *lineno_var; |
956 | #endif | 967 | #endif |
957 | struct FILE_list *FILE_list; | 968 | HFILE *HFILE_list; |
958 | /* Which signals have non-DFL handler (even with no traps set)? | 969 | /* Which signals have non-DFL handler (even with no traps set)? |
959 | * Set at the start to: | 970 | * Set at the start to: |
960 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) | 971 | * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS) |
@@ -1563,83 +1574,115 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd) | |||
1563 | } | 1574 | } |
1564 | 1575 | ||
1565 | 1576 | ||
1566 | /* Manipulating the list of open FILEs */ | 1577 | /* Manipulating HFILEs */ |
1567 | static FILE *remember_FILE(FILE *fp) | 1578 | static HFILE *hfopen(const char *name) |
1568 | { | 1579 | { |
1569 | if (fp) { | 1580 | HFILE *fp; |
1570 | struct FILE_list *n = xmalloc(sizeof(*n)); | 1581 | int fd; |
1571 | n->next = G.FILE_list; | 1582 | |
1572 | G.FILE_list = n; | 1583 | fd = STDIN_FILENO; |
1573 | n->fp = fp; | 1584 | if (name) { |
1574 | n->fd = fileno(fp); | 1585 | fd = open(name, O_RDONLY | O_CLOEXEC); |
1575 | close_on_exec_on(n->fd); | 1586 | if (fd < 0) |
1587 | return NULL; | ||
1588 | if (O_CLOEXEC == 0) /* ancient libc */ | ||
1589 | close_on_exec_on(fd); | ||
1576 | } | 1590 | } |
1591 | |||
1592 | fp = xmalloc(sizeof(*fp)); | ||
1593 | fp->is_stdin = (name == NULL); | ||
1594 | fp->fd = fd; | ||
1595 | fp->cur = fp->end = fp->buf; | ||
1596 | fp->next_hfile = G.HFILE_list; | ||
1597 | G.HFILE_list = fp; | ||
1577 | return fp; | 1598 | return fp; |
1578 | } | 1599 | } |
1579 | static void fclose_and_forget(FILE *fp) | 1600 | static void hfclose(HFILE *fp) |
1580 | { | 1601 | { |
1581 | struct FILE_list **pp = &G.FILE_list; | 1602 | HFILE **pp = &G.HFILE_list; |
1582 | while (*pp) { | 1603 | while (*pp) { |
1583 | struct FILE_list *cur = *pp; | 1604 | HFILE *cur = *pp; |
1584 | if (cur->fp == fp) { | 1605 | if (cur == fp) { |
1585 | *pp = cur->next; | 1606 | *pp = cur->next_hfile; |
1586 | free(cur); | ||
1587 | break; | 1607 | break; |
1588 | } | 1608 | } |
1589 | pp = &cur->next; | 1609 | pp = &cur->next_hfile; |
1590 | } | 1610 | } |
1591 | fclose(fp); | 1611 | if (fp->fd >= 0) |
1612 | close(fp->fd); | ||
1613 | free(fp); | ||
1592 | } | 1614 | } |
1593 | static int save_FILEs_on_redirect(int fd, int avoid_fd) | 1615 | static int refill_HFILE_and_getc(HFILE *fp) |
1594 | { | 1616 | { |
1595 | struct FILE_list *fl = G.FILE_list; | 1617 | int n; |
1618 | |||
1619 | if (fp->fd < 0) { | ||
1620 | /* Already saw EOF */ | ||
1621 | return EOF; | ||
1622 | } | ||
1623 | /* Try to buffer more input */ | ||
1624 | fp->cur = fp->buf; | ||
1625 | n = safe_read(fp->fd, fp->buf, sizeof(fp->buf)); | ||
1626 | if (n < 0) { | ||
1627 | bb_perror_msg("read error"); | ||
1628 | n = 0; | ||
1629 | } | ||
1630 | fp->end = fp->buf + n; | ||
1631 | if (n == 0) { | ||
1632 | /* EOF/error */ | ||
1633 | close(fp->fd); | ||
1634 | fp->fd = -1; | ||
1635 | return EOF; | ||
1636 | } | ||
1637 | return (unsigned char)(*fp->cur++); | ||
1638 | } | ||
1639 | /* Inlined for common case of non-empty buffer. | ||
1640 | */ | ||
1641 | static ALWAYS_INLINE int hfgetc(HFILE *fp) | ||
1642 | { | ||
1643 | if (fp->cur < fp->end) | ||
1644 | return (unsigned char)(*fp->cur++); | ||
1645 | /* Buffer empty */ | ||
1646 | return refill_HFILE_and_getc(fp); | ||
1647 | } | ||
1648 | static int move_HFILEs_on_redirect(int fd, int avoid_fd) | ||
1649 | { | ||
1650 | HFILE *fl = G.HFILE_list; | ||
1596 | while (fl) { | 1651 | while (fl) { |
1597 | if (fd == fl->fd) { | 1652 | if (fd == fl->fd) { |
1598 | /* We use it only on script files, they are all CLOEXEC */ | 1653 | /* We use it only on script files, they are all CLOEXEC */ |
1599 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); | 1654 | fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd); |
1600 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); | 1655 | debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd); |
1601 | return 1; | 1656 | return 1; /* "found and moved" */ |
1602 | } | ||
1603 | fl = fl->next; | ||
1604 | } | ||
1605 | return 0; | ||
1606 | } | ||
1607 | static void restore_redirected_FILEs(void) | ||
1608 | { | ||
1609 | struct FILE_list *fl = G.FILE_list; | ||
1610 | while (fl) { | ||
1611 | int should_be = fileno(fl->fp); | ||
1612 | if (fl->fd != should_be) { | ||
1613 | debug_printf_redir("restoring script fd from %d to %d\n", fl->fd, should_be); | ||
1614 | xmove_fd(fl->fd, should_be); | ||
1615 | fl->fd = should_be; | ||
1616 | } | 1657 | } |
1617 | fl = fl->next; | 1658 | fl = fl->next_hfile; |
1618 | } | 1659 | } |
1660 | return 0; /* "not in the list" */ | ||
1619 | } | 1661 | } |
1620 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 1662 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
1621 | static void close_all_FILE_list(void) | 1663 | static void close_all_HFILE_list(void) |
1622 | { | 1664 | { |
1623 | struct FILE_list *fl = G.FILE_list; | 1665 | HFILE *fl = G.HFILE_list; |
1624 | while (fl) { | 1666 | while (fl) { |
1625 | /* fclose would also free FILE object. | 1667 | /* hfclose would also free HFILE object. |
1626 | * It is disastrous if we share memory with a vforked parent. | 1668 | * It is disastrous if we share memory with a vforked parent. |
1627 | * I'm not sure we never come here after vfork. | 1669 | * I'm not sure we never come here after vfork. |
1628 | * Therefore just close fd, nothing more. | 1670 | * Therefore just close fd, nothing more. |
1629 | */ | 1671 | */ |
1630 | /*fclose(fl->fp); - unsafe */ | 1672 | /*hfclose(fl); - unsafe */ |
1631 | close(fl->fd); | 1673 | if (fl->fd >= 0) |
1632 | fl = fl->next; | 1674 | close(fl->fd); |
1675 | fl = fl->next_hfile; | ||
1633 | } | 1676 | } |
1634 | } | 1677 | } |
1635 | #endif | 1678 | #endif |
1636 | static int fd_in_FILEs(int fd) | 1679 | static int fd_in_HFILEs(int fd) |
1637 | { | 1680 | { |
1638 | struct FILE_list *fl = G.FILE_list; | 1681 | HFILE *fl = G.HFILE_list; |
1639 | while (fl) { | 1682 | while (fl) { |
1640 | if (fl->fd == fd) | 1683 | if (fl->fd == fd) |
1641 | return 1; | 1684 | return 1; |
1642 | fl = fl->next; | 1685 | fl = fl->next_hfile; |
1643 | } | 1686 | } |
1644 | return 0; | 1687 | return 0; |
1645 | } | 1688 | } |
@@ -2580,7 +2623,7 @@ static int get_user_input(struct in_str *i) | |||
2580 | } | 2623 | } |
2581 | fflush_all(); | 2624 | fflush_all(); |
2582 | //FIXME: here ^C or SIGINT will have effect only after <Enter> | 2625 | //FIXME: here ^C or SIGINT will have effect only after <Enter> |
2583 | r = fgetc(i->file); | 2626 | r = hfgetc(i->file); |
2584 | /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, | 2627 | /* In !ENABLE_FEATURE_EDITING we don't use read_line_input, |
2585 | * no ^C masking happens during fgetc, no special code for ^C: | 2628 | * no ^C masking happens during fgetc, no special code for ^C: |
2586 | * it generates SIGINT as usual. | 2629 | * it generates SIGINT as usual. |
@@ -2600,22 +2643,22 @@ static int fgetc_interactive(struct in_str *i) | |||
2600 | { | 2643 | { |
2601 | int ch; | 2644 | int ch; |
2602 | /* If it's interactive stdin, get new line. */ | 2645 | /* If it's interactive stdin, get new line. */ |
2603 | if (G_interactive_fd && i->file == stdin) { | 2646 | if (G_interactive_fd && i->file->is_stdin) { |
2604 | /* Returns first char (or EOF), the rest is in i->p[] */ | 2647 | /* Returns first char (or EOF), the rest is in i->p[] */ |
2605 | ch = get_user_input(i); | 2648 | ch = get_user_input(i); |
2606 | G.promptmode = 1; /* PS2 */ | 2649 | G.promptmode = 1; /* PS2 */ |
2607 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 2650 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
2608 | } else { | 2651 | } else { |
2609 | /* Not stdin: script file, sourced file, etc */ | 2652 | /* Not stdin: script file, sourced file, etc */ |
2610 | do ch = fgetc(i->file); while (ch == '\0'); | 2653 | do ch = hfgetc(i->file); while (ch == '\0'); |
2611 | } | 2654 | } |
2612 | return ch; | 2655 | return ch; |
2613 | } | 2656 | } |
2614 | #else | 2657 | #else |
2615 | static inline int fgetc_interactive(struct in_str *i) | 2658 | static ALWAYS_INLINE int fgetc_interactive(struct in_str *i) |
2616 | { | 2659 | { |
2617 | int ch; | 2660 | int ch; |
2618 | do ch = fgetc(i->file); while (ch == '\0'); | 2661 | do ch = hfgetc(i->file); while (ch == '\0'); |
2619 | return ch; | 2662 | return ch; |
2620 | } | 2663 | } |
2621 | #endif /* INTERACTIVE */ | 2664 | #endif /* INTERACTIVE */ |
@@ -2730,7 +2773,7 @@ static int i_peek2(struct in_str *i) | |||
2730 | ch = i->peek_buf[1]; | 2773 | ch = i->peek_buf[1]; |
2731 | if (ch == 0) { | 2774 | if (ch == 0) { |
2732 | /* We did not read it yet, get it now */ | 2775 | /* We did not read it yet, get it now */ |
2733 | do ch = fgetc(i->file); while (ch == '\0'); | 2776 | do ch = hfgetc(i->file); while (ch == '\0'); |
2734 | i->peek_buf[1] = ch; | 2777 | i->peek_buf[1] = ch; |
2735 | } | 2778 | } |
2736 | 2779 | ||
@@ -2774,10 +2817,10 @@ static int i_peek_and_eat_bkslash_nl(struct in_str *input) | |||
2774 | } | 2817 | } |
2775 | } | 2818 | } |
2776 | 2819 | ||
2777 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2820 | static void setup_file_in_str(struct in_str *i, HFILE *fp) |
2778 | { | 2821 | { |
2779 | memset(i, 0, sizeof(*i)); | 2822 | memset(i, 0, sizeof(*i)); |
2780 | i->file = f; | 2823 | i->file = fp; |
2781 | /* i->p = NULL; */ | 2824 | /* i->p = NULL; */ |
2782 | } | 2825 | } |
2783 | 2826 | ||
@@ -7106,19 +7149,19 @@ static void parse_and_run_string(const char *s) | |||
7106 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7149 | //IF_HUSH_LINENO_VAR(G.lineno = sv;) |
7107 | } | 7150 | } |
7108 | 7151 | ||
7109 | static void parse_and_run_file(FILE *f) | 7152 | static void parse_and_run_file(HFILE *fp) |
7110 | { | 7153 | { |
7111 | struct in_str input; | 7154 | struct in_str input; |
7112 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) | 7155 | IF_HUSH_LINENO_VAR(unsigned sv = G.lineno;) |
7113 | 7156 | ||
7114 | IF_HUSH_LINENO_VAR(G.lineno = 1;) | 7157 | IF_HUSH_LINENO_VAR(G.lineno = 1;) |
7115 | setup_file_in_str(&input, f); | 7158 | setup_file_in_str(&input, fp); |
7116 | parse_and_run_stream(&input, ';'); | 7159 | parse_and_run_stream(&input, ';'); |
7117 | IF_HUSH_LINENO_VAR(G.lineno = sv;) | 7160 | IF_HUSH_LINENO_VAR(G.lineno = sv;) |
7118 | } | 7161 | } |
7119 | 7162 | ||
7120 | #if ENABLE_HUSH_TICK | 7163 | #if ENABLE_HUSH_TICK |
7121 | static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | 7164 | static int generate_stream_from_string(const char *s, pid_t *pid_p) |
7122 | { | 7165 | { |
7123 | pid_t pid; | 7166 | pid_t pid; |
7124 | int channel[2]; | 7167 | int channel[2]; |
@@ -7220,7 +7263,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) | |||
7220 | free(to_free); | 7263 | free(to_free); |
7221 | # endif | 7264 | # endif |
7222 | close(channel[1]); | 7265 | close(channel[1]); |
7223 | return remember_FILE(xfdopen_for_read(channel[0])); | 7266 | return channel[0]; |
7224 | } | 7267 | } |
7225 | 7268 | ||
7226 | /* Return code is exit status of the process that is run. */ | 7269 | /* Return code is exit status of the process that is run. */ |
@@ -7230,7 +7273,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
7230 | pid_t pid; | 7273 | pid_t pid; |
7231 | int status, ch, eol_cnt; | 7274 | int status, ch, eol_cnt; |
7232 | 7275 | ||
7233 | fp = generate_stream_from_string(s, &pid); | 7276 | fp = xfdopen_for_read(generate_stream_from_string(s, &pid)); |
7234 | 7277 | ||
7235 | /* Now send results of command back into original context */ | 7278 | /* Now send results of command back into original context */ |
7236 | eol_cnt = 0; | 7279 | eol_cnt = 0; |
@@ -7249,7 +7292,7 @@ static int process_command_subs(o_string *dest, const char *s) | |||
7249 | } | 7292 | } |
7250 | 7293 | ||
7251 | debug_printf("done reading from `cmd` pipe, closing it\n"); | 7294 | debug_printf("done reading from `cmd` pipe, closing it\n"); |
7252 | fclose_and_forget(fp); | 7295 | fclose(fp); |
7253 | /* We need to extract exitcode. Test case | 7296 | /* We need to extract exitcode. Test case |
7254 | * "true; echo `sleep 1; false` $?" | 7297 | * "true; echo `sleep 1; false` $?" |
7255 | * should print 1 */ | 7298 | * should print 1 */ |
@@ -7427,20 +7470,17 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) | |||
7427 | return 1; /* "we closed fd" */ | 7470 | return 1; /* "we closed fd" */ |
7428 | } | 7471 | } |
7429 | #endif | 7472 | #endif |
7473 | /* If this one of script's fds? */ | ||
7474 | if (move_HFILEs_on_redirect(fd, avoid_fd)) | ||
7475 | return 1; /* yes. "we closed fd" (actually moved it) */ | ||
7476 | |||
7430 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: | 7477 | /* Are we called from setup_redirects(squirrel==NULL)? Two cases: |
7431 | * (1) Redirect in a forked child. No need to save FILEs' fds, | 7478 | * (1) Redirect in a forked child. |
7432 | * we aren't going to use them anymore, ok to trash. | 7479 | * (2) "exec 3>FILE". |
7433 | * (2) "exec 3>FILE". Bummer. We can save script FILEs' fds, | ||
7434 | * but how are we doing to restore them? | ||
7435 | * "fileno(fd) = new_fd" can't be done. | ||
7436 | */ | 7480 | */ |
7437 | if (!sqp) | 7481 | if (!sqp) |
7438 | return 0; | 7482 | return 0; |
7439 | 7483 | ||
7440 | /* If this one of script's fds? */ | ||
7441 | if (save_FILEs_on_redirect(fd, avoid_fd)) | ||
7442 | return 1; /* yes. "we closed fd" */ | ||
7443 | |||
7444 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ | 7484 | /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ |
7445 | *sqp = add_squirrel(*sqp, fd, avoid_fd); | 7485 | *sqp = add_squirrel(*sqp, fd, avoid_fd); |
7446 | return 0; /* "we did not close fd" */ | 7486 | return 0; /* "we did not close fd" */ |
@@ -7465,8 +7505,6 @@ static void restore_redirects(struct squirrel *sq) | |||
7465 | } | 7505 | } |
7466 | 7506 | ||
7467 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ | 7507 | /* If moved, G.interactive_fd stays on new fd, not restoring it */ |
7468 | |||
7469 | restore_redirected_FILEs(); | ||
7470 | } | 7508 | } |
7471 | 7509 | ||
7472 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU | 7510 | #if ENABLE_FEATURE_SH_STANDALONE && BB_MMU |
@@ -7474,7 +7512,7 @@ static void close_saved_fds_and_FILE_fds(void) | |||
7474 | { | 7512 | { |
7475 | if (G_interactive_fd) | 7513 | if (G_interactive_fd) |
7476 | close(G_interactive_fd); | 7514 | close(G_interactive_fd); |
7477 | close_all_FILE_list(); | 7515 | close_all_HFILE_list(); |
7478 | } | 7516 | } |
7479 | #endif | 7517 | #endif |
7480 | 7518 | ||
@@ -7487,7 +7525,7 @@ static int internally_opened_fd(int fd, struct squirrel *sq) | |||
7487 | return 1; | 7525 | return 1; |
7488 | #endif | 7526 | #endif |
7489 | /* If this one of script's fds? */ | 7527 | /* If this one of script's fds? */ |
7490 | if (fd_in_FILEs(fd)) | 7528 | if (fd_in_HFILEs(fd)) |
7491 | return 1; | 7529 | return 1; |
7492 | 7530 | ||
7493 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 7531 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { |
@@ -7512,7 +7550,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
7512 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); | 7550 | save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); |
7513 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | 7551 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ |
7514 | * of the heredoc */ | 7552 | * of the heredoc */ |
7515 | debug_printf_parse("set heredoc '%s'\n", | 7553 | debug_printf_redir("set heredoc '%s'\n", |
7516 | redir->rd_filename); | 7554 | redir->rd_filename); |
7517 | setup_heredoc(redir); | 7555 | setup_heredoc(redir); |
7518 | continue; | 7556 | continue; |
@@ -7524,8 +7562,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
7524 | int mode; | 7562 | int mode; |
7525 | 7563 | ||
7526 | if (redir->rd_filename == NULL) { | 7564 | if (redir->rd_filename == NULL) { |
7527 | /* | 7565 | /* Examples: |
7528 | * Examples: | ||
7529 | * "cmd >" (no filename) | 7566 | * "cmd >" (no filename) |
7530 | * "cmd > <file" (2nd redirect starts too early) | 7567 | * "cmd > <file" (2nd redirect starts too early) |
7531 | */ | 7568 | */ |
@@ -8706,7 +8743,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8706 | } | 8743 | } |
8707 | #endif | 8744 | #endif |
8708 | /* { list } */ | 8745 | /* { list } */ |
8709 | debug_printf("non-subshell group\n"); | 8746 | debug_printf_exec("non-subshell group\n"); |
8710 | rcode = 1; /* exitcode if redir failed */ | 8747 | rcode = 1; /* exitcode if redir failed */ |
8711 | if (setup_redirects(command, &squirrel) == 0) { | 8748 | if (setup_redirects(command, &squirrel) == 0) { |
8712 | debug_printf_exec(": run_list\n"); | 8749 | debug_printf_exec(": run_list\n"); |
@@ -9871,14 +9908,13 @@ int hush_main(int argc, char **argv) | |||
9871 | 9908 | ||
9872 | /* If we are login shell... */ | 9909 | /* If we are login shell... */ |
9873 | if (flags & OPT_login) { | 9910 | if (flags & OPT_login) { |
9874 | FILE *input; | 9911 | HFILE *input; |
9875 | debug_printf("sourcing /etc/profile\n"); | 9912 | debug_printf("sourcing /etc/profile\n"); |
9876 | input = fopen_for_read("/etc/profile"); | 9913 | input = hfopen("/etc/profile"); |
9877 | if (input != NULL) { | 9914 | if (input != NULL) { |
9878 | remember_FILE(input); | ||
9879 | install_special_sighandlers(); | 9915 | install_special_sighandlers(); |
9880 | parse_and_run_file(input); | 9916 | parse_and_run_file(input); |
9881 | fclose_and_forget(input); | 9917 | hfclose(input); |
9882 | } | 9918 | } |
9883 | /* bash: after sourcing /etc/profile, | 9919 | /* bash: after sourcing /etc/profile, |
9884 | * tries to source (in the given order): | 9920 | * tries to source (in the given order): |
@@ -9891,7 +9927,7 @@ int hush_main(int argc, char **argv) | |||
9891 | 9927 | ||
9892 | /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ | 9928 | /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */ |
9893 | if (!(flags & OPT_s) && G.global_argv[1]) { | 9929 | if (!(flags & OPT_s) && G.global_argv[1]) { |
9894 | FILE *input; | 9930 | HFILE *input; |
9895 | /* | 9931 | /* |
9896 | * "bash <script>" (which is never interactive (unless -i?)) | 9932 | * "bash <script>" (which is never interactive (unless -i?)) |
9897 | * sources $BASH_ENV here (without scanning $PATH). | 9933 | * sources $BASH_ENV here (without scanning $PATH). |
@@ -9902,13 +9938,15 @@ int hush_main(int argc, char **argv) | |||
9902 | G.global_argv++; | 9938 | G.global_argv++; |
9903 | debug_printf("running script '%s'\n", G.global_argv[0]); | 9939 | debug_printf("running script '%s'\n", G.global_argv[0]); |
9904 | xfunc_error_retval = 127; /* for "hush /does/not/exist" case */ | 9940 | xfunc_error_retval = 127; /* for "hush /does/not/exist" case */ |
9905 | input = xfopen_for_read(G.global_argv[0]); | 9941 | input = hfopen(G.global_argv[0]); |
9942 | if (!input) { | ||
9943 | bb_simple_perror_msg_and_die(G.global_argv[0]); | ||
9944 | } | ||
9906 | xfunc_error_retval = 1; | 9945 | xfunc_error_retval = 1; |
9907 | remember_FILE(input); | ||
9908 | install_special_sighandlers(); | 9946 | install_special_sighandlers(); |
9909 | parse_and_run_file(input); | 9947 | parse_and_run_file(input); |
9910 | #if ENABLE_FEATURE_CLEAN_UP | 9948 | #if ENABLE_FEATURE_CLEAN_UP |
9911 | fclose_and_forget(input); | 9949 | hfclose(input); |
9912 | #endif | 9950 | #endif |
9913 | goto final_return; | 9951 | goto final_return; |
9914 | } | 9952 | } |
@@ -10038,7 +10076,7 @@ int hush_main(int argc, char **argv) | |||
10038 | ); | 10076 | ); |
10039 | } | 10077 | } |
10040 | 10078 | ||
10041 | parse_and_run_file(stdin); | 10079 | parse_and_run_file(hfopen(NULL)); /* stdin */ |
10042 | 10080 | ||
10043 | final_return: | 10081 | final_return: |
10044 | hush_exit(G.last_exitcode); | 10082 | hush_exit(G.last_exitcode); |
@@ -10849,7 +10887,7 @@ Test that VAR is a valid variable name? | |||
10849 | static int FAST_FUNC builtin_source(char **argv) | 10887 | static int FAST_FUNC builtin_source(char **argv) |
10850 | { | 10888 | { |
10851 | char *arg_path, *filename; | 10889 | char *arg_path, *filename; |
10852 | FILE *input; | 10890 | HFILE *input; |
10853 | save_arg_t sv; | 10891 | save_arg_t sv; |
10854 | char *args_need_save; | 10892 | char *args_need_save; |
10855 | #if ENABLE_HUSH_FUNCTIONS | 10893 | #if ENABLE_HUSH_FUNCTIONS |
@@ -10873,10 +10911,10 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10873 | return EXIT_FAILURE; | 10911 | return EXIT_FAILURE; |
10874 | } | 10912 | } |
10875 | } | 10913 | } |
10876 | input = remember_FILE(fopen_or_warn(filename, "r")); | 10914 | input = hfopen(filename); |
10877 | free(arg_path); | 10915 | free(arg_path); |
10878 | if (!input) { | 10916 | if (!input) { |
10879 | /* bb_perror_msg("%s", *argv); - done by fopen_or_warn */ | 10917 | bb_perror_msg("%s", filename); |
10880 | /* POSIX: non-interactive shell should abort here, | 10918 | /* POSIX: non-interactive shell should abort here, |
10881 | * not merely fail. So far no one complained :) | 10919 | * not merely fail. So far no one complained :) |
10882 | */ | 10920 | */ |
@@ -10895,7 +10933,7 @@ static int FAST_FUNC builtin_source(char **argv) | |||
10895 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ | 10933 | /* "false; . ./empty_line; echo Zero:$?" should print 0 */ |
10896 | G.last_exitcode = 0; | 10934 | G.last_exitcode = 0; |
10897 | parse_and_run_file(input); | 10935 | parse_and_run_file(input); |
10898 | fclose_and_forget(input); | 10936 | hfclose(input); |
10899 | 10937 | ||
10900 | if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ | 10938 | if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */ |
10901 | restore_G_args(&sv, argv); | 10939 | restore_G_args(&sv, argv); |
diff --git a/shell/hush_test/hush-heredoc/heredocB.right b/shell/hush_test/hush-heredoc/heredocB.right new file mode 100644 index 000000000..43ba0b4f9 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocB.right | |||
@@ -0,0 +1,3 @@ | |||
1 | one - alpha | ||
2 | two - beta | ||
3 | three - gamma | ||
diff --git a/shell/hush_test/hush-heredoc/heredocB.tests b/shell/hush_test/hush-heredoc/heredocB.tests new file mode 100755 index 000000000..45ea4687f --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredocB.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | while read line1; do | ||
2 | read line2 <&3 | ||
3 | echo $line1 - $line2 | ||
4 | done <<EOF1 3<<EOF2 | ||
5 | one | ||
6 | two | ||
7 | three | ||
8 | EOF1 | ||
9 | alpha | ||
10 | beta | ||
11 | gamma | ||
12 | EOF2 | ||
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests index 740daa461..a8d93ce4f 100755 --- a/shell/hush_test/hush-redir/redir_script.tests +++ b/shell/hush_test/hush-redir/redir_script.tests | |||
@@ -27,6 +27,10 @@ test x"$fds1" = x"$fds" \ | |||
27 | test x"$fds1" = x" 10>&- 3>&-" && \ | 27 | test x"$fds1" = x" 10>&- 3>&-" && \ |
28 | test x"$fds" = x" 11>&- 3>&-" \ | 28 | test x"$fds" = x" 11>&- 3>&-" \ |
29 | && { echo "Ok: script fd is not closed"; exit 0; } | 29 | && { echo "Ok: script fd is not closed"; exit 0; } |
30 | # or we see that fd 3 moved to fd 10: | ||
31 | test x"$fds1" = x" 3>&- 4>&-" && \ | ||
32 | test x"$fds" = x" 10>&- 3>&-" \ | ||
33 | && { echo "Ok: script fd is not closed"; exit 0; } | ||
30 | 34 | ||
31 | echo "Bug: script fd is closed" | 35 | echo "Bug: script fd is closed" |
32 | echo "fds1:$fds1" | 36 | echo "fds1:$fds1" |