aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/Config.src7
-rw-r--r--shell/ash.c39
-rw-r--r--shell/ash_test/ash-read/read_ifs2.right9
-rwxr-xr-xshell/ash_test/ash-read/read_ifs2.tests9
-rw-r--r--shell/ash_test/ash-read/read_t.right8
-rwxr-xr-xshell/ash_test/ash-read/read_t.tests18
-rw-r--r--shell/ash_test/printenv.c4
-rw-r--r--shell/ash_test/recho.c2
-rw-r--r--shell/hush.c48
-rw-r--r--shell/hush_test/hush-read/read_t.right8
-rwxr-xr-xshell/hush_test/hush-read/read_t.tests18
-rw-r--r--shell/shell_common.c48
12 files changed, 157 insertions, 61 deletions
diff --git a/shell/Config.src b/shell/Config.src
index 5efbf9995..5b3fe08f3 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE
166 default y 166 default y
167 depends on SHELL_ASH || SHELL_HUSH 167 depends on SHELL_ASH || SHELL_HUSH
168 help 168 help
169 This option makes busybox shells to use $HISTFILESIZE variable 169 This option makes busybox shells to use $HISTSIZE and
170 to set shell history size. Note that its max value is capped 170 $HISTFILESIZE variables to set shell history size.
171 by "History size" setting in library tuning section. 171 Note that its max value is capped by "History size" setting
172 in library tuning section.
172 173
173config FEATURE_SH_EMBEDDED_SCRIPTS 174config FEATURE_SH_EMBEDDED_SCRIPTS
174 bool "Embed scripts in the binary" 175 bool "Embed scripts in the binary"
diff --git a/shell/ash.c b/shell/ash.c
index 3919118f0..0038aa1e9 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -929,6 +929,7 @@ raise_interrupt(void)
929 raise(SIGINT); 929 raise(SIGINT);
930#else 930#else
931 fflush_all(); 931 fflush_all();
932 kill(-getpid(), SIGINT);
932 _exit(SIGINT << 24); 933 _exit(SIGINT << 24);
933#endif 934#endif
934 } 935 }
@@ -4274,7 +4275,7 @@ signal_handler(int signo)
4274 return; 4275 return;
4275 } 4276 }
4276#if ENABLE_FEATURE_EDITING 4277#if ENABLE_FEATURE_EDITING
4277 bb_got_signal = signo; /* for read_line_input: "we got a signal" */ 4278 bb_got_signal = signo; /* for read_line_input / read builtin: "we got a signal" */
4278#endif 4279#endif
4279 gotsig[signo - 1] = 1; 4280 gotsig[signo - 1] = 1;
4280 pending_sig = signo; 4281 pending_sig = signo;
@@ -15812,6 +15813,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15812 r = shell_builtin_read(&params); 15813 r = shell_builtin_read(&params);
15813 INT_ON; 15814 INT_ON;
15814 15815
15816#if !ENABLE_PLATFORM_MINGW32
15815 if ((uintptr_t)r == 1 && errno == EINTR) { 15817 if ((uintptr_t)r == 1 && errno == EINTR) {
15816 /* To get SIGCHLD: sleep 1 & read x; echo $x 15818 /* To get SIGCHLD: sleep 1 & read x; echo $x
15817 * Correct behavior is to not exit "read" 15819 * Correct behavior is to not exit "read"
@@ -15820,8 +15822,15 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
15820 goto again; 15822 goto again;
15821 } 15823 }
15822 15824
15823#if ENABLE_PLATFORM_MINGW32 15825 if ((uintptr_t)r == 2) /* -t SEC timeout? */
15826 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
15827 /* The actual value observed with bash 5.2.15: */
15828 return 128 + SIGALRM;
15829#else /* ENABLE_PLATFORM_MINGW32 */
15824 if ((uintptr_t)r == 2) { 15830 if ((uintptr_t)r == 2) {
15831 /* Timeout, return 128 + SIGALRM */
15832 return 142;
15833 } else if ((uintptr_t)r == 3) {
15825 /* ^C pressed, propagate event */ 15834 /* ^C pressed, propagate event */
15826 if (trap[SIGINT]) { 15835 if (trap[SIGINT]) {
15827 write(STDOUT_FILENO, "^C", 2); 15836 write(STDOUT_FILENO, "^C", 2);
@@ -15969,8 +15978,25 @@ exitshell(void)
15969 char *p; 15978 char *p;
15970 15979
15971#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 15980#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
15972 save_history(line_input_state); /* may be NULL */ 15981 if (line_input_state) {
15982 const char *hp;
15983# if ENABLE_FEATURE_SH_HISTFILESIZE
15984// in bash:
15985// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
15986// "When this variable is assigned a value, the history file is truncated, if necessary"
15987// but we do it only at exit, not on assignment:
15988 /* Use HISTFILESIZE to limit file size */
15989 hp = lookupvar("HISTFILESIZE");
15990 if (hp)
15991 line_input_state->max_history = size_from_HISTFILESIZE(hp);
15992# endif
15993 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
15994 hp = lookupvar("HISTFILE");
15995 line_input_state->hist_file = hp;
15996 save_history(line_input_state); /* no-op if hist_file is NULL or "" */
15997 }
15973#endif 15998#endif
15999
15974 savestatus = exitstatus; 16000 savestatus = exitstatus;
15975 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); 16001 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
15976 if (setjmp(loc.loc)) 16002 if (setjmp(loc.loc))
@@ -16474,7 +16500,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
16474 if (hp) 16500 if (hp)
16475 line_input_state->hist_file = xstrdup(hp); 16501 line_input_state->hist_file = xstrdup(hp);
16476# if ENABLE_FEATURE_SH_HISTFILESIZE 16502# if ENABLE_FEATURE_SH_HISTFILESIZE
16477 hp = lookupvar("HISTFILESIZE"); 16503 hp = lookupvar("HISTSIZE");
16504 /* Using HISTFILESIZE above to limit max_history would be WRONG:
16505 * users may set HISTFILESIZE=0 in their profile scripts
16506 * to prevent _saving_ of history files, but still want to have
16507 * non-zero history limit for in-memory list.
16508 */
16478 line_input_state->max_history = size_from_HISTFILESIZE(hp); 16509 line_input_state->max_history = size_from_HISTFILESIZE(hp);
16479# endif 16510# endif
16480 } 16511 }
diff --git a/shell/ash_test/ash-read/read_ifs2.right b/shell/ash_test/ash-read/read_ifs2.right
new file mode 100644
index 000000000..797137dae
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.right
@@ -0,0 +1,9 @@
1|X|Y:Z:|
2|X|Y:Z|
3|X|Y|
4|X|Y|
5|X||
6|X||
7|||
8Whitespace should be trimmed too:
9|X|Y|
diff --git a/shell/ash_test/ash-read/read_ifs2.tests b/shell/ash_test/ash-read/read_ifs2.tests
new file mode 100755
index 000000000..f01a68978
--- /dev/null
+++ b/shell/ash_test/ash-read/read_ifs2.tests
@@ -0,0 +1,9 @@
1echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
2echo "X:Y:Z" | (IFS=": " read x y; echo "|$x|$y|")
3echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
4echo "X:Y" | (IFS=": " read x y; echo "|$x|$y|")
5echo "X:" | (IFS=": " read x y; echo "|$x|$y|")
6echo "X" | (IFS=": " read x y; echo "|$x|$y|")
7echo "" | (IFS=": " read x y; echo "|$x|$y|")
8echo Whitespace should be trimmed too:
9echo "X:Y : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/ash_test/ash-read/read_t.right
+++ b/shell/ash_test/ash-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/ash_test/ash-read/read_t.tests
+++ b/shell/ash_test/ash-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c86308d3b..f0f41984d 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -31,9 +31,7 @@
31extern char **environ; 31extern char **environ;
32 32
33int 33int
34main (argc, argv) 34main (int argc, char **argv)
35 int argc;
36 char **argv;
37{ 35{
38 register char **envp, *eval; 36 register char **envp, *eval;
39 int len; 37 int len;
diff --git a/shell/ash_test/recho.c b/shell/ash_test/recho.c
index 42a5feafd..7e96b14cc 100644
--- a/shell/ash_test/recho.c
+++ b/shell/ash_test/recho.c
@@ -27,7 +27,7 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <stdlib.h> 28#include <stdlib.h>
29 29
30void strprint(); 30void strprint(char *);
31 31
32int main(int argc, char **argv) 32int main(int argc, char **argv)
33{ 33{
diff --git a/shell/hush.c b/shell/hush.c
index 4a97293cc..0b2bc01f5 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1973,7 +1973,7 @@ static void record_pending_signo(int sig)
1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0]) 1973 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */ 1974 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
1975 ) { 1975 ) {
1976 bb_got_signal = sig; /* for read_line_input: "we got a signal" */ 1976 bb_got_signal = sig; /* for read_line_input / read builtin: "we got a signal" */
1977 } 1977 }
1978#endif 1978#endif
1979#if ENABLE_HUSH_FAST 1979#if ENABLE_HUSH_FAST
@@ -2099,11 +2099,29 @@ static sighandler_t pick_sighandler(unsigned sig)
2099 return handler; 2099 return handler;
2100} 2100}
2101 2101
2102static const char* FAST_FUNC get_local_var_value(const char *name);
2103
2102/* Restores tty foreground process group, and exits. */ 2104/* Restores tty foreground process group, and exits. */
2103static void hush_exit(int exitcode) 2105static void hush_exit(int exitcode)
2104{ 2106{
2105#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 2107#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2106 save_history(G.line_input_state); /* may be NULL */ 2108 if (G.line_input_state) {
2109 const char *hp;
2110# if ENABLE_FEATURE_SH_HISTFILESIZE
2111// in bash:
2112// HISTFILESIZE controls the on-disk history file size (in lines, 0=no history):
2113// "When this variable is assigned a value, the history file is truncated, if necessary"
2114// but we do it only at exit, not on every assignment:
2115 /* Use HISTFILESIZE to limit file size */
2116 hp = get_local_var_value("HISTFILESIZE");
2117 if (hp)
2118 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
2119# endif
2120 /* HISTFILE: "If unset, the command history is not saved when a shell exits." */
2121 hp = get_local_var_value("HISTFILE");
2122 G.line_input_state->hist_file = hp;
2123 save_history(G.line_input_state); /* no-op if hist_file is NULL or "" */
2124 }
2107#endif 2125#endif
2108 2126
2109 fflush_all(); 2127 fflush_all();
@@ -10427,7 +10445,7 @@ int hush_main(int argc, char **argv)
10427 if (!get_local_var_value("PATH")) 10445 if (!get_local_var_value("PATH"))
10428 set_local_var_from_halves("PATH", bb_default_root_path); 10446 set_local_var_from_halves("PATH", bb_default_root_path);
10429 10447
10430 /* PS1/PS2 are set later, if we determine that we are interactive */ 10448 /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */
10431 10449
10432 /* bash also exports SHLVL and _, 10450 /* bash also exports SHLVL and _,
10433 * and sets (but doesn't export) the following variables: 10451 * and sets (but doesn't export) the following variables:
@@ -10449,7 +10467,6 @@ int hush_main(int argc, char **argv)
10449 * BASH_SOURCE=() 10467 * BASH_SOURCE=()
10450 * DIRSTACK=() 10468 * DIRSTACK=()
10451 * PIPESTATUS=([0]="0") 10469 * PIPESTATUS=([0]="0")
10452 * HISTFILE=/<xxx>/.bash_history
10453 * HISTFILESIZE=500 10470 * HISTFILESIZE=500
10454 * HISTSIZE=500 10471 * HISTSIZE=500
10455 * MAILCHECK=60 10472 * MAILCHECK=60
@@ -10809,18 +10826,30 @@ int hush_main(int argc, char **argv)
10809 const char *hp = get_local_var_value("HISTFILE"); 10826 const char *hp = get_local_var_value("HISTFILE");
10810 if (!hp) { 10827 if (!hp) {
10811 hp = get_local_var_value("HOME"); 10828 hp = get_local_var_value("HOME");
10812 if (hp) 10829 if (hp) {
10813 hp = concat_path_file(hp, ".hush_history"); 10830 hp = concat_path_file(hp, ".hush_history");
10831 /* Make HISTFILE set on exit (else history won't be saved) */
10832 set_local_var_from_halves("HISTFILE", hp);
10833 }
10814 } else { 10834 } else {
10815 hp = xstrdup(hp); 10835 hp = xstrdup(hp);
10816 } 10836 }
10817 if (hp) { 10837 if (hp) {
10818 G.line_input_state->hist_file = hp; 10838 G.line_input_state->hist_file = hp;
10819 //set_local_var(xasprintf("HISTFILE=%s", ...));
10820 } 10839 }
10821# if ENABLE_FEATURE_SH_HISTFILESIZE 10840# if ENABLE_FEATURE_SH_HISTFILESIZE
10822 hp = get_local_var_value("HISTFILESIZE"); 10841 hp = get_local_var_value("HISTSIZE");
10842 /* Using HISTFILESIZE above to limit max_history would be WRONG:
10843 * users may set HISTFILESIZE=0 in their profile scripts
10844 * to prevent _saving_ of history files, but still want to have
10845 * non-zero history limit for in-memory list.
10846 */
10847// in bash, runtime history size is controlled by HISTSIZE (0=no history),
10848// HISTFILESIZE controls on-disk history file size (in lines, 0=no history):
10823 G.line_input_state->max_history = size_from_HISTFILESIZE(hp); 10849 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
10850// HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files."
10851// HISTSIZE: "The shell sets the default value to 500 after reading any startup files."
10852// (meaning: if the value wasn't set after startup files, the default value is set as described above)
10824# endif 10853# endif
10825 } 10854 }
10826# endif 10855# endif
@@ -11175,6 +11204,11 @@ static int FAST_FUNC builtin_read(char **argv)
11175 goto again; 11204 goto again;
11176 } 11205 }
11177 11206
11207 if ((uintptr_t)r == 2) /* -t SEC timeout? */
11208 /* bash: "The exit status is greater than 128 if the timeout is exceeded." */
11209 /* The actual value observed with bash 5.2.15: */
11210 return 128 + SIGALRM;
11211
11178 if ((uintptr_t)r > 1) { 11212 if ((uintptr_t)r > 1) {
11179 bb_simple_error_msg(r); 11213 bb_simple_error_msg(r);
11180 r = (char*)(uintptr_t)1; 11214 r = (char*)(uintptr_t)1;
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
index 04126cbe6..3eedae275 100644
--- a/shell/hush_test/hush-read/read_t.right
+++ b/shell/hush_test/hush-read/read_t.right
@@ -1,4 +1,4 @@
1>< 1>te:142<
2>< 2>:142<
3>test< 3>test:0<
4>test< 4>test:0<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
index d65f1aeaa..9fbeec517 100755
--- a/shell/hush_test/hush-read/read_t.tests
+++ b/shell/hush_test/hush-read/read_t.tests
@@ -1,10 +1,10 @@
1# bash 3.2 outputs: 1# bash 5.2 outputs:
2 2
3# >< 3# >te:142<
4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") 4{ echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply:$?<")
5# >< 5# >:142<
6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") 6{ sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply:$?<")
7# >test< 7# >test:0<
8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") 8{ echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply:$?<")
9# >test< 9# >test:0<
10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") 10{ sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply:$?<")
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 7fb5f8c58..657f0df8f 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -55,7 +55,7 @@ const char* FAST_FUNC
55shell_builtin_read(struct builtin_read_params *params) 55shell_builtin_read(struct builtin_read_params *params)
56{ 56{
57 struct pollfd pfd[1]; 57 struct pollfd pfd[1];
58#define fd (pfd[0].fd) /* -u FD */ 58#define fd (pfd->fd) /* -u FD */
59 unsigned err; 59 unsigned err;
60 unsigned end_ms; /* -t TIMEOUT */ 60 unsigned end_ms; /* -t TIMEOUT */
61 int nchars; /* -n NUM */ 61 int nchars; /* -n NUM */
@@ -144,7 +144,7 @@ shell_builtin_read(struct builtin_read_params *params)
144 * bash seems to ignore -p PROMPT for this use case. 144 * bash seems to ignore -p PROMPT for this use case.
145 */ 145 */
146 int r; 146 int r;
147 pfd[0].events = POLLIN; 147 pfd->events = POLLIN;
148 r = poll(pfd, 1, /*timeout:*/ 0); 148 r = poll(pfd, 1, /*timeout:*/ 0);
149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 149 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
150 return (const char *)(uintptr_t)(r <= 0); 150 return (const char *)(uintptr_t)(r <= 0);
@@ -209,8 +209,8 @@ shell_builtin_read(struct builtin_read_params *params)
209 * 32-bit unix time wrapped (year 2038+). 209 * 32-bit unix time wrapped (year 2038+).
210 */ 210 */
211 if (timeout <= 0) { /* already late? */ 211 if (timeout <= 0) { /* already late? */
212 retval = (const char *)(uintptr_t)1; 212 retval = (const char *)(uintptr_t)2;
213 goto ret; 213 break;
214 } 214 }
215 } 215 }
216 216
@@ -219,15 +219,23 @@ shell_builtin_read(struct builtin_read_params *params)
219 * regardless of SA_RESTART-ness of that signal! 219 * regardless of SA_RESTART-ness of that signal!
220 */ 220 */
221 errno = 0; 221 errno = 0;
222 pfd[0].events = POLLIN; 222 pfd->events = POLLIN;
223//TODO race with a signal arriving just before the poll! 223
224#if ENABLE_PLATFORM_MINGW32 224#if ENABLE_PLATFORM_MINGW32
225 /* Don't poll if timeout is -1, it hurts performance. */ 225 /* Don't poll if timeout is -1, it hurts performance. The
226 * caution above about interrupts isn't relevant on Windows
227 * where Ctrl-C causes an event, not a signal.
228 */
226 if (timeout >= 0) 229 if (timeout >= 0)
227#endif 230#endif
228 if (poll(pfd, 1, timeout) <= 0) { 231 /* test bb_got_signal, then poll(), atomically wrt signals */
229 /* timed out, or EINTR */ 232 if (check_got_signal_and_poll(pfd, timeout) <= 0) {
233 /* timed out, or some error */
230 err = errno; 234 err = errno;
235 if (!err) { /* timed out */
236 retval = (const char *)(uintptr_t)2;
237 break;
238 }
231 retval = (const char *)(uintptr_t)1; 239 retval = (const char *)(uintptr_t)1;
232 goto ret; 240 goto ret;
233 } 241 }
@@ -238,15 +246,18 @@ shell_builtin_read(struct builtin_read_params *params)
238 key = windows_read_key(fd, NULL, timeout); 246 key = windows_read_key(fd, NULL, timeout);
239 if (key == 0x03) { 247 if (key == 0x03) {
240 /* ^C pressed */ 248 /* ^C pressed */
241 retval = (const char *)(uintptr_t)2; 249 retval = (const char *)(uintptr_t)3;
242 goto ret; 250 goto ret;
243 } 251 }
244 else if (key == -1 || (key == 0x1a && bufpos == 0)) { 252 else if (key == -1) {
245 /* timeout or ^Z at start of buffer */ 253 /* timeout */
254 retval = (const char *)(uintptr_t)2;
255 break;
256 } else if (key == 0x1a && bufpos == 0) {
257 /* ^Z at start of buffer */
246 retval = (const char *)(uintptr_t)1; 258 retval = (const char *)(uintptr_t)1;
247 goto ret; 259 break;
248 } 260 } else if (key == '\b') {
249 else if (key == '\b') {
250 if (bufpos > 0) { 261 if (bufpos > 0) {
251 --bufpos; 262 --bufpos;
252 ++nchars; 263 ++nchars;
@@ -278,7 +289,7 @@ shell_builtin_read(struct builtin_read_params *params)
278 * and exit BS context. 289 * and exit BS context.
279 * - CR LF not in BS context: replace CR with LF */ 290 * - CR LF not in BS context: replace CR with LF */
280 buffer[--bufpos] = c; 291 buffer[--bufpos] = c;
281 ++nchars; 292 nchars += 1 + (backslash == 2);
282 } 293 }
283 } else if (backslash == 2) { 294 } else if (backslash == 2) {
284 /* We saw BS CR ??, keep escaped CR, exit BS context, 295 /* We saw BS CR ??, keep escaped CR, exit BS context,
@@ -298,6 +309,9 @@ shell_builtin_read(struct builtin_read_params *params)
298 backslash = 0; 309 backslash = 0;
299 if (c != '\n') 310 if (c != '\n')
300 goto put; 311 goto put;
312#if ENABLE_PLATFORM_MINGW32
313 ++nchars;
314#endif
301 continue; 315 continue;
302 } 316 }
303 if (c == '\\') { 317 if (c == '\\') {
@@ -338,7 +352,7 @@ shell_builtin_read(struct builtin_read_params *params)
338 } 352 }
339 put: 353 put:
340 bufpos++; 354 bufpos++;
341 } while (--nchars); 355 } while (IF_PLATFORM_MINGW32(backslash ||) --nchars);
342 356
343 if (argv[0]) { 357 if (argv[0]) {
344 /* Remove trailing space $IFS chars */ 358 /* Remove trailing space $IFS chars */