From 95ebcf79ff6f8ad21ceacb7bac665fb86c078d84 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Tue, 3 Nov 2015 09:42:23 +0000 Subject: [PATCH] ash: add support for bash 'function' keyword Where the POSIX shell allows functions to be defined as: name () compound-command [ redirections ] bash adds the alternative syntax: function name [()] compound-command [ redirections ] Implement this in ash's bash compatibility mode. Most compound commands work (for/while/until/if/case/[[]]/{}); one exception is: function f (echo "no way!") The other two variants work: f() (echo "ok") function f() (echo "also ok") function old new delta parse_command 1555 1744 +189 tokname_array 232 240 +8 .rodata 155612 155566 -46 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 197/-46) Total: 151 bytes Signed-off-by: Ron Yorston Signed-off-by: Denys Vlasenko --- shell/ash.c | 101 ++++++++++++++++------- shell/ash_test/ash-misc/func_bash1.right | 12 +++ shell/ash_test/ash-misc/func_bash1.tests | 28 +++++++ 3 files changed, 110 insertions(+), 31 deletions(-) create mode 100644 shell/ash_test/ash-misc/func_bash1.right create mode 100755 shell/ash_test/ash-misc/func_bash1.tests diff --git a/shell/ash.c b/shell/ash.c index 84502636a..e7a867f52 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -7726,36 +7726,40 @@ changepath(const char *new) clearcmdentry(firstchange); builtinloc = idx_bltin; } - -#define TEOF 0 -#define TNL 1 -#define TREDIR 2 -#define TWORD 3 -#define TSEMI 4 -#define TBACKGND 5 -#define TAND 6 -#define TOR 7 -#define TPIPE 8 -#define TLP 9 -#define TRP 10 -#define TENDCASE 11 -#define TENDBQUOTE 12 -#define TNOT 13 -#define TCASE 14 -#define TDO 15 -#define TDONE 16 -#define TELIF 17 -#define TELSE 18 -#define TESAC 19 -#define TFI 20 -#define TFOR 21 -#define TIF 22 -#define TIN 23 -#define TTHEN 24 -#define TUNTIL 25 -#define TWHILE 26 -#define TBEGIN 27 -#define TEND 28 +enum { + TEOF, + TNL, + TREDIR, + TWORD, + TSEMI, + TBACKGND, + TAND, + TOR, + TPIPE, + TLP, + TRP, + TENDCASE, + TENDBQUOTE, + TNOT, + TCASE, + TDO, + TDONE, + TELIF, + TELSE, + TESAC, + TFI, + TFOR, +#if ENABLE_ASH_BASH_COMPAT + TFUNCTION, +#endif + TIF, + TIN, + TTHEN, + TUNTIL, + TWHILE, + TBEGIN, + TEND +}; typedef smallint token_id_t; /* first char is indicating which tokens mark the end of a list */ @@ -7784,6 +7788,9 @@ static const char *const tokname_array[] = { "\1esac", "\1fi", "\0for", +#if ENABLE_ASH_BASH_COMPAT + "\0function", +#endif "\0if", "\0in", "\1then", @@ -10762,6 +10769,7 @@ simplecmd(void) int savecheckkwd; #if ENABLE_ASH_BASH_COMPAT smallint double_brackets_flag = 0; + smallint function_flag = 0; #endif args = NULL; @@ -10778,6 +10786,11 @@ simplecmd(void) t = readtoken(); switch (t) { #if ENABLE_ASH_BASH_COMPAT + case TFUNCTION: + if (peektoken() != TWORD) + raise_error_unexpected_syntax(TWORD); + function_flag = 1; + break; case TAND: /* "&&" */ case TOR: /* "||" */ if (!double_brackets_flag) { @@ -10806,6 +10819,29 @@ simplecmd(void) app = &n->narg.next; savecheckkwd = 0; } +#if ENABLE_ASH_BASH_COMPAT + if (function_flag) { + checkkwd = CHKNL | CHKKWD; + switch (peektoken()) { + case TBEGIN: + case TIF: + case TCASE: + case TUNTIL: + case TWHILE: + case TFOR: + goto do_func; + case TLP: + function_flag = 0; + break; + case TWORD: + if (strcmp("[[", wordtext) == 0) + goto do_func; + /* fall through */ + default: + raise_error_unexpected_syntax(-1); + } + } +#endif break; case TREDIR: *rpp = n = redirnode; @@ -10813,6 +10849,7 @@ simplecmd(void) parsefname(); /* read name of redirection file */ break; case TLP: + IF_ASH_BASH_COMPAT(do_func:) if (args && app == &args->narg.next && !vars && !redir ) { @@ -10820,7 +10857,7 @@ simplecmd(void) const char *name; /* We have a function */ - if (readtoken() != TRP) + if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP) raise_error_unexpected_syntax(TRP); name = n->narg.text; if (!goodname(name) @@ -10833,6 +10870,7 @@ simplecmd(void) n->narg.next = parse_command(); return n; } + IF_ASH_BASH_COMPAT(function_flag = 0;) /* fall through */ default: tokpushback = 1; @@ -11013,6 +11051,7 @@ parse_command(void) n1 = list(0); t = TEND; break; + IF_ASH_BASH_COMPAT(case TFUNCTION:) case TWORD: case TREDIR: tokpushback = 1; diff --git a/shell/ash_test/ash-misc/func_bash1.right b/shell/ash_test/ash-misc/func_bash1.right new file mode 100644 index 000000000..41bf8828c --- /dev/null +++ b/shell/ash_test/ash-misc/func_bash1.right @@ -0,0 +1,12 @@ +1 +2 +3 +1 +2 +3 +1 +2 +3 +1 +2 +3 diff --git a/shell/ash_test/ash-misc/func_bash1.tests b/shell/ash_test/ash-misc/func_bash1.tests new file mode 100755 index 000000000..2cc0970e8 --- /dev/null +++ b/shell/ash_test/ash-misc/func_bash1.tests @@ -0,0 +1,28 @@ +function f() { echo $1; } +f 1 + +function f() ( echo $1; ) +f 2 + +function f() ( echo $1 ) +f 3 + +function f() for i in 1 2 3; do + echo $i +done +f + +function f { echo $1; } +f 1 + +# the next two don't work +#function f ( echo $1; ) +f 2 + +#function f ( echo $1 ) +f 3 + +function f for i in 1 2 3; do + echo $i +done +f