hush: return builtin by Bayram Kurumahmut (kbayram AT ubicom.com)

~+200 bytes
This commit is contained in:
Denis Vlasenko 2009-04-17 23:44:18 +00:00
parent 3be2308676
commit 3d40d8e655
3 changed files with 91 additions and 14 deletions

View File

@ -443,6 +443,13 @@ struct globals {
smallint flag_SIGINT;
#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 */
@ -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);
@ -3909,6 +3929,10 @@ static int run_list(struct pipe *pi)
rword = RES_DONE;
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? */
@ -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.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
if (depth == UINT_MAX)
G.flag_break_continue = BC_BREAK;
G.depth_break_continue = UINT_MAX;
}
}
if (G.depth_of_loop < G.depth_break_continue)
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

View File

@ -0,0 +1,4 @@
One:1
Zero:0
One:1
Five:5

View File

@ -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:$?