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:
parent
05affda20c
commit
c1978f9133
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
|
|
||||||
#define COMMAND_GET_MACRO(_0, _1, _2, NAME, ...) NAME
|
#define COMMAND_GET_MACRO(_0, _1, _2, _3, NAME, ...) NAME
|
||||||
|
|
||||||
#define COMMAND_MOVE_0(class) \
|
#define COMMAND_MOVE_0(class) \
|
||||||
class(class&& o) { } \
|
class(class&& o) { } \
|
||||||
|
@ -13,7 +13,10 @@
|
||||||
#define COMMAND_MOVE_2(class, var1, var2) \
|
#define COMMAND_MOVE_2(class, var1, var2) \
|
||||||
class(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); } \
|
class(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); } \
|
||||||
class& operator=(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); return *this; }
|
class& operator=(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); return *this; }
|
||||||
#define COMMAND_MOVE(class, ...) COMMAND_GET_MACRO(_0 __VA_OPT__(,) __VA_ARGS__, COMMAND_MOVE_2, COMMAND_MOVE_1, COMMAND_MOVE_0)(class, __VA_ARGS__)
|
#define COMMAND_MOVE_3(class, var1, var2, var3) \
|
||||||
|
class(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); var3 = BAN::move(o.var3); } \
|
||||||
|
class& operator=(class&& o) { var1 = BAN::move(o.var1); var2 = BAN::move(o.var2); var3 = BAN::move(o.var3); return *this; }
|
||||||
|
#define COMMAND_MOVE(class, ...) COMMAND_GET_MACRO(_0 __VA_OPT__(,) __VA_ARGS__, COMMAND_MOVE_3, COMMAND_MOVE_2, COMMAND_MOVE_1, COMMAND_MOVE_0)(class, __VA_ARGS__)
|
||||||
|
|
||||||
#define COMMAND_RULE5(class, ...) \
|
#define COMMAND_RULE5(class, ...) \
|
||||||
class() = default; \
|
class() = default; \
|
||||||
|
@ -67,9 +70,19 @@ struct SingleCommand
|
||||||
CommandArgument value;
|
CommandArgument value;
|
||||||
};
|
};
|
||||||
|
|
||||||
COMMAND_RULE5(SingleCommand, environment, arguments);
|
struct Redirection
|
||||||
|
{
|
||||||
|
CommandArgument destination;
|
||||||
|
int source_fd;
|
||||||
|
bool append;
|
||||||
|
bool duplicate;
|
||||||
|
bool input;
|
||||||
|
};
|
||||||
|
|
||||||
|
COMMAND_RULE5(SingleCommand, environment, arguments, redirections);
|
||||||
BAN::Vector<EnvironmentVariable> environment;
|
BAN::Vector<EnvironmentVariable> environment;
|
||||||
BAN::Vector<CommandArgument> arguments;
|
BAN::Vector<CommandArgument> arguments;
|
||||||
|
BAN::Vector<Redirection> redirections;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PipedCommand
|
struct PipedCommand
|
||||||
|
|
|
@ -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_in, STDIN_FILENO);
|
||||||
CHECK_FD_OR_PERROR_AND_EXIT(command.fd_out, STDOUT_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()));
|
execv(command.command.get<BAN::String>().data(), const_cast<char* const*>(exec_args.data()));
|
||||||
perror("execv");
|
perror("execv");
|
||||||
exit(errno);
|
exit(errno);
|
||||||
|
@ -125,6 +166,7 @@ BAN::ErrorOr<int> Execute::execute_command_sync(BAN::Span<const BAN::String> arg
|
||||||
.command = {},
|
.command = {},
|
||||||
.arguments = arguments,
|
.arguments = arguments,
|
||||||
.environments = {},
|
.environments = {},
|
||||||
|
.redirections = {},
|
||||||
.fd_in = fd_in,
|
.fd_in = fd_in,
|
||||||
.fd_out = fd_out,
|
.fd_out = fd_out,
|
||||||
.background = false,
|
.background = false,
|
||||||
|
@ -194,6 +236,22 @@ BAN::ErrorOr<void> Execute::execute_command(const PipedCommand& piped_command)
|
||||||
return result;
|
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);
|
const int stdin_flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
if (stdin_flags == -1)
|
if (stdin_flags == -1)
|
||||||
perror("fcntl");
|
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 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 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 {
|
InternalCommand command {
|
||||||
.command = {},
|
.command = {},
|
||||||
.arguments = arguments.span(),
|
.arguments = arguments.span(),
|
||||||
.environments = environments.span(),
|
.environments = environments.span(),
|
||||||
|
.redirections = redirections.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,
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Execute
|
||||||
public:
|
public:
|
||||||
Execute() = default;
|
Execute() = default;
|
||||||
|
|
||||||
|
// FIXME: remove this, this is only used by *builtin* time which should not be a builtin
|
||||||
BAN::ErrorOr<int> execute_command_sync(BAN::Span<const BAN::String> arguments, int fd_in, int fd_out);
|
BAN::ErrorOr<int> execute_command_sync(BAN::Span<const BAN::String> arguments, int fd_in, int fd_out);
|
||||||
BAN::ErrorOr<void> execute_command(const SingleCommand&, int fd_in, int fd_out, bool background, pid_t pgrp = 0);
|
BAN::ErrorOr<void> execute_command(const SingleCommand&, int fd_in, int fd_out, bool background, pid_t pgrp = 0);
|
||||||
BAN::ErrorOr<void> execute_command(const PipedCommand&);
|
BAN::ErrorOr<void> execute_command(const PipedCommand&);
|
||||||
|
@ -39,9 +40,19 @@ private:
|
||||||
BAN::String value;
|
BAN::String value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Redirection
|
||||||
|
{
|
||||||
|
BAN::String path;
|
||||||
|
int source_fd;
|
||||||
|
bool append;
|
||||||
|
bool duplicate;
|
||||||
|
bool input;
|
||||||
|
};
|
||||||
|
|
||||||
Command command;
|
Command command;
|
||||||
BAN::Span<const BAN::String> arguments;
|
BAN::Span<const BAN::String> arguments;
|
||||||
BAN::Span<const Environment> environments;
|
BAN::Span<const Environment> environments;
|
||||||
|
BAN::Span<Redirection> redirections;
|
||||||
int fd_in;
|
int fd_in;
|
||||||
int fd_out;
|
int fd_out;
|
||||||
bool background;
|
bool background;
|
||||||
|
|
|
@ -21,6 +21,8 @@ BAN::ErrorOr<BAN::Vector<Token>> tokenize_string(BAN::StringView string)
|
||||||
case ')': return Token::Type::CloseParen;
|
case ')': return Token::Type::CloseParen;
|
||||||
case '$': return Token::Type::Dollar;
|
case '$': return Token::Type::Dollar;
|
||||||
case '"': return Token::Type::DoubleQuote;
|
case '"': return Token::Type::DoubleQuote;
|
||||||
|
case '>': return Token::Type::GreaterThan;
|
||||||
|
case '<': return Token::Type::LessThan;
|
||||||
case '{': return Token::Type::OpenCurly;
|
case '{': return Token::Type::OpenCurly;
|
||||||
case '(': return Token::Type::OpenParen;
|
case '(': return Token::Type::OpenParen;
|
||||||
case '|': return Token::Type::Pipe;
|
case '|': return Token::Type::Pipe;
|
||||||
|
|
|
@ -27,6 +27,12 @@ void Token::debug_dump() const
|
||||||
case Type::DoubleQuote:
|
case Type::DoubleQuote:
|
||||||
dprintln("Token <DoubleQuote>");
|
dprintln("Token <DoubleQuote>");
|
||||||
break;
|
break;
|
||||||
|
case Type::GreaterThan:
|
||||||
|
dprintln("Token <GreaterThan>");
|
||||||
|
break;
|
||||||
|
case Type::LessThan:
|
||||||
|
dprintln("Token <LessThan>");
|
||||||
|
break;
|
||||||
case Type::OpenCurly:
|
case Type::OpenCurly:
|
||||||
dprintln("Token <OpenCurly>");
|
dprintln("Token <OpenCurly>");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -19,6 +19,8 @@ public:
|
||||||
CloseParen,
|
CloseParen,
|
||||||
Dollar,
|
Dollar,
|
||||||
DoubleQuote,
|
DoubleQuote,
|
||||||
|
GreaterThan,
|
||||||
|
LessThan,
|
||||||
OpenCurly,
|
OpenCurly,
|
||||||
OpenParen,
|
OpenParen,
|
||||||
Pipe,
|
Pipe,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
static constexpr bool can_parse_argument_from_token_type(Token::Type token_type)
|
static constexpr bool can_parse_argument_from_token_type(Token::Type token_type)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +19,8 @@ static constexpr bool can_parse_argument_from_token_type(Token::Type token_type)
|
||||||
case Token::Type::Ampersand:
|
case Token::Type::Ampersand:
|
||||||
case Token::Type::CloseCurly:
|
case Token::Type::CloseCurly:
|
||||||
case Token::Type::CloseParen:
|
case Token::Type::CloseParen:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
case Token::Type::OpenParen:
|
case Token::Type::OpenParen:
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
|
@ -43,6 +46,8 @@ static constexpr char token_type_to_single_character(Token::Type type)
|
||||||
case Token::Type::CloseParen: return ')';
|
case Token::Type::CloseParen: return ')';
|
||||||
case Token::Type::Dollar: return '$';
|
case Token::Type::Dollar: return '$';
|
||||||
case Token::Type::DoubleQuote: return '"';
|
case Token::Type::DoubleQuote: return '"';
|
||||||
|
case Token::Type::GreaterThan: return '>';
|
||||||
|
case Token::Type::LessThan: return '<';
|
||||||
case Token::Type::OpenCurly: return '{';
|
case Token::Type::OpenCurly: return '{';
|
||||||
case Token::Type::OpenParen: return '(';
|
case Token::Type::OpenParen: return '(';
|
||||||
case Token::Type::Pipe: return '|';
|
case Token::Type::Pipe: return '|';
|
||||||
|
@ -74,6 +79,10 @@ static constexpr BAN::Error unexpected_token_error(Token::Type type)
|
||||||
return BAN::Error::from_literal("unexpected token $");
|
return BAN::Error::from_literal("unexpected token $");
|
||||||
case Token::Type::DoubleQuote:
|
case Token::Type::DoubleQuote:
|
||||||
return BAN::Error::from_literal("unexpected token \"");
|
return BAN::Error::from_literal("unexpected token \"");
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
return BAN::Error::from_literal("unexpected token >");
|
||||||
|
case Token::Type::LessThan:
|
||||||
|
return BAN::Error::from_literal("unexpected token <");
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
return BAN::Error::from_literal("unexpected token {");
|
return BAN::Error::from_literal("unexpected token {");
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
|
@ -185,6 +194,8 @@ BAN::ErrorOr<CommandArgument::ArgumentPart> TokenParser::parse_backslash(bool is
|
||||||
case Token::Type::CloseParen:
|
case Token::Type::CloseParen:
|
||||||
case Token::Type::Dollar:
|
case Token::Type::Dollar:
|
||||||
case Token::Type::DoubleQuote:
|
case Token::Type::DoubleQuote:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
case Token::Type::OpenParen:
|
case Token::Type::OpenParen:
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
|
@ -277,6 +288,8 @@ BAN::ErrorOr<CommandArgument::ArgumentPart> TokenParser::parse_dollar()
|
||||||
case Token::Type::CloseCurly:
|
case Token::Type::CloseCurly:
|
||||||
case Token::Type::CloseParen:
|
case Token::Type::CloseParen:
|
||||||
case Token::Type::DoubleQuote:
|
case Token::Type::DoubleQuote:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
case Token::Type::Semicolon:
|
case Token::Type::Semicolon:
|
||||||
case Token::Type::SingleQuote:
|
case Token::Type::SingleQuote:
|
||||||
|
@ -363,6 +376,8 @@ BAN::ErrorOr<CommandArgument::ArgumentPart> TokenParser::parse_single_quote()
|
||||||
case Token::Type::CloseParen:
|
case Token::Type::CloseParen:
|
||||||
case Token::Type::Dollar:
|
case Token::Type::Dollar:
|
||||||
case Token::Type::DoubleQuote:
|
case Token::Type::DoubleQuote:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
case Token::Type::OpenParen:
|
case Token::Type::OpenParen:
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
|
@ -403,6 +418,8 @@ BAN::ErrorOr<CommandArgument> TokenParser::parse_argument()
|
||||||
case Token::Type::Ampersand:
|
case Token::Type::Ampersand:
|
||||||
case Token::Type::CloseCurly:
|
case Token::Type::CloseCurly:
|
||||||
case Token::Type::CloseParen:
|
case Token::Type::CloseParen:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
case Token::Type::OpenParen:
|
case Token::Type::OpenParen:
|
||||||
case Token::Type::Pipe:
|
case Token::Type::Pipe:
|
||||||
|
@ -466,6 +483,56 @@ BAN::ErrorOr<CommandArgument> TokenParser::parse_argument()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<SingleCommand::Redirection> TokenParser::parse_redirection()
|
||||||
|
{
|
||||||
|
int source_fd = -1;
|
||||||
|
|
||||||
|
if (peek_token().type() == Token::Type::String)
|
||||||
|
{
|
||||||
|
const auto string = read_token().string();
|
||||||
|
|
||||||
|
source_fd = 0;
|
||||||
|
for (char ch : string)
|
||||||
|
source_fd = (source_fd * 10) + (ch - '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto token_type = peek_token().type();
|
||||||
|
consume_token();
|
||||||
|
|
||||||
|
switch (token_type)
|
||||||
|
{
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
if (source_fd == -1)
|
||||||
|
source_fd = STDOUT_FILENO;
|
||||||
|
break;
|
||||||
|
case Token::Type::LessThan:
|
||||||
|
if (source_fd == -1)
|
||||||
|
source_fd = STDIN_FILENO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool append = (peek_token().type() == token_type);
|
||||||
|
if (append)
|
||||||
|
consume_token();
|
||||||
|
|
||||||
|
const bool duplicate = (peek_token().type() == Token::Type::Ampersand);
|
||||||
|
if (duplicate)
|
||||||
|
consume_token();
|
||||||
|
|
||||||
|
while (peek_token().type() == Token::Type::Whitespace)
|
||||||
|
consume_token();
|
||||||
|
|
||||||
|
return SingleCommand::Redirection {
|
||||||
|
.destination = TRY(parse_argument()),
|
||||||
|
.source_fd = source_fd,
|
||||||
|
.append = append,
|
||||||
|
.duplicate = duplicate,
|
||||||
|
.input = (token_type == Token::Type::LessThan),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<SingleCommand> TokenParser::parse_single_command()
|
BAN::ErrorOr<SingleCommand> TokenParser::parse_single_command()
|
||||||
{
|
{
|
||||||
SingleCommand result;
|
SingleCommand result;
|
||||||
|
@ -527,6 +594,8 @@ BAN::ErrorOr<SingleCommand> TokenParser::parse_single_command()
|
||||||
case Token::Type::CloseCurly:
|
case Token::Type::CloseCurly:
|
||||||
case Token::Type::Dollar:
|
case Token::Type::Dollar:
|
||||||
case Token::Type::DoubleQuote:
|
case Token::Type::DoubleQuote:
|
||||||
|
case Token::Type::GreaterThan:
|
||||||
|
case Token::Type::LessThan:
|
||||||
case Token::Type::OpenCurly:
|
case Token::Type::OpenCurly:
|
||||||
case Token::Type::OpenParen:
|
case Token::Type::OpenParen:
|
||||||
case Token::Type::SingleQuote:
|
case Token::Type::SingleQuote:
|
||||||
|
@ -562,18 +631,56 @@ BAN::ErrorOr<SingleCommand> TokenParser::parse_single_command()
|
||||||
consume_token();
|
consume_token();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto can_parse_redirection =
|
||||||
|
[this]() -> bool
|
||||||
|
{
|
||||||
|
const auto& token = peek_token();
|
||||||
|
|
||||||
|
if (token.type() == Token::Type::GreaterThan)
|
||||||
|
return true;
|
||||||
|
if (token.type() == Token::Type::LessThan)
|
||||||
|
return true;
|
||||||
|
if (token.type() != Token::Type::String)
|
||||||
|
return false;
|
||||||
|
if (token.string().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool is_number = true;
|
||||||
|
for (size_t i = 0; i < token.string().size() && is_number; i++)
|
||||||
|
is_number = isdigit(token.string()[i]);
|
||||||
|
if (!is_number)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto temp = read_token();
|
||||||
|
const bool is_redir =
|
||||||
|
(peek_token().type() == Token::Type::GreaterThan) ||
|
||||||
|
(peek_token().type() == Token::Type::LessThan);
|
||||||
|
MUST(unget_token(BAN::move(temp)));
|
||||||
|
if (!is_redir)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
while (peek_token().type() != Token::Type::EOF_)
|
while (peek_token().type() != Token::Type::EOF_)
|
||||||
{
|
{
|
||||||
while (peek_token().type() == Token::Type::Whitespace)
|
while (peek_token().type() == Token::Type::Whitespace)
|
||||||
consume_token();
|
consume_token();
|
||||||
|
|
||||||
auto argument = TRY(parse_argument());
|
if (can_parse_redirection())
|
||||||
TRY(result.arguments.push_back(BAN::move(argument)));
|
{
|
||||||
|
auto redirection = TRY(parse_redirection());
|
||||||
while (peek_token().type() == Token::Type::Whitespace)
|
TRY(result.redirections.push_back(BAN::move(redirection)));
|
||||||
consume_token();
|
}
|
||||||
if (!can_parse_argument_from_token_type(peek_token().type()))
|
else if (can_parse_argument_from_token_type(peek_token().type()))
|
||||||
|
{
|
||||||
|
auto argument = TRY(parse_argument());
|
||||||
|
TRY(result.arguments.push_back(BAN::move(argument)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -44,6 +44,8 @@ private:
|
||||||
BAN::ErrorOr<CommandArgument::ArgumentPart> parse_single_quote();
|
BAN::ErrorOr<CommandArgument::ArgumentPart> parse_single_quote();
|
||||||
BAN::ErrorOr<CommandArgument> parse_argument();
|
BAN::ErrorOr<CommandArgument> parse_argument();
|
||||||
|
|
||||||
|
BAN::ErrorOr<SingleCommand::Redirection> parse_redirection();
|
||||||
|
|
||||||
BAN::ErrorOr<SingleCommand> parse_single_command();
|
BAN::ErrorOr<SingleCommand> parse_single_command();
|
||||||
BAN::ErrorOr<PipedCommand> parse_piped_command();
|
BAN::ErrorOr<PipedCommand> parse_piped_command();
|
||||||
BAN::ErrorOr<CommandTree> parse_command_tree();
|
BAN::ErrorOr<CommandTree> parse_command_tree();
|
||||||
|
|
Loading…
Reference in New Issue