mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 03:18:11 +00:00
Shell: Parse and correctly evaluate 'command &' and 'command &&'
This commit adds the InBackground and ShortCircuitOnFailure attributes to commands, which respectively cause the command to be run in the background, and abort the command chain with exit status != 0.
This commit is contained in:
parent
143be7234f
commit
c23c354779
3 changed files with 70 additions and 5 deletions
|
@ -49,11 +49,11 @@ void Parser::commit_subcommand()
|
||||||
m_subcommands.append({ move(m_tokens), move(m_redirections), {} });
|
m_subcommands.append({ move(m_tokens), move(m_redirections), {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::commit_command()
|
void Parser::commit_command(Attributes attributes)
|
||||||
{
|
{
|
||||||
if (m_subcommands.is_empty())
|
if (m_subcommands.is_empty())
|
||||||
return;
|
return;
|
||||||
m_commands.append({ move(m_subcommands) });
|
m_commands.append({ move(m_subcommands), attributes });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::do_pipe()
|
void Parser::do_pipe()
|
||||||
|
@ -109,6 +109,31 @@ Vector<Command> Parser::parse()
|
||||||
commit_command();
|
commit_command();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (ch == '&') {
|
||||||
|
commit_token(Token::Special);
|
||||||
|
|
||||||
|
if (i + 1 >= m_input.length()) {
|
||||||
|
in_background:;
|
||||||
|
// Nothing interesting past this token, commit with InBackground
|
||||||
|
commit_subcommand();
|
||||||
|
commit_command(Attributes::InBackground);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch = m_input.characters()[++i];
|
||||||
|
++m_position;
|
||||||
|
|
||||||
|
if (ch == '&') {
|
||||||
|
// This is '&&', commit with ShortCircuit
|
||||||
|
commit_subcommand();
|
||||||
|
commit_command(Attributes::ShortCircuitOnFailure);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
--i;
|
||||||
|
--m_position;
|
||||||
|
goto in_background;
|
||||||
|
}
|
||||||
if (ch == '|') {
|
if (ch == '|') {
|
||||||
commit_token(Token::Special);
|
commit_token(Token::Special);
|
||||||
if (m_tokens.is_empty()) {
|
if (m_tokens.is_empty()) {
|
||||||
|
|
|
@ -45,6 +45,12 @@ struct Token {
|
||||||
Type type;
|
Type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Attributes {
|
||||||
|
None = 0x0,
|
||||||
|
ShortCircuitOnFailure = 0x1,
|
||||||
|
InBackground = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
struct Redirection {
|
struct Redirection {
|
||||||
enum Type {
|
enum Type {
|
||||||
Pipe,
|
Pipe,
|
||||||
|
@ -71,6 +77,7 @@ struct Subcommand {
|
||||||
|
|
||||||
struct Command {
|
struct Command {
|
||||||
Vector<Subcommand> subcommands;
|
Vector<Subcommand> subcommands;
|
||||||
|
Attributes attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
|
@ -89,7 +96,7 @@ private:
|
||||||
};
|
};
|
||||||
void commit_token(Token::Type, AllowEmptyToken = AllowEmptyToken::No);
|
void commit_token(Token::Type, AllowEmptyToken = AllowEmptyToken::No);
|
||||||
void commit_subcommand();
|
void commit_subcommand();
|
||||||
void commit_command();
|
void commit_command(Attributes = None);
|
||||||
void do_pipe();
|
void do_pipe();
|
||||||
void begin_redirect_read(int fd);
|
void begin_redirect_read(int fd);
|
||||||
void begin_redirect_write(int fd);
|
void begin_redirect_write(int fd);
|
||||||
|
|
|
@ -1052,8 +1052,7 @@ IterationDecision Shell::wait_for_pid(const Shell::SpawnedProcess& process, bool
|
||||||
if (WEXITSTATUS(wstatus) != 0)
|
if (WEXITSTATUS(wstatus) != 0)
|
||||||
dbg() << "Shell: " << process.name << ":" << process.pid << " exited with status " << WEXITSTATUS(wstatus);
|
dbg() << "Shell: " << process.name << ":" << process.pid << " exited with status " << WEXITSTATUS(wstatus);
|
||||||
|
|
||||||
if (is_first_command_in_chain)
|
return_value = WEXITSTATUS(wstatus);
|
||||||
return_value = WEXITSTATUS(wstatus);
|
|
||||||
|
|
||||||
if (job) {
|
if (job) {
|
||||||
auto* mutable_job = const_cast<Job*>(job);
|
auto* mutable_job = const_cast<Job*>(job);
|
||||||
|
@ -1128,6 +1127,7 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbgprintf("\n");
|
dbgprintf("\n");
|
||||||
for (auto& redirecton : command.subcommands[i].redirections) {
|
for (auto& redirecton : command.subcommands[i].redirections) {
|
||||||
for (size_t j = 0; j < i; ++j)
|
for (size_t j = 0; j < i; ++j)
|
||||||
|
@ -1151,6 +1151,13 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (auto attributes = command.attributes) {
|
||||||
|
dbgprintf("\n ");
|
||||||
|
if (attributes & Attributes::InBackground)
|
||||||
|
dbgprintf("InBackground ");
|
||||||
|
if (attributes & Attributes::ShortCircuitOnFailure)
|
||||||
|
dbgprintf("ShortCircuitOnFailure ");
|
||||||
|
}
|
||||||
dbgprintf("\n");
|
dbgprintf("\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1159,8 +1166,20 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd)
|
||||||
tcgetattr(0, &trm);
|
tcgetattr(0, &trm);
|
||||||
|
|
||||||
int return_value = 0;
|
int return_value = 0;
|
||||||
|
bool fail_short_circuits = false;
|
||||||
|
|
||||||
for (auto& command : commands) {
|
for (auto& command : commands) {
|
||||||
|
if (fail_short_circuits) {
|
||||||
|
if (command.attributes & Attributes::ShortCircuitOnFailure)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Do not fail any command after this one, as we've reached the end of a short-circuit chain,
|
||||||
|
// e.g. foo && bar && baz ; foobar
|
||||||
|
// ^ we reached this command.
|
||||||
|
fail_short_circuits = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (command.subcommands.is_empty())
|
if (command.subcommands.is_empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1306,6 +1325,14 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd)
|
||||||
dbgprintf(" %d (%s)\n", child.pid, child.name.characters());
|
dbgprintf(" %d (%s)\n", child.pid, child.name.characters());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (command.attributes & Attributes::InBackground) {
|
||||||
|
// Set the jobs as running in background and continue without waiting.
|
||||||
|
for (auto& child : children)
|
||||||
|
const_cast<Job*>(jobs.get(child.pid).value())->set_running_in_background(true);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < children.size(); ++i) {
|
for (size_t i = 0; i < children.size(); ++i) {
|
||||||
auto& child = children[i];
|
auto& child = children[i];
|
||||||
dbg() << "Now waiting for " << child.name << " (" << child.pid << ")";
|
dbg() << "Now waiting for " << child.name << " (" << child.pid << ")";
|
||||||
|
@ -1314,6 +1341,12 @@ ExitCodeOrContinuationRequest Shell::run_command(const StringView& cmd)
|
||||||
break;
|
break;
|
||||||
} while (errno == EINTR);
|
} while (errno == EINTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.attributes & Attributes::ShortCircuitOnFailure) {
|
||||||
|
if (return_value != 0) {
|
||||||
|
fail_short_circuits = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_return_code = return_value;
|
last_return_code = return_value;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue