diff options
-rw-r--r-- | shell/hush.c | 93 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/func3.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/func3.tests | 8 |
3 files changed, 91 insertions, 14 deletions
diff --git a/shell/hush.c b/shell/hush.c index 84364e0e7..82634ff83 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -444,6 +444,13 @@ struct globals { #if ENABLE_HUSH_LOOPS smallint flag_break_continue; #endif +#if ENABLE_HUSH_FUNCTIONS + /* 0: outside of a function (or sourced file) + * -1: inside of a function, ok to use return builtin + * 1: return is invoked, skip all till end of func. + */ + smallint flag_return_in_progress; +#endif smallint fake_mode; smallint exiting; /* used to prevent EXIT trap recursion */ /* These four support $?, $#, and $1 */ @@ -522,6 +529,9 @@ static int builtin_wait(char **argv); static int builtin_break(char **argv); static int builtin_continue(char **argv); #endif +#if ENABLE_HUSH_FUNCTIONS +static int builtin_return(char **argv); +#endif /* Table of built-in functions. They can be forked or not, depending on * context: within pipes, they fork. As simple commands, they do not. @@ -575,7 +585,9 @@ static const struct built_in_command bltins[] = { #endif BLTIN("pwd" , builtin_pwd , "Print current directory"), BLTIN("read" , builtin_read , "Input environment variable"), -// BLTIN("return" , builtin_return , "Return from a function"), +#if ENABLE_HUSH_FUNCTIONS + BLTIN("return" , builtin_return , "Return from a function"), +#endif BLTIN("set" , builtin_set , "Set/unset shell local variables"), BLTIN("shift" , builtin_shift , "Shift positional parameters"), BLTIN("test" , builtin_test , "Test condition"), @@ -2849,9 +2861,14 @@ static void exec_function(nommu_save_t *nommu_save, static int run_function(const struct function *funcp, char **argv) { int rc; + smallint sv_flg; save_arg_t sv; save_and_replace_G_args(&sv, argv); + /* "we are in function, ok to use return" */ + sv_flg = G.flag_return_in_progress; + G.flag_return_in_progress = -1; + /* On MMU, funcp->body is always non-NULL */ #if !BB_MMU if (!funcp->body) { @@ -2863,6 +2880,8 @@ static int run_function(const struct function *funcp, char **argv) { rc = run_list(funcp->body); } + + G.flag_return_in_progress = sv_flg; restore_G_args(&sv, argv); return rc; @@ -3886,7 +3905,8 @@ static int run_list(struct pipe *pi) #endif rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ if (r != -1) { - /* We only ran a builtin: rcode is already known + /* We ran a builtin, function, or group. + * rcode is already known * and we don't need to wait for anything. */ G.last_exitcode = rcode; debug_printf_exec(": builtin/func exitcode %d\n", rcode); @@ -3910,6 +3930,10 @@ static int run_list(struct pipe *pi) continue; } #endif +#if ENABLE_HUSH_FUNCTIONS + if (G.flag_return_in_progress == 1) + goto check_jobs_and_break; +#endif } else if (pi->followup == PIPE_BG) { /* What does bash do with attempts to background builtins? */ /* even bash 3.2 doesn't do that well with nested bg: @@ -6675,6 +6699,7 @@ static int builtin_shift(char **argv) static int builtin_source(char **argv) { FILE *input; + smallint sv_flg; save_arg_t sv; if (*++argv == NULL) @@ -6688,12 +6713,17 @@ static int builtin_source(char **argv) } close_on_exec_on(fileno(input)); - /* Now run the file */ + sv_flg = G.flag_return_in_progress; + /* "we are inside sourced file, ok to use return" */ + G.flag_return_in_progress = -1; save_and_replace_G_args(&sv, argv); + parse_and_run_file(input); - restore_G_args(&sv, argv); fclose(input); + restore_G_args(&sv, argv); + G.flag_return_in_progress = sv_flg; + return G.last_exitcode; } @@ -6833,25 +6863,36 @@ static int builtin_wait(char **argv) return ret; } +#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS +static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min) +{ + if (argv[1]) { + def = bb_strtou(argv[1], NULL, 10); + if (errno || def < def_min || argv[2]) { + bb_error_msg("%s: bad arguments", argv[0]); + def = UINT_MAX; + } + } + return def; +} +#endif + #if ENABLE_HUSH_LOOPS static int builtin_break(char **argv) { + unsigned depth; if (G.depth_of_loop == 0) { bb_error_msg("%s: only meaningful in a loop", argv[0]); return EXIT_SUCCESS; /* bash compat */ } G.flag_break_continue++; /* BC_BREAK = 1 */ - G.depth_break_continue = 1; - if (argv[1]) { - G.depth_break_continue = bb_strtou(argv[1], NULL, 10); - if (errno || !G.depth_break_continue || argv[2]) { - bb_error_msg("%s: bad arguments", argv[0]); - G.flag_break_continue = BC_BREAK; - G.depth_break_continue = UINT_MAX; - } - } - if (G.depth_of_loop < G.depth_break_continue) + + G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1); + if (depth == UINT_MAX) + G.flag_break_continue = BC_BREAK; + if (G.depth_of_loop < depth) G.depth_break_continue = G.depth_of_loop; + return EXIT_SUCCESS; } @@ -6861,3 +6902,27 @@ static int builtin_continue(char **argv) return builtin_break(argv); } #endif + +#if ENABLE_HUSH_FUNCTIONS +static int builtin_return(char **argv UNUSED_PARAM) +{ + int rc; + + if (G.flag_return_in_progress != -1) { + bb_error_msg("%s: not in a function or sourced script", argv[0]); + return EXIT_FAILURE; /* bash compat */ + } + + G.flag_return_in_progress = 1; + + /* bash: + * out of range: wraps around at 256, does not error out + * non-numeric param: + * f() { false; return qwe; }; f; echo $? + * bash: return: qwe: numeric argument required <== we do this + * 255 <== we also do this + */ + rc = parse_numeric_argv1(argv, G.last_exitcode, 0); + return rc; +} +#endif diff --git a/shell/hush_test/hush-misc/func3.right b/shell/hush_test/hush-misc/func3.right new file mode 100644 index 000000000..b6d73459a --- /dev/null +++ b/shell/hush_test/hush-misc/func3.right @@ -0,0 +1,4 @@ +One:1 +Zero:0 +One:1 +Five:5 diff --git a/shell/hush_test/hush-misc/func3.tests b/shell/hush_test/hush-misc/func3.tests new file mode 100755 index 000000000..fa6f26a23 --- /dev/null +++ b/shell/hush_test/hush-misc/func3.tests @@ -0,0 +1,8 @@ +f() { false; return; echo BAD; }; +{ f; echo One:$?; }; echo Zero:$? + +f() { false; return; }; +f; echo One:$? + +f() { return 5; }; +f; echo Five:$? |