From 9678636911b39a7adf9b51d5b625cf4dc7e4ac81 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 11 Apr 2018 16:02:58 +0200 Subject: [PATCH] hush: IFS fixes $ IFS=": "; x=" "; set x $x; for v; do echo "|$v|"; done |x| $ IFS=": "; x=":"; set x $x; for v; do echo "|$v|"; done |x| || function old new delta run_pipe 1789 1870 +81 expand_on_ifs 310 361 +51 pseudo_exec_argv 588 591 +3 builtin_local 50 53 +3 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 4/0 up/down: 138/0) Total: 138 bytes Signed-off-by: Denys Vlasenko --- .../ash-vars/var_wordsplit_ifs4.right | 5 +++ .../ash-vars/var_wordsplit_ifs4.tests | 4 ++ shell/hush.c | 38 ++++++++++++++++++- .../hush-vars/var_wordsplit_ifs4.right | 5 +++ .../hush-vars/var_wordsplit_ifs4.tests | 4 ++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 shell/ash_test/ash-vars/var_wordsplit_ifs4.right create mode 100755 shell/ash_test/ash-vars/var_wordsplit_ifs4.tests create mode 100644 shell/hush_test/hush-vars/var_wordsplit_ifs4.right create mode 100755 shell/hush_test/hush-vars/var_wordsplit_ifs4.tests diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs4.right b/shell/ash_test/ash-vars/var_wordsplit_ifs4.right new file mode 100644 index 000000000..c27284c31 --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs4.right @@ -0,0 +1,5 @@ +|x| +Ok1:0 +|x| +|| +Ok2:0 diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs4.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs4.tests new file mode 100755 index 000000000..638bfbb28 --- /dev/null +++ b/shell/ash_test/ash-vars/var_wordsplit_ifs4.tests @@ -0,0 +1,4 @@ +IFS=": "; x=" "; set x $x; for v; do echo "|$v|"; done +echo Ok1:$? +IFS=": "; x=":"; set x $x; for v; do echo "|$v|"; done +echo Ok2:$? diff --git a/shell/hush.c b/shell/hush.c index 248364be2..8e95a26a6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -930,6 +930,7 @@ struct globals { unsigned getopt_count; #endif const char *ifs; + char *ifs_whitespace; /* = G.ifs or malloced */ const char *cwd; struct variable *top_var; char **expanded_assignments; @@ -5696,10 +5697,20 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha /* We know str here points to at least one IFS char */ last_is_ifs = 1; - str += strspn(str, G.ifs); /* skip IFS chars */ + str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */ if (!*str) /* EOL - do not finalize word */ break; + if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */ + && strchr(G.ifs, *str) /* the second check would fail */ + ) { + /* This is a non-whitespace $IFS char */ + /* Skip it and IFS whitespace chars, start new word */ + str++; + str += strspn(str, G.ifs_whitespace); + goto new_word; + } + /* Start new word... but not always! */ /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ if (output->has_quoted_part @@ -5710,6 +5721,7 @@ static int expand_on_ifs(int *ended_with_ifs, o_string *output, int n, const cha */ || (n > 0 && output->data[output->length - 1]) ) { + new_word: o_addchr(output, '\0'); debug_print_list("expand_on_ifs", output, n); n = o_save_ptr(output, n); @@ -8283,9 +8295,31 @@ static NOINLINE int run_pipe(struct pipe *pi) /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*" * Result should be 3 lines: q w e, qwe, q w e */ + if (G.ifs_whitespace != G.ifs) + free(G.ifs_whitespace); G.ifs = get_local_var_value("IFS"); - if (!G.ifs) + if (G.ifs) { + char *p; + G.ifs_whitespace = (char*)G.ifs; + p = skip_whitespace(G.ifs); + if (*p) { + /* Not all $IFS is whitespace */ + char *d; + int len = p - G.ifs; + p = skip_non_whitespace(p); + G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */ + d = mempcpy(G.ifs_whitespace, G.ifs, len); + while (*p) { + if (isspace(*p)) + *d++ = *p; + p++; + } + *d = '\0'; + } + } else { G.ifs = defifs; + G.ifs_whitespace = (char*)G.ifs; + } IF_HUSH_JOB(pi->pgrp = -1;) pi->stopped_cmds = 0; diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs4.right b/shell/hush_test/hush-vars/var_wordsplit_ifs4.right new file mode 100644 index 000000000..c27284c31 --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs4.right @@ -0,0 +1,5 @@ +|x| +Ok1:0 +|x| +|| +Ok2:0 diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs4.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs4.tests new file mode 100755 index 000000000..638bfbb28 --- /dev/null +++ b/shell/hush_test/hush-vars/var_wordsplit_ifs4.tests @@ -0,0 +1,4 @@ +IFS=": "; x=" "; set x $x; for v; do echo "|$v|"; done +echo Ok1:$? +IFS=": "; x=":"; set x $x; for v; do echo "|$v|"; done +echo Ok2:$?