Shell: Implement redirections

This works well enough. At the moment redirections can appear anywhere
in the command **after** environment variables and possible alias
command. This means commands like `>foo echo hello` are not supported.
I don't think this is big enough problem for now (99% time redirections
are given after arguments) that i would rewrite the environment and
alias parsing.
This commit is contained in:
2025-07-22 16:54:06 +03:00
parent 05affda20c
commit c1978f9133
8 changed files with 212 additions and 9 deletions

View File

@@ -99,6 +99,47 @@ BAN::ErrorOr<Execute::ExecuteResult> Execute::execute_command_no_wait(const Inte
CHECK_FD_OR_PERROR_AND_EXIT(command.fd_in, STDIN_FILENO);
CHECK_FD_OR_PERROR_AND_EXIT(command.fd_out, STDOUT_FILENO);
for (const auto& redirection : command.redirections)
{
int dst_fd = -1;
if (redirection.duplicate)
{
if (!redirection.path.empty())
{
dst_fd = 0;
for (char ch : redirection.path)
{
if (!isdigit(ch))
{
dst_fd = -1;
break;
}
dst_fd = (dst_fd * 10) + (ch - '0');
}
}
}
else
{
const int flags = O_CREAT
| (redirection.input ? O_RDONLY : O_WRONLY)
| (redirection.append ? O_APPEND : O_TRUNC);
dst_fd = open(redirection.path.data(), flags, 0644);
if (dst_fd == -1)
{
perror("open");
exit(errno);
}
}
CHECK_FD_OR_PERROR_AND_EXIT(dst_fd, redirection.source_fd);
if (!redirection.duplicate)
close(dst_fd);
}
execv(command.command.get<BAN::String>().data(), const_cast<char* const*>(exec_args.data()));
perror("execv");
exit(errno);
@@ -125,6 +166,7 @@ BAN::ErrorOr<int> Execute::execute_command_sync(BAN::Span<const BAN::String> arg
.command = {},
.arguments = arguments,
.environments = {},
.redirections = {},
.fd_in = fd_in,
.fd_out = fd_out,
.background = false,
@@ -194,6 +236,22 @@ BAN::ErrorOr<void> Execute::execute_command(const PipedCommand& piped_command)
return result;
};
const auto evaluate_redirections =
[this](BAN::Span<const SingleCommand::Redirection> redirections) -> BAN::ErrorOr<BAN::Vector<InternalCommand::Redirection>>
{
BAN::Vector<InternalCommand::Redirection> result;
TRY(result.reserve(redirections.size()));
for (const auto& redirection : redirections)
TRY(result.push_back({
.path = TRY(redirection.destination.evaluate(*this)),
.source_fd = redirection.source_fd,
.append = redirection.append,
.duplicate = redirection.duplicate,
.input = redirection.input,
}));
return result;
};
const int stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
if (stdin_flags == -1)
perror("fcntl");
@@ -221,11 +279,13 @@ BAN::ErrorOr<void> Execute::execute_command(const PipedCommand& piped_command)
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()));
const auto redirections = TRY_OR_PERROR_AND_BREAK(evaluate_redirections(piped_command.commands[i].redirections.span()));
InternalCommand command {
.command = {},
.arguments = arguments.span(),
.environments = environments.span(),
.redirections = redirections.span(),
.fd_in = fd_in,
.fd_out = fd_out,
.background = piped_command.background,