hush: pass functions to child shells on NOMMU

function                                             old     new   delta
new_function                                           -     123    +123
hush_main                                           1198    1262     +64
re_execute_shell                                     341     387     +46
run_pipe                                            1872    1790     -82
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/1 up/down: 233/-82)           Total: 151 bytes

diff --git a/shell/hush.c b/shell/hush.c
index 16a4d73..9c2c0f3 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -2322,6 +2322,9 @@
 	char param_buf[sizeof("-$%x:%x:%x:%x") + sizeof(unsigned) * 4];
 	char *heredoc_argv[4];
 	struct variable *cur;
+#if ENABLE_HUSH_FUNCTIONS
+	struct function *funcp;
+#endif
 	char **argv, **pp;
 	unsigned cnt;
 
@@ -2341,7 +2344,7 @@
 			, (unsigned) G.last_exitcode
 			USE_HUSH_LOOPS(, G.depth_of_loop)
 			);
-	/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...>
+	/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> <funcs...>
 	 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
 	 */
 	cnt = 6;
@@ -2349,6 +2352,10 @@
 		if (!cur->flg_export || cur->flg_read_only)
 			cnt += 2;
 	}
+#if ENABLE_HUSH_FUNCTIONS
+	for (funcp = G.top_func; funcp; funcp = funcp->next)
+		cnt += 3;
+#endif
 	pp = g_argv;
 	while (*pp++)
 		cnt++;
@@ -2366,7 +2373,13 @@
 			*pp++ = cur->varstr;
 		}
 	}
-//TODO: pass functions
+#if ENABLE_HUSH_FUNCTIONS
+	for (funcp = G.top_func; funcp; funcp = funcp->next) {
+		*pp++ = (char *) "-F";
+		*pp++ = funcp->name;
+		*pp++ = funcp->body_as_string;
+	}
+#endif
 	/* We can pass activated traps here. Say, -Tnn:trap_string
 	 *
 	 * However, POSIX says that subshells reset signals with traps
@@ -2649,6 +2662,15 @@
 
 
 static int run_list(struct pipe *pi);
+#if BB_MMU
+#define parse_stream(pstring, input, end_trigger) \
+	parse_stream(input, end_trigger)
+#endif
+static struct pipe *parse_stream(char **pstring,
+		struct in_str *input,
+		int end_trigger);
+static void parse_and_run_string(const char *s);
+
 
 static const struct built_in_command* find_builtin(const char *name)
 {
@@ -2676,6 +2698,52 @@
 	return funcp;
 }
 
+/* Note: takes ownership on name ptr */
+static struct function *new_function(char *name)
+{
+	struct function *funcp;
+	struct function **funcpp = &G.top_func;
+
+	while ((funcp = *funcpp) != NULL) {
+		struct command *cmd;
+
+		if (strcmp(funcp->name, name) != 0) {
+			funcpp = &funcp->next;
+			continue;
+		}
+
+		cmd = funcp->parent_cmd;
+		debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
+		if (!cmd) {
+			debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
+			free(funcp->name);
+			/* Note: if !funcp->body, do not free body_as_string!
+			 * This is a special case of "-F name body" function:
+			 * body_as_string was not malloced! */
+			if (funcp->body) {
+				free_pipe_list(funcp->body);
+#if !BB_MMU
+				free(funcp->body_as_string);
+#endif
+			}
+		} else {
+			debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
+			cmd->argv[0] = funcp->name;
+			cmd->group = funcp->body;
+#if !BB_MMU
+			cmd->group_as_string = funcp->body_as_string;
+#endif
+		}
+		goto skip;
+	}
+	debug_printf_exec("remembering new function '%s'\n", command->argv[0]);
+	funcp = *funcpp = xzalloc(sizeof(*funcp));
+	/*funcp->next = NULL;*/
+ skip:
+	funcp->name = name;
+	return funcp;
+}
+
 static void exec_function(const struct function *funcp, char **argv) NORETURN;
 static void exec_function(const struct function *funcp, char **argv)
 {
@@ -2687,6 +2755,7 @@
 	while (*++argv)
 		n++;
 	G.global_argc = n;
+	/* On MMU, funcp->body is always non-NULL */
 	n = run_list(funcp->body);
 	fflush(NULL);
 	_exit(n);
@@ -2719,7 +2788,17 @@
 	G.global_argc = n;
 	G.global_argv = argv;
 
-	n = run_list(funcp->body);
+	/* On MMU, funcp->body is always non-NULL */
+#if !BB_MMU
+	if (!funcp->body) {
+		/* Function defined by -F */
+		parse_and_run_string(funcp->body_as_string);
+		n = G.last_exitcode;
+	} else
+#endif
+	{
+		n = run_list(funcp->body);
+	}
 
 	if (G.global_args_malloced) {
 		/* function ran "set -- arg1 arg2 ..." */
@@ -3201,37 +3280,9 @@
 		if (command->grp_type == GRP_FUNCTION) {
 			/* "executing" func () { list } */
 			struct function *funcp;
-			struct function **funcpp = &G.top_func;
 
-			while ((funcp = *funcpp) != NULL) {
-				if (strcmp(funcp->name, command->argv[0]) == 0) {
-					struct command *cmd = funcp->parent_cmd;
-
-					debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
-					if (!cmd) {
-						debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
-						free(funcp->name);
-						free_pipe_list(funcp->body);
-#if !BB_MMU
-						free(funcp->body_as_string);
-#endif
-					} else {
-						debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
-						cmd->argv[0] = funcp->name;
-						cmd->group = funcp->body;
-#if !BB_MMU
-						cmd->group_as_string = funcp->body_as_string;
-#endif
-					}
-					goto skip;
-				}
-				funcpp = &funcp->next;
-			}
-			debug_printf_exec("remembering new function '%s'\n", command->argv[0]);
-			funcp = *funcpp = xzalloc(sizeof(*funcp));
-			/*funcp->next = NULL;*/
- skip:
-			funcp->name = command->argv[0];
+			funcp = new_function(command->argv[0]);
+			/* funcp->name is already set to argv[0] */
 			funcp->body = command->group;
 #if !BB_MMU
 			funcp->body_as_string = command->group_as_string;
@@ -4599,15 +4650,6 @@
 }
 
 
-#if BB_MMU
-#define parse_stream(pstring, input, end_trigger) \
-	parse_stream(input, end_trigger)
-#endif
-static struct pipe *parse_stream(char **pstring,
-		struct in_str *input,
-		int end_trigger);
-static void parse_and_run_string(const char *s);
-
 #if ENABLE_HUSH_TICK
 static FILE *generate_stream_from_string(const char *s)
 {
@@ -5866,7 +5908,10 @@
 	while (1) {
 		opt = getopt(argc, argv, "c:xins"
 #if !BB_MMU
-				"<:$:!:?:D:R:V:"
+				"<:$:R:V:"
+# if ENABLE_HUSH_FUNCTIONS
+				"F:"
+# endif
 #endif
 		);
 		if (opt <= 0)
@@ -5913,6 +5958,16 @@
 		case 'V':
 			set_local_var(xstrdup(optarg), 0, opt == 'R');
 			break;
+# if ENABLE_HUSH_FUNCTIONS
+		case 'F': {
+			struct function *funcp = new_function(optarg);
+			/* funcp->name is already set to optarg */
+			/* funcp->body is set to NULL. It's a special case. */
+			funcp->body_as_string = argv[optind];
+			optind++;
+			break;
+		}
+# endif
 #endif
 		case 'n':
 		case 'x':