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 <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2015-11-03 09:42:23 +00:00 committed by Denys Vlasenko
parent bc9bee01f3
commit 95ebcf79ff
3 changed files with 110 additions and 31 deletions

View File

@ -7726,36 +7726,40 @@ changepath(const char *new)
clearcmdentry(firstchange); clearcmdentry(firstchange);
builtinloc = idx_bltin; builtinloc = idx_bltin;
} }
enum {
#define TEOF 0 TEOF,
#define TNL 1 TNL,
#define TREDIR 2 TREDIR,
#define TWORD 3 TWORD,
#define TSEMI 4 TSEMI,
#define TBACKGND 5 TBACKGND,
#define TAND 6 TAND,
#define TOR 7 TOR,
#define TPIPE 8 TPIPE,
#define TLP 9 TLP,
#define TRP 10 TRP,
#define TENDCASE 11 TENDCASE,
#define TENDBQUOTE 12 TENDBQUOTE,
#define TNOT 13 TNOT,
#define TCASE 14 TCASE,
#define TDO 15 TDO,
#define TDONE 16 TDONE,
#define TELIF 17 TELIF,
#define TELSE 18 TELSE,
#define TESAC 19 TESAC,
#define TFI 20 TFI,
#define TFOR 21 TFOR,
#define TIF 22 #if ENABLE_ASH_BASH_COMPAT
#define TIN 23 TFUNCTION,
#define TTHEN 24 #endif
#define TUNTIL 25 TIF,
#define TWHILE 26 TIN,
#define TBEGIN 27 TTHEN,
#define TEND 28 TUNTIL,
TWHILE,
TBEGIN,
TEND
};
typedef smallint token_id_t; typedef smallint token_id_t;
/* first char is indicating which tokens mark the end of a list */ /* first char is indicating which tokens mark the end of a list */
@ -7784,6 +7788,9 @@ static const char *const tokname_array[] = {
"\1esac", "\1esac",
"\1fi", "\1fi",
"\0for", "\0for",
#if ENABLE_ASH_BASH_COMPAT
"\0function",
#endif
"\0if", "\0if",
"\0in", "\0in",
"\1then", "\1then",
@ -10762,6 +10769,7 @@ simplecmd(void)
int savecheckkwd; int savecheckkwd;
#if ENABLE_ASH_BASH_COMPAT #if ENABLE_ASH_BASH_COMPAT
smallint double_brackets_flag = 0; smallint double_brackets_flag = 0;
smallint function_flag = 0;
#endif #endif
args = NULL; args = NULL;
@ -10778,6 +10786,11 @@ simplecmd(void)
t = readtoken(); t = readtoken();
switch (t) { switch (t) {
#if ENABLE_ASH_BASH_COMPAT #if ENABLE_ASH_BASH_COMPAT
case TFUNCTION:
if (peektoken() != TWORD)
raise_error_unexpected_syntax(TWORD);
function_flag = 1;
break;
case TAND: /* "&&" */ case TAND: /* "&&" */
case TOR: /* "||" */ case TOR: /* "||" */
if (!double_brackets_flag) { if (!double_brackets_flag) {
@ -10806,6 +10819,29 @@ simplecmd(void)
app = &n->narg.next; app = &n->narg.next;
savecheckkwd = 0; 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; break;
case TREDIR: case TREDIR:
*rpp = n = redirnode; *rpp = n = redirnode;
@ -10813,6 +10849,7 @@ simplecmd(void)
parsefname(); /* read name of redirection file */ parsefname(); /* read name of redirection file */
break; break;
case TLP: case TLP:
IF_ASH_BASH_COMPAT(do_func:)
if (args && app == &args->narg.next if (args && app == &args->narg.next
&& !vars && !redir && !vars && !redir
) { ) {
@ -10820,7 +10857,7 @@ simplecmd(void)
const char *name; const char *name;
/* We have a function */ /* We have a function */
if (readtoken() != TRP) if (IF_ASH_BASH_COMPAT(!function_flag &&) readtoken() != TRP)
raise_error_unexpected_syntax(TRP); raise_error_unexpected_syntax(TRP);
name = n->narg.text; name = n->narg.text;
if (!goodname(name) if (!goodname(name)
@ -10833,6 +10870,7 @@ simplecmd(void)
n->narg.next = parse_command(); n->narg.next = parse_command();
return n; return n;
} }
IF_ASH_BASH_COMPAT(function_flag = 0;)
/* fall through */ /* fall through */
default: default:
tokpushback = 1; tokpushback = 1;
@ -11013,6 +11051,7 @@ parse_command(void)
n1 = list(0); n1 = list(0);
t = TEND; t = TEND;
break; break;
IF_ASH_BASH_COMPAT(case TFUNCTION:)
case TWORD: case TWORD:
case TREDIR: case TREDIR:
tokpushback = 1; tokpushback = 1;

View File

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

View File

@ -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