diff options
| author | Ron Yorston <rmy@pobox.com> | 2023-02-21 10:51:15 +0000 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2023-02-21 10:51:15 +0000 |
| commit | 665f3ac4ad7e5f32b88860f413fdb9fafd0eb3e5 (patch) | |
| tree | cd57156c0337e23a523308282fcba525e5953e94 | |
| parent | 98e16f25885fa49e30003d39e9b7316163c14e83 (diff) | |
| download | busybox-w32-665f3ac4ad7e5f32b88860f413fdb9fafd0eb3e5.tar.gz busybox-w32-665f3ac4ad7e5f32b88860f413fdb9fafd0eb3e5.tar.bz2 busybox-w32-665f3ac4ad7e5f32b88860f413fdb9fafd0eb3e5.zip | |
ash: revised CRLF handling for scripts
As noted in commit 2d848eba5 (ash: fix CRLF handling), removing
CRs from CRLF pairs in preadbuffer() is complicated by the
possibility that a CRLF pair might be split across the boundary
between buffers.
Add a wrapper around calls to nonblock_immune_read() to allow for
this.
Adds 104-128 bytes.
(GitHub issue #280)
| -rw-r--r-- | shell/ash.c | 65 |
1 files changed, 64 insertions, 1 deletions
diff --git a/shell/ash.c b/shell/ash.c index 152ca198d..742067216 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -801,6 +801,11 @@ struct parsefile { | |||
| 801 | 801 | ||
| 802 | /* Number of outstanding calls to pungetc. */ | 802 | /* Number of outstanding calls to pungetc. */ |
| 803 | int unget; | 803 | int unget; |
| 804 | |||
| 805 | #if ENABLE_PLATFORM_MINGW32 | ||
| 806 | /* True if a trailing CR from a previous read was left unprocessed. */ | ||
| 807 | int cr; | ||
| 808 | #endif | ||
| 804 | }; | 809 | }; |
| 805 | 810 | ||
| 806 | static struct parsefile basepf; /* top level input file */ | 811 | static struct parsefile basepf; /* top level input file */ |
| @@ -11725,6 +11730,54 @@ static void popstring(void) | |||
| 11725 | INT_ON; | 11730 | INT_ON; |
| 11726 | } | 11731 | } |
| 11727 | 11732 | ||
| 11733 | #if ENABLE_ASH_IGNORE_CR | ||
| 11734 | /* | ||
| 11735 | * Wrapper around nonblock_immune_read() to remove CRs, but only from | ||
| 11736 | * CRLF pairs. The tricky part is handling a CR at the end of the buffer. | ||
| 11737 | */ | ||
| 11738 | static inline ssize_t | ||
| 11739 | nonblock_immune_wrapper(struct parsefile *pf, char *buffer, size_t count) | ||
| 11740 | { | ||
| 11741 | int nr, injected_cr; | ||
| 11742 | |||
| 11743 | // Inject unprocessed CR from previous read into the buffer. | ||
| 11744 | if (pf->cr) | ||
| 11745 | *buffer = '\r'; | ||
| 11746 | retry: | ||
| 11747 | nr = nonblock_immune_read(pf->pf_fd, buffer + pf->cr, count - pf->cr); | ||
| 11748 | if (nr < 0) | ||
| 11749 | return nr; | ||
| 11750 | |||
| 11751 | injected_cr = pf->cr; | ||
| 11752 | nr += pf->cr; | ||
| 11753 | pf->cr = 0; | ||
| 11754 | |||
| 11755 | if (nr > 0) { | ||
| 11756 | nr = remove_cr(buffer, nr); | ||
| 11757 | // remove_cr() won't reduce size to zero, so [nr - 1] is OK. | ||
| 11758 | if (buffer[nr - 1] == '\r') { | ||
| 11759 | if (nr > 1) { | ||
| 11760 | // Ignore trailing CR for now: we'll deal with it later. | ||
| 11761 | pf->cr = 1; | ||
| 11762 | --nr; | ||
| 11763 | } else if (injected_cr) { // nr == 1 | ||
| 11764 | // Buffer only contains an injected CR. This means the | ||
| 11765 | // read returned EOF. Return the buffer as-is. The | ||
| 11766 | // next call will detect EOF. | ||
| 11767 | } else { | ||
| 11768 | // Buffer only contains a CR from the most recent read. | ||
| 11769 | // Try another read, treating the CR as injected. We'll | ||
| 11770 | // either get more characters or EOF. Either way we | ||
| 11771 | // won't end up here again. | ||
| 11772 | pf->cr = 1; | ||
| 11773 | goto retry; | ||
| 11774 | } | ||
| 11775 | } | ||
| 11776 | } | ||
| 11777 | return nr; | ||
| 11778 | } | ||
| 11779 | #endif | ||
| 11780 | |||
| 11728 | static int | 11781 | static int |
| 11729 | preadfd(void) | 11782 | preadfd(void) |
| 11730 | { | 11783 | { |
| @@ -11735,7 +11788,11 @@ preadfd(void) | |||
| 11735 | #if ENABLE_FEATURE_EDITING | 11788 | #if ENABLE_FEATURE_EDITING |
| 11736 | /* retry: */ | 11789 | /* retry: */ |
| 11737 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) | 11790 | if (!iflag || g_parsefile->pf_fd != STDIN_FILENO) |
| 11791 | #if ENABLE_ASH_IGNORE_CR | ||
| 11792 | nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1); | ||
| 11793 | #else | ||
| 11738 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 11794 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
| 11795 | #endif | ||
| 11739 | else { | 11796 | else { |
| 11740 | # if ENABLE_ASH_IDLE_TIMEOUT | 11797 | # if ENABLE_ASH_IDLE_TIMEOUT |
| 11741 | int timeout = -1; | 11798 | int timeout = -1; |
| @@ -11812,7 +11869,11 @@ preadfd(void) | |||
| 11812 | } | 11869 | } |
| 11813 | } | 11870 | } |
| 11814 | #else | 11871 | #else |
| 11872 | # if ENABLE_ASH_IGNORE_CR | ||
| 11873 | nr = nonblock_immune_wrapper(g_parsefile, buf, IBUFSIZ - 1); | ||
| 11874 | # else | ||
| 11815 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); | 11875 | nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1); |
| 11876 | # endif | ||
| 11816 | #endif | 11877 | #endif |
| 11817 | 11878 | ||
| 11818 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ | 11879 | #if 0 /* disabled: nonblock_immune_read() handles this problem */ |
| @@ -11883,7 +11944,9 @@ preadbuffer(void) | |||
| 11883 | more--; | 11944 | more--; |
| 11884 | 11945 | ||
| 11885 | c = *q; | 11946 | c = *q; |
| 11886 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) { | 11947 | /* Remove CR from input buffer as an alternative to ASH_IGNORE_CR. */ |
| 11948 | if (c == '\0' || (c == '\r' && | ||
| 11949 | ENABLE_PLATFORM_MINGW32 && !ENABLE_ASH_IGNORE_CR)) { | ||
| 11887 | memmove(q, q + 1, more); | 11950 | memmove(q, q + 1, more); |
| 11888 | } else { | 11951 | } else { |
| 11889 | q++; | 11952 | q++; |
