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>
This commit is contained in:
Denys Vlasenko 2018-04-11 17:18:34 +02:00
parent 9678636911
commit 44257ad5d0
3 changed files with 54 additions and 1 deletions

View File

@ -0,0 +1,9 @@
|X|Y:Z:|
|X|Y:Z|
|X|Y|
|X|Y|
|X||
|X||
|||
Whitespace should be trimmed too:
|X|Y|

View File

@ -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|")

View File

@ -274,9 +274,44 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
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 "" */