hush: return builtin by Bayram Kurumahmut (kbayram AT ubicom.com)
~+200 bytes
This commit is contained in:
parent
3be2308676
commit
3d40d8e655
93
shell/hush.c
93
shell/hush.c
@ -443,6 +443,13 @@ struct globals {
|
|||||||
smallint flag_SIGINT;
|
smallint flag_SIGINT;
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
smallint flag_break_continue;
|
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
|
#endif
|
||||||
smallint fake_mode;
|
smallint fake_mode;
|
||||||
smallint exiting; /* used to prevent EXIT trap recursion */
|
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_break(char **argv);
|
||||||
static int builtin_continue(char **argv);
|
static int builtin_continue(char **argv);
|
||||||
#endif
|
#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
|
/* Table of built-in functions. They can be forked or not, depending on
|
||||||
* context: within pipes, they fork. As simple commands, they do not.
|
* context: within pipes, they fork. As simple commands, they do not.
|
||||||
@ -575,7 +585,9 @@ static const struct built_in_command bltins[] = {
|
|||||||
#endif
|
#endif
|
||||||
BLTIN("pwd" , builtin_pwd , "Print current directory"),
|
BLTIN("pwd" , builtin_pwd , "Print current directory"),
|
||||||
BLTIN("read" , builtin_read , "Input environment variable"),
|
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("set" , builtin_set , "Set/unset shell local variables"),
|
||||||
BLTIN("shift" , builtin_shift , "Shift positional parameters"),
|
BLTIN("shift" , builtin_shift , "Shift positional parameters"),
|
||||||
BLTIN("test" , builtin_test , "Test condition"),
|
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)
|
static int run_function(const struct function *funcp, char **argv)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
smallint sv_flg;
|
||||||
save_arg_t sv;
|
save_arg_t sv;
|
||||||
|
|
||||||
save_and_replace_G_args(&sv, argv);
|
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 */
|
/* On MMU, funcp->body is always non-NULL */
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
if (!funcp->body) {
|
if (!funcp->body) {
|
||||||
@ -2863,6 +2880,8 @@ static int run_function(const struct function *funcp, char **argv)
|
|||||||
{
|
{
|
||||||
rc = run_list(funcp->body);
|
rc = run_list(funcp->body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
G.flag_return_in_progress = sv_flg;
|
||||||
restore_G_args(&sv, argv);
|
restore_G_args(&sv, argv);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -3886,7 +3905,8 @@ static int run_list(struct pipe *pi)
|
|||||||
#endif
|
#endif
|
||||||
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
|
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
|
||||||
if (r != -1) {
|
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. */
|
* and we don't need to wait for anything. */
|
||||||
G.last_exitcode = rcode;
|
G.last_exitcode = rcode;
|
||||||
debug_printf_exec(": builtin/func exitcode %d\n", rcode);
|
debug_printf_exec(": builtin/func exitcode %d\n", rcode);
|
||||||
@ -3909,6 +3929,10 @@ static int run_list(struct pipe *pi)
|
|||||||
rword = RES_DONE;
|
rword = RES_DONE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if ENABLE_HUSH_FUNCTIONS
|
||||||
|
if (G.flag_return_in_progress == 1)
|
||||||
|
goto check_jobs_and_break;
|
||||||
#endif
|
#endif
|
||||||
} else if (pi->followup == PIPE_BG) {
|
} else if (pi->followup == PIPE_BG) {
|
||||||
/* What does bash do with attempts to background builtins? */
|
/* 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)
|
static int builtin_source(char **argv)
|
||||||
{
|
{
|
||||||
FILE *input;
|
FILE *input;
|
||||||
|
smallint sv_flg;
|
||||||
save_arg_t sv;
|
save_arg_t sv;
|
||||||
|
|
||||||
if (*++argv == NULL)
|
if (*++argv == NULL)
|
||||||
@ -6688,12 +6713,17 @@ static int builtin_source(char **argv)
|
|||||||
}
|
}
|
||||||
close_on_exec_on(fileno(input));
|
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);
|
save_and_replace_G_args(&sv, argv);
|
||||||
|
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
restore_G_args(&sv, argv);
|
|
||||||
fclose(input);
|
fclose(input);
|
||||||
|
|
||||||
|
restore_G_args(&sv, argv);
|
||||||
|
G.flag_return_in_progress = sv_flg;
|
||||||
|
|
||||||
return G.last_exitcode;
|
return G.last_exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6833,25 +6863,36 @@ static int builtin_wait(char **argv)
|
|||||||
return ret;
|
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
|
#if ENABLE_HUSH_LOOPS
|
||||||
static int builtin_break(char **argv)
|
static int builtin_break(char **argv)
|
||||||
{
|
{
|
||||||
|
unsigned depth;
|
||||||
if (G.depth_of_loop == 0) {
|
if (G.depth_of_loop == 0) {
|
||||||
bb_error_msg("%s: only meaningful in a loop", argv[0]);
|
bb_error_msg("%s: only meaningful in a loop", argv[0]);
|
||||||
return EXIT_SUCCESS; /* bash compat */
|
return EXIT_SUCCESS; /* bash compat */
|
||||||
}
|
}
|
||||||
G.flag_break_continue++; /* BC_BREAK = 1 */
|
G.flag_break_continue++; /* BC_BREAK = 1 */
|
||||||
G.depth_break_continue = 1;
|
|
||||||
if (argv[1]) {
|
G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
|
||||||
G.depth_break_continue = bb_strtou(argv[1], NULL, 10);
|
if (depth == UINT_MAX)
|
||||||
if (errno || !G.depth_break_continue || argv[2]) {
|
G.flag_break_continue = BC_BREAK;
|
||||||
bb_error_msg("%s: bad arguments", argv[0]);
|
if (G.depth_of_loop < depth)
|
||||||
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 = G.depth_of_loop;
|
G.depth_break_continue = G.depth_of_loop;
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6861,3 +6902,27 @@ static int builtin_continue(char **argv)
|
|||||||
return builtin_break(argv);
|
return builtin_break(argv);
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
||||||
|
4
shell/hush_test/hush-misc/func3.right
Normal file
4
shell/hush_test/hush-misc/func3.right
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
One:1
|
||||||
|
Zero:0
|
||||||
|
One:1
|
||||||
|
Five:5
|
8
shell/hush_test/hush-misc/func3.tests
Executable file
8
shell/hush_test/hush-misc/func3.tests
Executable 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:$?
|
Loading…
Reference in New Issue
Block a user