hush: support "f() (cmd)" functions

Many other shells support this construct

function                                             old     new   delta
parse_stream                                        2950    3018     +68

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-04-03 14:56:52 +02:00
parent 49015a60cb
commit fbf44854a3
6 changed files with 61 additions and 42 deletions

View File

@ -1,6 +1,3 @@
1 1
2 2
3 3
1
2
3

View File

@ -6,8 +6,3 @@ f 2
f() ( echo $1 ) f() ( echo $1 )
f 3 f 3
f() for i in 1 2 3; do
echo $i
done
f

View File

@ -0,0 +1,3 @@
1
2
3

View File

@ -0,0 +1,4 @@
f() for i in 1 2 3; do
echo $i
done
f

View File

@ -4297,6 +4297,11 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
/* dest contains characters seen prior to ( or {. /* dest contains characters seen prior to ( or {.
* Typically it's empty, but for function defs, * Typically it's empty, but for function defs,
* it contains function name (without '()'). */ * it contains function name (without '()'). */
#if BB_MMU
# define as_string NULL
#else
char *as_string = NULL;
#endif
struct pipe *pipe_list; struct pipe *pipe_list;
int endch; int endch;
struct command *command = ctx->command; struct command *command = ctx->command;
@ -4325,7 +4330,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
do do
ch = i_getch(input); ch = i_getch(input);
while (ch == ' ' || ch == '\t' || ch == '\n'); while (ch == ' ' || ch == '\t' || ch == '\n');
if (ch != '{') { if (ch != '{' && ch != '(') {
syntax_error_unexpected_ch(ch); syntax_error_unexpected_ch(ch);
return 1; return 1;
} }
@ -4347,12 +4352,12 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
} }
#endif #endif
#if ENABLE_HUSH_FUNCTIONS IF_HUSH_FUNCTIONS(skip:)
skip:
#endif
endch = '}'; endch = '}';
if (ch == '(') { if (ch == '(') {
endch = ')'; endch = ')';
IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
command->cmd_type = CMD_SUBSHELL; command->cmd_type = CMD_SUBSHELL;
} else { } else {
/* bash does not allow "{echo...", requires whitespace */ /* bash does not allow "{echo...", requires whitespace */
@ -4369,17 +4374,12 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
} }
} }
{
#if BB_MMU
# define as_string NULL
#else
char *as_string = NULL;
#endif
pipe_list = parse_stream(&as_string, input, endch); pipe_list = parse_stream(&as_string, input, endch);
#if !BB_MMU #if !BB_MMU
if (as_string) if (as_string)
o_addstr(&ctx->as_string, as_string); o_addstr(&ctx->as_string, as_string);
#endif #endif
/* empty ()/{} or parse error? */ /* empty ()/{} or parse error? */
if (!pipe_list || pipe_list == ERR_PTR) { if (!pipe_list || pipe_list == ERR_PTR) {
/* parse_stream already emitted error msg */ /* parse_stream already emitted error msg */
@ -4389,18 +4389,39 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
"parse_stream returned %p\n", pipe_list); "parse_stream returned %p\n", pipe_list);
return 1; return 1;
} }
command->group = pipe_list;
#if !BB_MMU #if !BB_MMU
as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
command->group_as_string = as_string; command->group_as_string = as_string;
debug_printf_parse("end of group, remembering as:'%s'\n", debug_printf_parse("end of group, remembering as:'%s'\n",
command->group_as_string); command->group_as_string);
#endif #endif
#undef as_string
#if ENABLE_HUSH_FUNCTIONS
/* Convert "f() (cmds)" to "f() {(cmds)}" */
if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
struct command *cmd2;
cmd2 = xzalloc(sizeof(*cmd2));
cmd2->cmd_type = CMD_SUBSHELL;
cmd2->group = pipe_list;
# if !BB_MMU
//UNTESTED!
cmd2->group_as_string = command->group_as_string;
command->group_as_string = xasprintf("(%s)", command->group_as_string);
# endif
pipe_list = new_pipe();
pipe_list->cmds = cmd2;
pipe_list->num_cmds = 1;
} }
#endif
command->group = pipe_list;
debug_printf_parse("parse_group return 0\n"); debug_printf_parse("parse_group return 0\n");
return 0; return 0;
/* command remains "open", available for possible redirects */ /* command remains "open", available for possible redirects */
#undef as_string
} }
#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS #if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS

View File

@ -1,9 +1,8 @@
f() { echo $1; } f() { echo $1; }
f 1 f 1
# hush fails on this syntax, but i've never seen anyone use it ... f() ( echo $1; )
#f() ( echo $1; )
f 2 f 2
#f() ( echo $1 ) f() ( echo $1 )
f 3 f 3