mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 16:54:57 +00:00
Shell: Allow control structures to appear in pipe sequences
This makes commands like the following to be possible: ```sh $ ls && for $(seq 10) { echo $it } $ ls | for $(seq 1) { cat > foobar } ```
This commit is contained in:
parent
7b5ead64a5
commit
aa2df9277d
5 changed files with 73 additions and 41 deletions
|
@ -439,7 +439,7 @@ RefPtr<Job> Shell::run_command(const AST::Command& command)
|
|||
}
|
||||
|
||||
// If the command is empty, store the redirections and apply them to all later commands.
|
||||
if (command.argv.is_empty()) {
|
||||
if (command.argv.is_empty() && !command.should_immediately_execute_next) {
|
||||
m_global_redirections.append(command.redirections);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -481,6 +481,25 @@ RefPtr<Job> Shell::run_command(const AST::Command& command)
|
|||
return IterationDecision::Continue;
|
||||
};
|
||||
|
||||
auto apply_rewirings = [&] {
|
||||
for (auto& rewiring : rewirings) {
|
||||
#ifdef SH_DEBUG
|
||||
dbgprintf("in %s<%d>, dup2(%d, %d)\n", command.argv.is_empty() ? "(<Empty>)" : command.argv[0].characters(), getpid(), rewiring.dest_fd, rewiring.source_fd);
|
||||
#endif
|
||||
int rc = dup2(rewiring.dest_fd, rewiring.source_fd);
|
||||
if (rc < 0) {
|
||||
perror("dup2(run)");
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// dest_fd is closed via the `fds` collector, but rewiring.other_pipe_end->dest_fd
|
||||
// isn't yet in that collector when the first child spawns.
|
||||
if (rewiring.other_pipe_end && close(rewiring.other_pipe_end->dest_fd) < 0)
|
||||
perror("close other pipe end");
|
||||
}
|
||||
|
||||
return IterationDecision::Continue;
|
||||
};
|
||||
|
||||
for (auto& redirection : m_global_redirections) {
|
||||
if (resolve_redirection(redirection) == IterationDecision::Break)
|
||||
return nullptr;
|
||||
|
@ -491,6 +510,19 @@ RefPtr<Job> Shell::run_command(const AST::Command& command)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (command.should_immediately_execute_next) {
|
||||
ASSERT(command.argv.is_empty());
|
||||
|
||||
SavedFileDescriptors fds { rewirings };
|
||||
if (apply_rewirings() == IterationDecision::Break)
|
||||
return nullptr;
|
||||
|
||||
for (auto& next_in_chain : command.next_chain)
|
||||
run_tail(next_in_chain, 0);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int retval = 0;
|
||||
if (run_builtin(command, rewirings, retval)) {
|
||||
for (auto& next_in_chain : command.next_chain)
|
||||
|
@ -524,20 +556,8 @@ RefPtr<Job> Shell::run_command(const AST::Command& command)
|
|||
|
||||
tcsetattr(0, TCSANOW, &default_termios);
|
||||
|
||||
for (auto& rewiring : rewirings) {
|
||||
#ifdef SH_DEBUG
|
||||
dbgprintf("in %s<%d>, dup2(%d, %d)\n", argv[0], getpid(), rewiring.dest_fd, rewiring.source_fd);
|
||||
#endif
|
||||
int rc = dup2(rewiring.dest_fd, rewiring.source_fd);
|
||||
if (rc < 0) {
|
||||
perror("dup2(run)");
|
||||
return nullptr;
|
||||
}
|
||||
// dest_fd is closed via the `fds` collector, but rewiring.other_pipe_end->dest_fd
|
||||
// isn't yet in that collector when the first child spawns.
|
||||
if (rewiring.other_pipe_end && close(rewiring.other_pipe_end->dest_fd) < 0)
|
||||
perror("close other pipe end");
|
||||
}
|
||||
if (apply_rewirings() == IterationDecision::Break)
|
||||
return nullptr;
|
||||
|
||||
fds.collect();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue