mirror of
https://github.com/sheumann/hush.git
synced 2024-08-26 12:29:04 +00:00
hush: disallow "{echo hi; }" (require whitespace)
and "{ echo hi }" (require semicolon or &) function old new delta parse_stream 2098 2176 +78 done_command 98 84 -14 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/1 up/down: 78/-14) Total: 64 bytes
This commit is contained in:
parent
5ff9629b8f
commit
f8c1f02d2f
55
shell/hush.c
55
shell/hush.c
@ -319,6 +319,10 @@ struct command {
|
|||||||
*/
|
*/
|
||||||
struct redir_struct *redirects; /* I/O redirections */
|
struct redir_struct *redirects; /* I/O redirections */
|
||||||
};
|
};
|
||||||
|
/* Is there anything in this command at all? */
|
||||||
|
#define IS_NULL_CMD(cmd) \
|
||||||
|
(!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
|
||||||
|
|
||||||
|
|
||||||
struct pipe {
|
struct pipe {
|
||||||
struct pipe *next;
|
struct pipe *next;
|
||||||
@ -341,6 +345,9 @@ typedef enum pipe_style {
|
|||||||
PIPE_OR = 3,
|
PIPE_OR = 3,
|
||||||
PIPE_BG = 4,
|
PIPE_BG = 4,
|
||||||
} pipe_style;
|
} pipe_style;
|
||||||
|
/* Is there anything in this pipe at all? */
|
||||||
|
#define IS_NULL_PIPE(pi) \
|
||||||
|
((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
|
||||||
|
|
||||||
/* This holds pointers to the various results of parsing */
|
/* This holds pointers to the various results of parsing */
|
||||||
struct parse_context {
|
struct parse_context {
|
||||||
@ -3971,8 +3978,10 @@ static struct pipe *new_pipe(void)
|
|||||||
return pi;
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Command (member of a pipe) is complete. The only possible error here
|
/* Command (member of a pipe) is complete, or we start a new pipe
|
||||||
* is out of memory, in which case xmalloc exits. */
|
* if ctx->command is NULL.
|
||||||
|
* No errors possible here.
|
||||||
|
*/
|
||||||
static int done_command(struct parse_context *ctx)
|
static int done_command(struct parse_context *ctx)
|
||||||
{
|
{
|
||||||
/* The command is really already in the pipe structure, so
|
/* The command is really already in the pipe structure, so
|
||||||
@ -3981,13 +3990,9 @@ static int done_command(struct parse_context *ctx)
|
|||||||
struct command *command = ctx->command;
|
struct command *command = ctx->command;
|
||||||
|
|
||||||
if (command) {
|
if (command) {
|
||||||
if (command->group == NULL
|
if (IS_NULL_CMD(command)) {
|
||||||
&& command->argv == NULL
|
|
||||||
&& command->redirects == NULL
|
|
||||||
) {
|
|
||||||
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
|
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
|
||||||
memset(command, 0, sizeof(*command)); /* paranoia */
|
goto clear_and_ret;
|
||||||
return pi->num_cmds;
|
|
||||||
}
|
}
|
||||||
pi->num_cmds++;
|
pi->num_cmds++;
|
||||||
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
|
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
|
||||||
@ -3999,12 +4004,9 @@ static int done_command(struct parse_context *ctx)
|
|||||||
/* Only real trickiness here is that the uncommitted
|
/* Only real trickiness here is that the uncommitted
|
||||||
* command structure is not counted in pi->num_cmds. */
|
* command structure is not counted in pi->num_cmds. */
|
||||||
pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
|
pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
|
||||||
command = &pi->cmds[pi->num_cmds];
|
ctx->command = command = &pi->cmds[pi->num_cmds];
|
||||||
|
clear_and_ret:
|
||||||
memset(command, 0, sizeof(*command));
|
memset(command, 0, sizeof(*command));
|
||||||
|
|
||||||
ctx->command = command;
|
|
||||||
/* but ctx->pipe and ctx->list_head remain unchanged */
|
|
||||||
|
|
||||||
return pi->num_cmds; /* used only for 0/nonzero check */
|
return pi->num_cmds; /* used only for 0/nonzero check */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4024,9 +4026,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
|
|||||||
|
|
||||||
/* Without this check, even just <enter> on command line generates
|
/* Without this check, even just <enter> on command line generates
|
||||||
* tree of three NOPs (!). Which is harmless but annoying.
|
* tree of three NOPs (!). Which is harmless but annoying.
|
||||||
* IOW: it is safe to do it unconditionally.
|
* IOW: it is safe to do it unconditionally. */
|
||||||
* RES_NONE case is for "for a in; do ..." (empty IN set)
|
|
||||||
* and other cases to work. */
|
|
||||||
if (not_null
|
if (not_null
|
||||||
#if ENABLE_HUSH_IF
|
#if ENABLE_HUSH_IF
|
||||||
|| ctx->ctx_res_w == RES_FI
|
|| ctx->ctx_res_w == RES_FI
|
||||||
@ -4048,7 +4048,7 @@ static void done_pipe(struct parse_context *ctx, pipe_style type)
|
|||||||
ctx->pipe->next = new_p;
|
ctx->pipe->next = new_p;
|
||||||
ctx->pipe = new_p;
|
ctx->pipe = new_p;
|
||||||
/* RES_THEN, RES_DO etc are "sticky" -
|
/* RES_THEN, RES_DO etc are "sticky" -
|
||||||
* they remain set for commands inside if/while.
|
* they remain set for pipes inside if/while.
|
||||||
* This is used to control execution.
|
* This is used to control execution.
|
||||||
* RES_FOR and RES_IN are NOT sticky (needed to support
|
* RES_FOR and RES_IN are NOT sticky (needed to support
|
||||||
* cases where variable or value happens to match a keyword):
|
* cases where variable or value happens to match a keyword):
|
||||||
@ -4304,7 +4304,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
|
|||||||
&& ctx->ctx_res_w != RES_IN
|
&& ctx->ctx_res_w != RES_IN
|
||||||
# endif
|
# endif
|
||||||
) {
|
) {
|
||||||
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
|
debug_printf_parse("checking '%s' for reserved-ness\n", word->data);
|
||||||
if (reserved_word(word, ctx)) {
|
if (reserved_word(word, ctx)) {
|
||||||
o_reset_to_empty_unquoted(word);
|
o_reset_to_empty_unquoted(word);
|
||||||
debug_printf_parse("done_word return %d\n",
|
debug_printf_parse("done_word return %d\n",
|
||||||
@ -4775,6 +4775,14 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
|
|||||||
if (ch == '(') {
|
if (ch == '(') {
|
||||||
endch = ')';
|
endch = ')';
|
||||||
command->grp_type = GRP_SUBSHELL;
|
command->grp_type = GRP_SUBSHELL;
|
||||||
|
} else {
|
||||||
|
/* bash does not allow "{echo...", requires whitespace */
|
||||||
|
ch = i_getch(input);
|
||||||
|
if (ch != ' ' && ch != '\t' && ch != '\n') {
|
||||||
|
syntax_error_unexpected_ch(ch);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nommu_addchr(&ctx->as_string, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -5352,13 +5360,11 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (end_trigger && end_trigger == ch
|
if (end_trigger && end_trigger == ch
|
||||||
&& (heredoc_cnt == 0 || end_trigger != ';')
|
&& (heredoc_cnt == 0 || end_trigger != ';')
|
||||||
) {
|
) {
|
||||||
//TODO: disallow "{ cmd }" without semicolon
|
|
||||||
if (heredoc_cnt) {
|
if (heredoc_cnt) {
|
||||||
/* This is technically valid:
|
/* This is technically valid:
|
||||||
* { cat <<HERE; }; echo Ok
|
* { cat <<HERE; }; echo Ok
|
||||||
* heredoc
|
* heredoc
|
||||||
* heredoc
|
* heredoc
|
||||||
* heredoc
|
|
||||||
* HERE
|
* HERE
|
||||||
* but we don't support this.
|
* but we don't support this.
|
||||||
* We require heredoc to be in enclosing {}/(),
|
* We require heredoc to be in enclosing {}/(),
|
||||||
@ -5370,6 +5376,15 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (done_word(&dest, &ctx)) {
|
if (done_word(&dest, &ctx)) {
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
|
/* Disallow "{ cmd }" without semicolon or & */
|
||||||
|
//debug_printf_parse("null pi %d\n", IS_NULL_PIPE(ctx.pipe))
|
||||||
|
//debug_printf_parse("null cmd %d\n", IS_NULL_CMD(ctx.command))
|
||||||
|
if (ch == '}'
|
||||||
|
&& !(IS_NULL_PIPE(ctx.pipe) && IS_NULL_CMD(ctx.command))
|
||||||
|
) {
|
||||||
|
syntax_error_unexpected_ch(ch);
|
||||||
|
goto parse_error;
|
||||||
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
dest.o_assignment = MAYBE_ASSIGNMENT;
|
dest.o_assignment = MAYBE_ASSIGNMENT;
|
||||||
/* Do we sit outside of any if's, loops or case's? */
|
/* Do we sit outside of any if's, loops or case's? */
|
||||||
|
Loading…
Reference in New Issue
Block a user