diff --git a/Userland/Shell/AST.cpp b/Userland/Shell/AST.cpp index 5f978dbe72..7b368e44dc 100644 --- a/Userland/Shell/AST.cpp +++ b/Userland/Shell/AST.cpp @@ -1230,7 +1230,7 @@ ErrorOr> ForLoop::run(RefPtr shell) size_t consecutive_interruptions = 0; auto run = [&](auto& block_value) { - if (shell->has_error(Shell::ShellError::InternalControlFlowBreak)) { + if (shell->has_error(Shell::ShellError::InternalControlFlowBreak) || shell->has_error(Shell::ShellError::InternalControlFlowReturn)) { shell->take_error(); return IterationDecision::Break; } diff --git a/Userland/Shell/Builtin.cpp b/Userland/Shell/Builtin.cpp index e148050b08..58d4705295 100644 --- a/Userland/Shell/Builtin.cpp +++ b/Userland/Shell/Builtin.cpp @@ -268,6 +268,23 @@ ErrorOr Shell::builtin_continue(Main::Arguments arguments) return 0; } +ErrorOr Shell::builtin_return(Main::Arguments arguments) +{ + int return_code = last_return_code.value_or(0); + + Core::ArgsParser parser; + parser.add_positional_argument(return_code, "Return code to return to the parent shell", "return-code", Core::ArgsParser::Required::No); + parser.set_general_help("Return from a function or source file"); + + if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage)) + return 1; + + last_return_code = return_code & 0xff; + raise_error(ShellError::InternalControlFlowReturn, "POSIX return"); + + return 0; +} + ErrorOr Shell::builtin_bg(Main::Arguments arguments) { int job_id = -1; diff --git a/Userland/Shell/Shell.cpp b/Userland/Shell/Shell.cpp index 0370e234ef..fe1af01ccd 100644 --- a/Userland/Shell/Shell.cpp +++ b/Userland/Shell/Shell.cpp @@ -487,6 +487,9 @@ bool Shell::invoke_function(const AST::Command& command, int& retval) (void)function.body->run(*this); + if (has_error(ShellError::InternalControlFlowReturn)) + take_error(); + retval = last_return_code.value_or(0); return true; } @@ -2435,6 +2438,7 @@ void Shell::possibly_print_error() const break; case ShellError::InternalControlFlowBreak: case ShellError::InternalControlFlowContinue: + case ShellError::InternalControlFlowReturn: case ShellError::InternalControlFlowInterrupted: case ShellError::InternalControlFlowKilled: return; diff --git a/Userland/Shell/Shell.h b/Userland/Shell/Shell.h index 2779446625..f322437469 100644 --- a/Userland/Shell/Shell.h +++ b/Userland/Shell/Shell.h @@ -60,6 +60,7 @@ __ENUMERATE_SHELL_BUILTIN(noop, InAllModes) \ __ENUMERATE_SHELL_BUILTIN(break, OnlyInPOSIXMode) \ __ENUMERATE_SHELL_BUILTIN(continue, OnlyInPOSIXMode) \ + __ENUMERATE_SHELL_BUILTIN(return, InAllModes) \ __ENUMERATE_SHELL_BUILTIN(read, OnlyInPOSIXMode) \ __ENUMERATE_SHELL_BUILTIN(run_with_env, OnlyInPOSIXMode) \ __ENUMERATE_SHELL_BUILTIN(argsparser_parse, InAllModes) \ @@ -360,6 +361,7 @@ public: None, InternalControlFlowBreak, InternalControlFlowContinue, + InternalControlFlowReturn, InternalControlFlowInterrupted, InternalControlFlowKilled, EvaluatedSyntaxError, @@ -396,6 +398,7 @@ public: switch (error) { case ShellError::InternalControlFlowBreak: case ShellError::InternalControlFlowContinue: + case ShellError::InternalControlFlowReturn: case ShellError::InternalControlFlowInterrupted: case ShellError::InternalControlFlowKilled: return true;