mirror of
https://github.com/sheumann/hush.git
synced 2024-12-23 05:29:58 +00:00
hush: fix set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
parent
b103fb10cf
commit
1fd3d94a6c
65
shell/hush.c
65
shell/hush.c
@ -3839,10 +3839,10 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
o_addchr(&dest, '\0');
|
o_addchr(&dest, '\0');
|
||||||
dest.length = 0;
|
dest.length = 0;
|
||||||
|
|
||||||
G.ifs = get_local_var_value("IFS");
|
/* We used to separate words on $IFS here. This was wrong.
|
||||||
if (G.ifs == NULL)
|
* $IFS is used only for word splitting when $var is expanded,
|
||||||
G.ifs = defifs;
|
* here we should use blank chars as separators, not $iFS
|
||||||
|
*/
|
||||||
reset:
|
reset:
|
||||||
#if ENABLE_HUSH_INTERACTIVE
|
#if ENABLE_HUSH_INTERACTIVE
|
||||||
input->promptmode = 0; /* PS1 */
|
input->promptmode = 0; /* PS1 */
|
||||||
@ -3852,7 +3852,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
is_in_dquote = 0;
|
is_in_dquote = 0;
|
||||||
heredoc_cnt = 0;
|
heredoc_cnt = 0;
|
||||||
while (1) {
|
while (1) {
|
||||||
const char *is_ifs;
|
const char *is_blank;
|
||||||
const char *is_special;
|
const char *is_special;
|
||||||
int ch;
|
int ch;
|
||||||
int next;
|
int next;
|
||||||
@ -3922,20 +3922,20 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (ctx.command->argv /* word [word]{... - non-special */
|
if (ctx.command->argv /* word [word]{... - non-special */
|
||||||
|| dest.length /* word{... - non-special */
|
|| dest.length /* word{... - non-special */
|
||||||
|| dest.has_quoted_part /* ""{... - non-special */
|
|| dest.has_quoted_part /* ""{... - non-special */
|
||||||
|| (next != ';' /* }; - special */
|
|| (next != ';' /* }; - special */
|
||||||
&& next != ')' /* }) - special */
|
&& next != ')' /* }) - special */
|
||||||
&& next != '&' /* }& and }&& ... - special */
|
&& next != '&' /* }& and }&& ... - special */
|
||||||
&& next != '|' /* }|| ... - special */
|
&& next != '|' /* }|| ... - special */
|
||||||
&& !strchr(G.ifs, next) /* {word - non-special */
|
&& !strchr(defifs, next) /* {word - non-special */
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
/* They are not special, skip "{}" */
|
/* They are not special, skip "{}" */
|
||||||
is_special += 2;
|
is_special += 2;
|
||||||
}
|
}
|
||||||
is_special = strchr(is_special, ch);
|
is_special = strchr(is_special, ch);
|
||||||
is_ifs = strchr(G.ifs, ch);
|
is_blank = strchr(defifs, ch);
|
||||||
|
|
||||||
if (!is_special && !is_ifs) { /* ordinary char */
|
if (!is_special && !is_blank) { /* ordinary char */
|
||||||
ordinary_char:
|
ordinary_char:
|
||||||
o_addQchr(&dest, ch);
|
o_addQchr(&dest, ch);
|
||||||
if ((dest.o_assignment == MAYBE_ASSIGNMENT
|
if ((dest.o_assignment == MAYBE_ASSIGNMENT
|
||||||
@ -3948,7 +3948,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ifs) {
|
if (is_blank) {
|
||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&dest, &ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
@ -3973,7 +3973,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
dest.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
ch = ';';
|
ch = ';';
|
||||||
/* note: if (is_ifs) continue;
|
/* note: if (is_blank) continue;
|
||||||
* will still trigger for us */
|
* will still trigger for us */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4041,7 +4041,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
skip_end_trigger:
|
skip_end_trigger:
|
||||||
if (is_ifs)
|
if (is_blank)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Catch <, > before deciding whether this word is
|
/* Catch <, > before deciding whether this word is
|
||||||
@ -4348,7 +4348,7 @@ static int process_command_subs(o_string *dest, const char *s);
|
|||||||
* of strings. (Think VAR="a b"; echo $VAR).
|
* of strings. (Think VAR="a b"; echo $VAR).
|
||||||
* This new list is allocated as a single malloc block.
|
* This new list is allocated as a single malloc block.
|
||||||
* NULL-terminated list of char* pointers is at the beginning of it,
|
* NULL-terminated list of char* pointers is at the beginning of it,
|
||||||
* followed by strings themself.
|
* followed by strings themselves.
|
||||||
* Caller can deallocate entire list by single free(list). */
|
* Caller can deallocate entire list by single free(list). */
|
||||||
|
|
||||||
/* Store given string, finalizing the word and starting new one whenever
|
/* Store given string, finalizing the word and starting new one whenever
|
||||||
@ -4505,18 +4505,20 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
|
|||||||
char *exp_saveptr; /* points to expansion operator */
|
char *exp_saveptr; /* points to expansion operator */
|
||||||
char *exp_word = exp_word; /* for compiler */
|
char *exp_word = exp_word; /* for compiler */
|
||||||
|
|
||||||
|
*p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
|
||||||
var = arg;
|
var = arg;
|
||||||
*p = '\0';
|
|
||||||
exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
|
exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
|
||||||
first_char = arg[0] = first_ch & 0x7f;
|
first_char = arg[0] = first_ch & 0x7f;
|
||||||
exp_op = 0;
|
exp_op = 0;
|
||||||
|
|
||||||
if (first_char == '#' && arg[1] && !exp_saveptr) {
|
if (first_char == '#' /* ${#... */
|
||||||
/* handle length expansion ${#var} */
|
&& arg[1] && !exp_saveptr /* not ${#} and not ${#<op_char>...} */
|
||||||
|
) {
|
||||||
|
/* It must be length operator: ${#var} */
|
||||||
var++;
|
var++;
|
||||||
exp_op = 'L';
|
exp_op = 'L';
|
||||||
} else {
|
} else {
|
||||||
/* maybe handle parameter expansion */
|
/* Maybe handle parameter expansion */
|
||||||
if (exp_saveptr /* if 2nd char is one of expansion operators */
|
if (exp_saveptr /* if 2nd char is one of expansion operators */
|
||||||
&& strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
|
&& strchr(NUMERIC_SPECVARS_STR, first_char) /* 1st char is special variable */
|
||||||
) {
|
) {
|
||||||
@ -4531,8 +4533,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
|
|||||||
exp_word = exp_saveptr + 1;
|
exp_word = exp_saveptr + 1;
|
||||||
if (exp_op == ':') {
|
if (exp_op == ':') {
|
||||||
exp_op = *exp_word++;
|
exp_op = *exp_word++;
|
||||||
|
//TODO: try ${var:} and ${var:bogus} in non-bash config
|
||||||
if (ENABLE_HUSH_BASH_COMPAT
|
if (ENABLE_HUSH_BASH_COMPAT
|
||||||
&& (exp_op == '\0' || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
|
&& (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
|
||||||
) {
|
) {
|
||||||
/* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
|
/* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
|
||||||
exp_op = ':';
|
exp_op = ':';
|
||||||
@ -4543,7 +4546,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
|
|||||||
} /* else: it's not an expansion op, but bare ${var} */
|
} /* else: it's not an expansion op, but bare ${var} */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* lookup the variable in question */
|
/* Look up the variable in question */
|
||||||
if (isdigit(var[0])) {
|
if (isdigit(var[0])) {
|
||||||
/* parse_dollar() should have vetted var for us */
|
/* parse_dollar() should have vetted var for us */
|
||||||
int n = xatoi_positive(var);
|
int n = xatoi_positive(var);
|
||||||
@ -4622,7 +4625,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
|
|||||||
/* It's ${var/[/]pattern[/repl]} thing */
|
/* It's ${var/[/]pattern[/repl]} thing */
|
||||||
/*
|
/*
|
||||||
* Pattern is taken literally, while
|
* Pattern is taken literally, while
|
||||||
* repl should be de-backslased and globbed
|
* repl should be unbackslashed and globbed
|
||||||
* by the usual expansion rules:
|
* by the usual expansion rules:
|
||||||
* >az; >bz;
|
* >az; >bz;
|
||||||
* v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
|
* v='a bz'; echo "${v/a*z/a*z}" prints "a*z"
|
||||||
@ -4998,9 +5001,12 @@ static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Used for expansion of right hand of assignments */
|
/* Used for expansion of right hand of assignments,
|
||||||
/* NB: should NOT do globbing!
|
* $((...)), heredocs, variable espansion parts.
|
||||||
* "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*" */
|
*
|
||||||
|
* NB: should NOT do globbing!
|
||||||
|
* "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
|
||||||
|
*/
|
||||||
static char *expand_string_to_string(const char *str)
|
static char *expand_string_to_string(const char *str)
|
||||||
{
|
{
|
||||||
char *argv[2], **list;
|
char *argv[2], **list;
|
||||||
@ -6413,6 +6419,13 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
|
debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
|
||||||
debug_enter();
|
debug_enter();
|
||||||
|
|
||||||
|
/* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
|
||||||
|
* Result should be 3 lines: q w e, qwe, q w e
|
||||||
|
*/
|
||||||
|
G.ifs = get_local_var_value("IFS");
|
||||||
|
if (!G.ifs)
|
||||||
|
G.ifs = defifs;
|
||||||
|
|
||||||
IF_HUSH_JOB(pi->pgrp = -1;)
|
IF_HUSH_JOB(pi->pgrp = -1;)
|
||||||
pi->stopped_cmds = 0;
|
pi->stopped_cmds = 0;
|
||||||
command = &pi->cmds[0];
|
command = &pi->cmds[0];
|
||||||
|
Loading…
Reference in New Issue
Block a user