From 6e981d122287cd98a2c657db09583747b24e4fa7 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 15 Oct 2024 23:45:08 +0300 Subject: [PATCH] Shell: Add support for inline environment variables e.g. `foo=$(echo lol) Shell -c 'echo $foo'` will now print lol! --- userspace/programs/Shell/CommandTypes.h | 10 ++++-- userspace/programs/Shell/Execute.cpp | 28 ++++++++++++++++- userspace/programs/Shell/Execute.h | 11 ++++++- userspace/programs/Shell/TokenParser.cpp | 40 ++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/userspace/programs/Shell/CommandTypes.h b/userspace/programs/Shell/CommandTypes.h index 8f53aa55..17105064 100644 --- a/userspace/programs/Shell/CommandTypes.h +++ b/userspace/programs/Shell/CommandTypes.h @@ -60,9 +60,15 @@ struct CommandArgument struct SingleCommand { - BAN::ErrorOr> evaluate_arguments(Execute& execute) const; + struct EnvironmentVariable + { + COMMAND_RULE5(EnvironmentVariable, name, value); + BAN::String name; + CommandArgument value; + }; - COMMAND_RULE5(SingleCommand, arguments); + COMMAND_RULE5(SingleCommand, environment, arguments); + BAN::Vector environment; BAN::Vector arguments; }; diff --git a/userspace/programs/Shell/Execute.cpp b/userspace/programs/Shell/Execute.cpp index 4c222246..a4ee5c9a 100644 --- a/userspace/programs/Shell/Execute.cpp +++ b/userspace/programs/Shell/Execute.cpp @@ -86,6 +86,9 @@ BAN::ErrorOr Execute::execute_command_no_wait(const Inte exit(builtin_ret.value()); } + for (const auto& environment : command.environments) + setenv(environment.name.data(), environment.value.data(), true); + BAN::Vector exec_args; TRY_OR_EXIT(exec_args.reserve(command.arguments.size() + 1)); for (const auto& argument : command.arguments) @@ -119,6 +122,7 @@ BAN::ErrorOr Execute::execute_command_sync(BAN::Span arg InternalCommand command { .command = {}, .arguments = arguments, + .environments = {}, .fd_in = fd_in, .fd_out = fd_out, .background = false, @@ -168,6 +172,26 @@ BAN::ErrorOr Execute::execute_command(const PipedCommand& piped_command) BAN::Vector child_codes; TRY(child_codes.resize(piped_command.commands.size(), 126)); + const auto evaluate_arguments = + [this](BAN::Span arguments) -> BAN::ErrorOr> + { + BAN::Vector result; + TRY(result.reserve(arguments.size())); + for (const auto& argument : arguments) + TRY(result.push_back(TRY(argument.evaluate(*this)))); + return result; + }; + + const auto evaluate_environment = + [this](BAN::Span environments) -> BAN::ErrorOr> + { + BAN::Vector result; + TRY(result.reserve(environments.size())); + for (const auto& environment : environments) + TRY(result.emplace_back(environment.name, TRY(environment.value.evaluate(*this)))); + return result; + }; + for (size_t i = 0; i < piped_command.commands.size(); i++) { int new_pipe[2] { STDIN_FILENO, STDOUT_FILENO }; @@ -189,11 +213,13 @@ BAN::ErrorOr Execute::execute_command(const PipedCommand& piped_command) const int fd_in = last_pipe_rd; const int fd_out = new_pipe[1]; - auto arguments = TRY_OR_PERROR_AND_BREAK(piped_command.commands[i].evaluate_arguments(*this)); + const auto arguments = TRY_OR_PERROR_AND_BREAK(evaluate_arguments(piped_command.commands[i].arguments.span())); + const auto environments = TRY_OR_PERROR_AND_BREAK(evaluate_environment(piped_command.commands[i].environment.span())); InternalCommand command { .command = {}, .arguments = arguments.span(), + .environments = environments.span(), .fd_in = fd_in, .fd_out = fd_out, .background = piped_command.background, diff --git a/userspace/programs/Shell/Execute.h b/userspace/programs/Shell/Execute.h index 147a400e..6bec3fc1 100644 --- a/userspace/programs/Shell/Execute.h +++ b/userspace/programs/Shell/Execute.h @@ -25,14 +25,23 @@ public: private: struct InternalCommand { + using Command = BAN::Variant; + enum class Type { Builtin, External, }; - BAN::Variant command; + struct Environment + { + BAN::String name; + BAN::String value; + }; + + Command command; BAN::Span arguments; + BAN::Span environments; int fd_in; int fd_out; bool background; diff --git a/userspace/programs/Shell/TokenParser.cpp b/userspace/programs/Shell/TokenParser.cpp index ba771e67..dee3cd66 100644 --- a/userspace/programs/Shell/TokenParser.cpp +++ b/userspace/programs/Shell/TokenParser.cpp @@ -469,6 +469,43 @@ BAN::ErrorOr TokenParser::parse_single_command() { SingleCommand result; + while (peek_token().type() == Token::Type::Whitespace) + consume_token(); + + while (peek_token().type() == Token::Type::String) + { + BAN::String env_name; + + const auto& string = peek_token().string(); + if (!isalpha(string.front())) + break; + + const auto env_len = string.sv().find([](char ch) { return !(isalnum(ch) || ch == '_'); }); + if (!env_len.has_value() || string[*env_len] != '=') + break; + TRY(env_name.append(string.sv().substring(0, *env_len))); + + auto full_value = TRY(parse_argument()); + ASSERT(!full_value.parts.empty()); + ASSERT(full_value.parts.front().has()); + + auto& first_arg = full_value.parts.front().get(); + ASSERT(first_arg.value.sv().starts_with(env_name)); + ASSERT(first_arg.value[*env_len] == '='); + for (size_t i = 0; i < *env_len + 1; i++) + first_arg.value.remove(0); + if (first_arg.value.empty()) + full_value.parts.remove(0); + + SingleCommand::EnvironmentVariable environment_variable; + environment_variable.name = BAN::move(env_name); + environment_variable.value = BAN::move(full_value); + TRY(result.environment.emplace_back(BAN::move(environment_variable))); + + while (peek_token().type() == Token::Type::Whitespace) + consume_token(); + } + BAN::HashSet used_aliases; while (peek_token().type() == Token::Type::String) { @@ -519,6 +556,9 @@ BAN::ErrorOr TokenParser::parse_single_command() for (size_t i = tokenized_alias.size(); i > 0; i--) TRY(unget_token(BAN::move(tokenized_alias[i - 1]))); TRY(used_aliases.insert(TRY(BAN::String::formatted("{}", token.string())))); + + while (peek_token().type() == Token::Type::Whitespace) + consume_token(); } while (peek_token().type() != Token::Type::EOF_)