ash,hush: fix SIGCHLD interrupting read builtin

function                                             old     new   delta
readcmd                                              169     217     +48
shell_builtin_read                                  1087    1097     +10
localcmd                                             366     364      -2
builtin_read                                         197     193      -4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/2 up/down: 58/-6)              Total: 52 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/shell/ash.c b/shell/ash.c
index 70ee15e..60c8ffe 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -13268,6 +13268,7 @@
 	/* "read -s" needs to save/restore termios, can't allow ^C
 	 * to jump out of it.
 	 */
+ again:
 	INT_OFF;
 	r = shell_builtin_read(setvar0,
 		argptr,
@@ -13280,6 +13281,12 @@
 	);
 	INT_ON;
 
+	if ((uintptr_t)r == 1 && errno == EINTR) {
+		/* to get SIGCHLD: sleep 1 & read x; echo $x */
+		if (pending_sig == 0)
+			goto again;
+	}
+
 	if ((uintptr_t)r > 1)
 		ash_msg_and_raise_error(r);
 
diff --git a/shell/ash_test/ash-read/read_SIGCHLD.right b/shell/ash_test/ash-read/read_SIGCHLD.right
new file mode 100644
index 0000000..b3dc7ab
--- /dev/null
+++ b/shell/ash_test/ash-read/read_SIGCHLD.right
@@ -0,0 +1,2 @@
+x='Ok'
+exitcode:0
diff --git a/shell/ash_test/ash-read/read_SIGCHLD.tests b/shell/ash_test/ash-read/read_SIGCHLD.tests
new file mode 100755
index 0000000..c5f673a
--- /dev/null
+++ b/shell/ash_test/ash-read/read_SIGCHLD.tests
@@ -0,0 +1,4 @@
+x=BAD
+{ sleep 0.4; echo Ok; } | { sleep 0.2 & read x; echo "x='$x'"; }
+echo "exitcode:$?"
+
diff --git a/shell/hush.c b/shell/hush.c
index e18920f..125463a 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -9038,6 +9038,9 @@
  * - terminates shell (regardless of interactivity);
  * if it has non-empty trap:
  * - executes trap and returns to read;
+ * SIGCHLD from children:
+ * - does not interrupt read regardless of interactivity:
+ *   try: sleep 1 & read x; echo $x
  */
 static int FAST_FUNC builtin_read(char **argv)
 {
@@ -9071,7 +9074,7 @@
 
 	if ((uintptr_t)r == 1 && errno == EINTR) {
 		unsigned sig = check_and_run_traps();
-		if (sig && sig != SIGINT)
+		if (sig != SIGINT)
 			goto again;
 	}
 
diff --git a/shell/hush_test/hush-read/read_SIGCHLD.right b/shell/hush_test/hush-read/read_SIGCHLD.right
new file mode 100644
index 0000000..b3dc7ab
--- /dev/null
+++ b/shell/hush_test/hush-read/read_SIGCHLD.right
@@ -0,0 +1,2 @@
+x='Ok'
+exitcode:0
diff --git a/shell/hush_test/hush-read/read_SIGCHLD.tests b/shell/hush_test/hush-read/read_SIGCHLD.tests
new file mode 100755
index 0000000..c5f673a
--- /dev/null
+++ b/shell/hush_test/hush-read/read_SIGCHLD.tests
@@ -0,0 +1,4 @@
+x=BAD
+{ sleep 0.4; echo Ok; } | { sleep 0.2 & read x; echo "x='$x'"; }
+echo "exitcode:$?"
+
diff --git a/shell/shell_common.c b/shell/shell_common.c
index fb86e68..03b7d0b 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -204,15 +204,17 @@
 		c = buffer[bufpos];
 		if (c == '\0')
 			continue;
-		if (backslash) {
-			backslash = 0;
-			if (c != '\n')
-				goto put;
-			continue;
-		}
-		if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
-			backslash = 1;
-			continue;
+		if (!(read_flags & BUILTIN_READ_RAW)) {
+			if (backslash) {
+				backslash = 0;
+				if (c != '\n')
+					goto put;
+				continue;
+			}
+			if (c == '\\') {
+				backslash = 1;
+				continue;
+			}
 		}
 		if (c == '\n')
 			break;