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
|
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;
|
BAN::Vector<CommandArgument> arguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,9 @@ BAN::ErrorOr<Execute::ExecuteResult> Execute::execute_command_no_wait(const Inte
|
||||||
exit(builtin_ret.value());
|
exit(builtin_ret.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& environment : command.environments)
|
||||||
|
setenv(environment.name.data(), environment.value.data(), true);
|
||||||
|
|
||||||
BAN::Vector<const char*> exec_args;
|
BAN::Vector<const char*> exec_args;
|
||||||
TRY_OR_EXIT(exec_args.reserve(command.arguments.size() + 1));
|
TRY_OR_EXIT(exec_args.reserve(command.arguments.size() + 1));
|
||||||
for (const auto& argument : command.arguments)
|
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 {
|
InternalCommand command {
|
||||||
.command = {},
|
.command = {},
|
||||||
.arguments = arguments,
|
.arguments = arguments,
|
||||||
|
.environments = {},
|
||||||
.fd_in = fd_in,
|
.fd_in = fd_in,
|
||||||
.fd_out = fd_out,
|
.fd_out = fd_out,
|
||||||
.background = false,
|
.background = false,
|
||||||
|
@ -168,6 +172,26 @@ BAN::ErrorOr<void> Execute::execute_command(const PipedCommand& piped_command)
|
||||||
BAN::Vector<int> child_codes;
|
BAN::Vector<int> child_codes;
|
||||||
TRY(child_codes.resize(piped_command.commands.size(), 126));
|
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++)
|
for (size_t i = 0; i < piped_command.commands.size(); i++)
|
||||||
{
|
{
|
||||||
int new_pipe[2] { STDIN_FILENO, STDOUT_FILENO };
|
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_in = last_pipe_rd;
|
||||||
const int fd_out = new_pipe[1];
|
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 {
|
InternalCommand command {
|
||||||
.command = {},
|
.command = {},
|
||||||
.arguments = arguments.span(),
|
.arguments = arguments.span(),
|
||||||
|
.environments = environments.span(),
|
||||||
.fd_in = fd_in,
|
.fd_in = fd_in,
|
||||||
.fd_out = fd_out,
|
.fd_out = fd_out,
|
||||||
.background = piped_command.background,
|
.background = piped_command.background,
|
||||||
|
|
|
@ -25,14 +25,23 @@ public:
|
||||||
private:
|
private:
|
||||||
struct InternalCommand
|
struct InternalCommand
|
||||||
{
|
{
|
||||||
|
using Command = BAN::Variant<Builtin::BuiltinCommand, BAN::String>;
|
||||||
|
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
Builtin,
|
Builtin,
|
||||||
External,
|
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 BAN::String> arguments;
|
||||||
|
BAN::Span<const Environment> environments;
|
||||||
int fd_in;
|
int fd_in;
|
||||||
int fd_out;
|
int fd_out;
|
||||||
bool background;
|
bool background;
|
||||||
|
|
|
@ -469,6 +469,43 @@ BAN::ErrorOr<SingleCommand> TokenParser::parse_single_command()
|
||||||
{
|
{
|
||||||
SingleCommand result;
|
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;
|
BAN::HashSet<BAN::String> used_aliases;
|
||||||
while (peek_token().type() == Token::Type::String)
|
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--)
|
for (size_t i = tokenized_alias.size(); i > 0; i--)
|
||||||
TRY(unget_token(BAN::move(tokenized_alias[i - 1])));
|
TRY(unget_token(BAN::move(tokenized_alias[i - 1])));
|
||||||
TRY(used_aliases.insert(TRY(BAN::String::formatted("{}", token.string()))));
|
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_)
|
while (peek_token().type() != Token::Type::EOF_)
|
||||||
|
|
Loading…
Reference in New Issue