hush: fix IFS handling in read

$ echo "X:Y:" | (IFS=": " read x y; echo "|$x|$y|")
|X|Y|
$ echo "X:Y  :  " | (IFS=": " read x y; echo "|$x|$y|")
|X|Y|

function                                             old     new   delta
shell_builtin_read                                  1320    1426    +106

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/shell/hush_test/hush-read/read_ifs2.right b/shell/hush_test/hush-read/read_ifs2.right
new file mode 100644
index 0000000..797137d
--- /dev/null
+++ b/shell/hush_test/hush-read/read_ifs2.right
@@ -0,0 +1,9 @@
+|X|Y:Z:|
+|X|Y:Z|
+|X|Y|
+|X|Y|
+|X||
+|X||
+|||
+Whitespace should be trimmed too:
+|X|Y|
diff --git a/shell/hush_test/hush-read/read_ifs2.tests b/shell/hush_test/hush-read/read_ifs2.tests
new file mode 100755
index 0000000..f01a689
--- /dev/null
+++ b/shell/hush_test/hush-read/read_ifs2.tests
@@ -0,0 +1,9 @@
+echo "X:Y:Z:" | (IFS=": " read x y; echo "|$x|$y|")
+echo "X:Y:Z"  | (IFS=": " read x y; echo "|$x|$y|")
+echo "X:Y:"   | (IFS=": " read x y; echo "|$x|$y|")
+echo "X:Y"    | (IFS=": " read x y; echo "|$x|$y|")
+echo "X:"     | (IFS=": " read x y; echo "|$x|$y|")
+echo "X"      | (IFS=": " read x y; echo "|$x|$y|")
+echo ""       | (IFS=": " read x y; echo "|$x|$y|")
+echo Whitespace should be trimmed too:
+echo "X:Y  : " | (IFS=": " read x y; echo "|$x|$y|")
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 9e58ee4..0a07296 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -274,9 +274,44 @@
 
 	if (argv[0]) {
 		/* Remove trailing space $IFS chars */
-		while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
+		while (--bufpos >= 0
+		 && isspace(buffer[bufpos])
+		 && strchr(ifs, buffer[bufpos]) != NULL
+		) {
 			continue;
+		}
 		buffer[bufpos + 1] = '\0';
+
+		/* Last variable takes the entire remainder with delimiters
+		 * (sans trailing whitespace $IFS),
+		 * but ***only "if there are fewer vars than fields"(c)***!
+		 * The "X:Y:" case below: there are two fields,
+		 * and therefore last delimiter (:) is eaten:
+		 * IFS=": "
+		 * echo "X:Y:Z:"  | (read x y; echo "|$x|$y|") # |X|Y:Z:|
+		 * echo "X:Y:Z"   | (read x y; echo "|$x|$y|") # |X|Y:Z|
+		 * echo "X:Y:"    | (read x y; echo "|$x|$y|") # |X|Y|, not |X|Y:|
+		 * echo "X:Y  : " | (read x y; echo "|$x|$y|") # |X|Y|
+		 */
+		if (bufpos >= 0
+		 && strchr(ifs, buffer[bufpos]) != NULL
+		) {
+			/* There _is_ a non-whitespace IFS char */
+			/* Skip whitespace IFS char before it */
+			while (--bufpos >= 0
+			 && isspace(buffer[bufpos])
+			 && strchr(ifs, buffer[bufpos]) != NULL
+			) {
+				continue;
+			}
+			/* Are there $IFS chars? */
+			if (strcspn(buffer, ifs) >= ++bufpos) {
+				/* No: last var takes one field, not more */
+				/* So, drop trailing IFS delims */
+				buffer[bufpos] = '\0';
+			}
+		}
+
 		/* Use the remainder as a value for the next variable */
 		setvar(*argv, buffer);
 		/* Set the rest to "" */