[git commit] ash: add support for bash 'function' keyword

Denys Vlasenko vda.linux at googlemail.com
Wed Nov 4 18:30:24 UTC 2015


commit: http://git.busybox.net/busybox/commit/?id=95ebcf79ff6f8ad21ceacb7bac665fb86c078d84
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 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(-)

diff --git a/shell/ash.c b/shell/ash.c
index 8450263..e7a867f 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 0000000..41bf882
--- /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 0000000..2cc0970
--- /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


More information about the busybox-cvs mailing list