Shell: Add support for inline environment variables
e.g. `foo=$(echo lol) Shell -c 'echo $foo'` will now print lol!
This commit is contained in:
parent
8317bb13ca
commit
6e981d1222
|
@ -60,9 +60,15 @@ struct CommandArgument
|
|||
|
||||
struct SingleCommand
|
||||
{
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> 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<EnvironmentVariable> environment;
|
||||
BAN::Vector<CommandArgument> arguments;
|
||||
};
|
||||
|
||||
|
|
|
@ -86,6 +86,9 @@ BAN::ErrorOr<Execute::ExecuteResult> 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<const char*> exec_args;
|
||||
TRY_OR_EXIT(exec_args.reserve(command.arguments.size() + 1));
|
||||
for (const auto& argument : command.arguments)
|
||||
|
@ -119,6 +122,7 @@ BAN::ErrorOr<int> Execute::execute_command_sync(BAN::Span<const BAN::String> arg
|
|||
InternalCommand command {
|
||||
.command = {},
|
||||
.arguments = arguments,
|
||||
.environments = {},
|
||||
.fd_in = fd_in,
|
||||
.fd_out = fd_out,
|
||||
.background = false,
|
||||
|
@ -168,6 +172,26 @@ BAN::ErrorOr<void> Execute::execute_command(const PipedCommand& piped_command)
|
|||
BAN::Vector<int> child_codes;
|
||||
TRY(child_codes.resize(piped_command.commands.size(), 126));
|
||||
|
||||
const auto evaluate_arguments =
|
||||
[this](BAN::Span<const CommandArgument> arguments) -> BAN::ErrorOr<BAN::Vector<BAN::String>>
|
||||
{
|
||||
BAN::Vector<BAN::String> 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<const SingleCommand::EnvironmentVariable> environments) -> BAN::ErrorOr<BAN::Vector<InternalCommand::Environment>>
|
||||
{
|
||||
BAN::Vector<InternalCommand::Environment> 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<void> 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,
|
||||
|
|
|
@ -25,14 +25,23 @@ public:
|
|||
private:
|
||||
struct InternalCommand
|
||||
{
|
||||
using Command = BAN::Variant<Builtin::BuiltinCommand, BAN::String>;
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Builtin,
|
||||
External,
|
||||
};
|
||||
|
||||
BAN::Variant<Builtin::BuiltinCommand, BAN::String> command;
|
||||
struct Environment
|
||||
{
|
||||
BAN::String name;
|
||||
BAN::String value;
|
||||
};
|
||||
|
||||
Command command;
|
||||
BAN::Span<const BAN::String> arguments;
|
||||
BAN::Span<const Environment> environments;
|
||||
int fd_in;
|
||||
int fd_out;
|
||||
bool background;
|
||||
|
|
|
@ -469,6 +469,43 @@ BAN::ErrorOr<SingleCommand> 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<CommandArgument::FixedString>());
|
||||
|
||||
auto& first_arg = full_value.parts.front().get<CommandArgument::FixedString>();
|
||||
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<BAN::String> used_aliases;
|
||||
while (peek_token().type() == Token::Type::String)
|
||||
{
|
||||
|
@ -519,6 +556,9 @@ BAN::ErrorOr<SingleCommand> 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_)
|
||||
|
|
Loading…
Reference in New Issue