MiniDevil As beautiful as a shell
Execution model

Simple command (no pipe / no redir)

exec_simple_command(args, shell)
---> is_builtin(args[0]) ?
YES -> exec_builtin(args, shell)
NO -> exec_external(args, shell)
int exec_external(char **args, t_shell *shell)
Fork and execute an external command.
Definition: exec_cmd.c:150
int exec_simple_command(char **args, t_shell *shell)
Execute a simple command (builtin or external)
Definition: exec_utils.c:95
int exec_builtin(char **args, t_shell *shell)
Dispatch a builtin command to its function.
Definition: exec_utils.c:67
int is_builtin(char *cmd)
Check if a command is a shell builtin.
Definition: exec_utils.c:39
  • Builtins run in the parent process (so cd, export, unset and exit can modify shell state).
  • External commands fork a child, call reset_child_signals() and then execve().

Redirections

handle_redir() in executor_redir.c implements the SRER pattern: Save-Redirect-Execute-Restore

fd = open_redir_file(file, type)
saved_fd = dup(target) // save original STDIN or STDOUT
dup2(fd, target) // redirect
close(fd)
status = executor(redir->cmd) // recurse into child node
dup2(saved_fd, target) // restore
close(saved_fd)
return(status)
int executor(t_ast *node, t_shell *shell)
Main AST executor that dispatches by node type.
Definition: executor.c:42
int open_redir_file(char *file, t_node_type type)
Open a file for redirection (based on the redirection type)

For heredocs, the pipe read end fd is stored in redir->heredoc_fd, consumed once and set to -1 after use.

Pipelines

handle_pipe() in executor_pipe.c:

pipe(pipe_fd)
-------------
fork() -> left child: close(pipe_fd[0]), dup2(pipe_fd[1], STDOUT), executor(left)
fork() -> right child: close(pipe_fd[1]), dup2(pipe_fd[0], STDIN), executor(right)
parent: close both pipe ends, waitpid(left), waitpid(right)
return right child status

Children call reset_child_signals() and clean up inherited memory before exit.

Note
Inherited memory: current_input, current_ast, env list and GNL stash
  • Pipeline exit status = rightmost command's status
  • WIFSIGNALED is handled with 128 + WTERMSIG(status)

Heredoc pre-collection

Before execution, collect_heredocs() walks through the AST in left to right order and calls handle_heredoc() for every NODE_REDIR_HEREDOC node.

Each heredoc creates a pipe, reads lines until the delimiter (or CTRL C) & stores the pipe read end in redir->heredoc_fd.

If CTRL C interrupts any heredoc, collect_heredocs() calls close_heredoc_fds() to close the already collected pipe fds and returns -1.

The caller sets exit_status = 130.