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 <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-04-11 16:02:58 +02:00
parent 34179956f9
commit 9678636911
5 changed files with 54 additions and 2 deletions

View File

@ -0,0 +1,5 @@
|x|
Ok1:0
|x|
||
Ok2:0

View File

@ -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:$?

View File

@ -930,6 +930,7 @@ struct globals {
unsigned getopt_count; unsigned getopt_count;
#endif #endif
const char *ifs; const char *ifs;
char *ifs_whitespace; /* = G.ifs or malloced */
const char *cwd; const char *cwd;
struct variable *top_var; struct variable *top_var;
char **expanded_assignments; 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 */ /* We know str here points to at least one IFS char */
last_is_ifs = 1; 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 */ if (!*str) /* EOL - do not finalize word */
break; 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! */ /* Start new word... but not always! */
/* Case "v=' a'; echo ''$v": we do need to finalize empty word: */ /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
if (output->has_quoted_part 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]) || (n > 0 && output->data[output->length - 1])
) { ) {
new_word:
o_addchr(output, '\0'); o_addchr(output, '\0');
debug_print_list("expand_on_ifs", output, n); debug_print_list("expand_on_ifs", output, n);
n = o_save_ptr(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 "$*" /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
* Result should be 3 lines: q w e, qwe, q w e * 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"); 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 = defifs;
G.ifs_whitespace = (char*)G.ifs;
}
IF_HUSH_JOB(pi->pgrp = -1;) IF_HUSH_JOB(pi->pgrp = -1;)
pi->stopped_cmds = 0; pi->stopped_cmds = 0;

View File

@ -0,0 +1,5 @@
|x|
Ok1:0
|x|
||
Ok2:0

View File

@ -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:$?