Executor

Walks the AST and runs each node: simple commands, pipelines, logical operators, subshells, blocks, and background jobs.

Defines the executor functions for handling AST nodes.

Author

zweng, pulgamecanica

Defines

MAX_PIPELINE
MAX_SAVED_FDS
CMD_HASH_BUCKETS

Default bucket count for the command-path hash table (prime, small).

Functions

void cmd_hash_init(struct s_shell *shell)

Allocate shell->cmd_hash if not already present.

void cmd_hash_destroy(struct s_shell *shell)

Drop every entry and free the table itself. Safe to call repeatedly.

void cmd_hash_clear(struct s_shell *shell)

Drop every entry but keep the table allocated (for hash -r).

t_cmd_hash_value *cmd_hash_get(struct s_shell *shell, const char *name)

Look up name without bumping the hit counter.

int cmd_hash_set(struct s_shell *shell, const char *name, const char *path)

Insert or replace name -> path (path is duplicated internally).

Preserves the hit counter when name already exists, so hash -p doesn’t reset history. Returns 1 on success, 0 on allocation failure (table left unchanged).

int cmd_hash_delete(struct s_shell *shell, const char *name)

Remove name. Returns 1 if an entry was removed, 0 otherwise.

void cmd_hash_iter(struct s_shell *shell, void (*f)(const char*, t_cmd_hash_value*, void*), void *userdata)

Iterate every (name, value) pair in insertion-agnostic order.

int executor_execute(struct s_shell *shell, t_ast *ast)

Main dispatch function for executing AST nodes.

Parameters:
  • shell – Pointer to the central shell state

  • ast – Pointer to the AST node to execute

Returns:

Exit status code

int execute_simple_command(struct s_shell *shell, t_cmd *cmd)

Node-type executors.

Parameters:
  • shell – Pointer to the central shell state

  • ast – Pointer to the AST node to execute

Returns:

Exit status code

int execute_pipeline(struct s_shell *shell, t_ast *ast)
int execute_and(struct s_shell *shell, t_ast *ast)
int execute_or(struct s_shell *shell, t_ast *ast)
int execute_sequence(struct s_shell *shell, t_ast *ast)
int execute_subshell(struct s_shell *shell, t_ast *ast)
int execute_block(struct s_shell *shell, t_ast *ast)
int execute_background(struct s_shell *shell, t_ast *ast)
int setup_redirections(t_list *redirs, int saved_fds[3])

redirection setup

If saved_fds is not NULL, save stdin/stdout/stderr first (for restore).

Parameters:
  • redirs – List of redirection nodes

  • saved_fds – Array to save original file descriptors

Returns:

Exit status code

void restore_redirections(int saved_fds[3])

Restore redirections.

Parameters:
  • saved_fds – Array of saved file descriptors

char *find_command(struct s_shell *shell, const char *name)

Command search (PATH)

Parameters:
  • shell – Pointer to the central shell state

  • name – Name of the command to search for

Returns:

Pointer to the found command, or NULL if not found

void exec_pipeline_external(struct s_shell *shell, t_cmd *cmd)

Pipeline helper (called from pipe_child, does not return)

Parameters:
  • shell – Pointer to the central shell state

  • cmd – Pointer to the command node

int get_exit_status(int wstatus)

get exit status from wait status

  • Normal exit: WEXITSTATUS (0-255)

    • Killed by signal: 128 + signal number

Parameters:
  • wstatus – Wait status code

Returns:

Exit status code

void split_assignment(const char *assign, char **name, char **value)

Caller must free both *name and *value.

If no ‘=’ found, *name = dup of assign, *value = dup of “”.

void apply_assignments(struct s_shell *shell, t_list *assigns, int do_export)

Apply variable assignments to the shell (shared by the simple command and pipeline child paths).

Parameters:
  • shell – The shell instance.

  • assigns – List of “NAME=value” assignment strings.

  • do_export – Whether to also export each variable.

int report_command_error(const char *name)

Print the diagnostic for an unrunnable command and return its exit status: 126 if the path exists but is not executable, 127 if it could not be found.

Print the diagnostic for an unrunnable command and return its exit status: 126 if the path exists but is not executable, 127 if it could not be found.

Distinguishes “exists but not executable” (126, permission denied) from “not found” (127). Only a name containing ‘/’ can be probed for existence on disk; a bare name that PATH search failed to resolve is reported as not found.

Parameters:
  • name – The command name as typed.

  • name – The command name as typed.

Returns:

126 or 127.

Returns:

126 if the path exists but is not executable, 127 otherwise.

struct t_cmd_hash_value
#include <executor.h>

Cached PATH lookup, indexed by command name in shell->cmd_hash.

path is heap-allocated and owned by the value. hits tracks how many times the cache satisfied a lookup (read by hash).

Public Members

char *path
size_t hits

Dispatch

Command execution functionality for 42sh.

Author

wengzhang, pulgamecanica

Functions

static int dispatch_node(t_shell *shell, t_ast *ast)
int executor_execute(t_shell *shell, t_ast *ast)

Simple Commands

Command execution functionality for 42sh.

Author

wengzhang, pulgamecanica

Functions

void apply_assignments(t_shell *shell, t_list *assigns, int do_export)

Apply assignments permanently to shell variables.

Used for a bare assignment, and in a forked child (a simple command or a pipeline stage) before execve.

Parameters:
  • shell – The shell instance.

  • assigns – The list of assignments.

  • do_export – Whether to export the variables.

static int exec_assignment_only(t_shell *shell, t_cmd *cmd)

Handle empty command (just assignments and/or redirections).

Example: FOO=bar or FOO=bar > file

Parameters:
  • shell – The shell instance.

  • cmd – The command structure.

Returns:

0 on success, 1 on failure.

static int save_and_apply_assigns(t_shell *shell, t_list *assigns, char **old_names, char **old_vals)

Save old values, apply temporary assignments for builtin scope.

old_names/old_vals arrays store what to restore afterward.

Parameters:
  • shell – The shell instance.

  • assigns – The list of assignments.

  • old_names – Array to store old variable names.

  • old_vals – Array to store old variable values.

Returns:

Count of saved assignments.

static void restore_assigns(t_shell *shell, char **old_names, char **old_vals, int count)

Restore old variable values after builtin execution.

Parameters:
  • shell – The shell instance.

  • old_names – Array of old variable names.

  • old_vals – Array of old variable values.

  • count – Number of assignments to restore.

static int exec_builtin(t_shell *shell, t_cmd *cmd, t_builtin_fn fn)

Execute a builtin with temporary assignments and redirections.

Assignments are scoped to this command only, then restored.

Parameters:
  • shell – The shell instance.

  • cmd – The command structure.

  • fn – The builtin function to execute.

Returns:

The exit status of the builtin.

static void exec_child(t_shell *shell, t_cmd *cmd)

Child process: place in own pgrp, apply assignments, exec.

setpgid(0, 0) race-free paired with parent-side setpgid(pid,pid); tcsetpgrp hands the terminal off before the child could block on stdin (parent’s SIG_IGN for SIGTTOU is still inherited here).

static int launch_simple_job(t_shell *shell, t_cmd *cmd, pid_t pid)
static void prewarm_cmd_cache(t_shell *shell, t_cmd *cmd)

Resolve and cache the external command in the parent before forking, so the cache survives in the shell process.

Skipped when the command has prefix assignments - those may change PATH for this invocation only, so resolution must happen in the child after apply_assignments. The result is freed immediately; we’re only here to warm shell->cmd_hash.

int execute_simple_command(t_shell *shell, t_cmd *cmd)

Logical (&&, ||, ;)

Logical command execution functionality for 42sh.

Author

wengzhang, pulgamecanica

Functions

int execute_and(t_shell *shell, t_ast *ast)

cmd1 && cmd2 : run right only if left succeeds (exit 0)

int execute_or(t_shell *shell, t_ast *ast)

cmd1 || cmd2 : run right only if left fails (exit != 0)

int execute_sequence(t_shell *shell, t_ast *ast)

cmd1 ; cmd2 : run both, return status of right

Pipes

Pipeline command execution functionality for 42sh.

Author

wengzhang, pulgamecanica

Functions

static int collect_pipeline(t_ast *ast, t_ast **cmds, int max)

Flatten nested PIPE nodes left-to-right into a flat array.

Example: (A | B) | C => [A, B, C]

static void close_pipes(int pipes[][2], int count)
static void pipe_child(t_shell *shell, t_ast *cmd_ast, int pipes[][2], int info[3])

Pipeline stage in the child: join the job pgrp, wire pipes, exec.

info = { stage_index, stage_count, group_pgid }. When group_pgid == 0 this stage becomes the pgrp leader; the parent mirrors setpgid race-free. Expansion of NODE_COMMAND stages is done in the parent before forking (see expand_pipeline_stages) so the child inherits the already-expanded argv and debug output stays in one process. The child also clears interactive (a forked stage is not the interactive shell) and applies one-shot assignments before redirections, mirroring exec_child.

void exec_pipeline_external(t_shell *shell, t_cmd *cmd)
static int open_pipes(int pipes[][2], int n)
static int fork_stage(t_shell *shell, t_ast *cmd_ast, int pipes[][2], int info[3])
static int spawn_pipeline(t_shell *shell, t_ast **cmds, int n, int pipes[][2], t_job *job)
static void prewarm_pipeline_cache(t_shell *shell, t_ast **cmds, int n)

Warm the command-path cache in the parent for each pipeline stage so cache hits survive the fork.

Mirrors prewarm_cmd_cache() in exec_command.c: skip builtins, absolute / relative paths, and stages with prefix assignments (the latter may shadow PATH for that stage only).

static void expand_pipeline_stages(t_shell *shell, t_ast **cmds, int n)

Expand every NODE_COMMAND stage in the parent before forking.

Runs the same expand_command pass that execute_simple_command uses, so each child inherits a fully-expanded argv. Compound stages (subshells etc.) are expanded later inside their own executor_execute call.

int execute_pipeline(t_shell *shell, t_ast *ast)

Subshell, Block, Background

Subshell, block, and background execution for 42sh.

Author

wengzhang, pulgamecanica

Functions

static void subshell_child(t_shell *shell, t_ast *ast)
static int launch_subshell_job(t_shell *shell, t_ast *ast, pid_t pid)
int execute_subshell(t_shell *shell, t_ast *ast)

( cmd ) - runs in a forked child (subshell) with its own pgrp, routed through the job-control machinery so Ctrl-Z works.

int execute_block(t_shell *shell, t_ast *ast)

{ cmd; } - runs in the current shell, but with its own redirections.

static void bg_child(t_shell *shell, t_ast *ast)

Execute a background command.

cmd & - runs in a forked child with its own process group.

Parameters:
  • shell – The shell instance.

  • ast – The abstract syntax tree node.

Returns:

The exit status of the command.

static void bg_apply_assignments(t_shell *shell, t_list *assigns)

Apply (and export) inline assignments in a bg subshell child.

Equivalent to apply_assignments(shell, ..., 1) in exec_command, inlined here because that helper isn’t exported and pulling it in would over-couple the modules.

static void bg_simple_child(t_shell *shell, t_ast *outer, t_cmd *cmd)

Child path for simple-command &: fork once, exec directly.

Bypasses the bg_child → executor_execute → exec_command → exec_child double-fork. That chain put the actual program in its own pgrp (not the job’s pgrp) and made exec_child call tcsetpgrp from a background pgrp — both broken. This single fork keeps the program in the same pgrp the parent tracks and never claims the terminal.

int execute_background(t_shell *shell, t_ast *ast)

I/O Redirections

Redirection setup and restore for 42sh.

Author

wengzhang, pulgamecanica

Functions

static int redir_get_fd(t_redir *redir)
static int redir_open_file(t_redir *redir)
static int apply_dup_redir(t_redir *redir, int target_fd)
static int apply_one_redir(t_redir *redir)
int setup_redirections(t_list *redirs, int saved_fds[3])

redirection setup

If saved_fds is not NULL, save stdin/stdout/stderr first (for restore).

void restore_redirections(int saved_fds[3])

Restore redirections.

Parameters:
  • saved_fds – Array of saved file descriptors

PATH Lookup

Command search functionality for 42sh.

Author

wengzhang, pulgamecanica

Functions

static char *build_path(const char *dir, const char *name)

Build “dir/name” path. Caller must free the result.

Parameters:
  • dir – The directory path.

  • name – The file name.

Returns:

The full path, or NULL if allocation fails.

static void free_split(char **arr)

Search each directory in PATH for an executable named name.

Parameters:
  • path_var – The PATH environment variable.

  • name – The command name.

Returns:

Heap-allocated full path, or NULL if not found.

static char *search_path(const char *path_var, const char *name)

Search each directory in PATH for an executable named name.

Parameters:
  • path_var – The PATH environment variable.

  • name – The command name.

Returns:

Heap-allocated full path, or NULL if not found.

static char *lookup_cached(t_shell *shell, const char *name)

Consult the command-path cache before walking PATH.

A cached path that no longer exists (file deleted, PATH changed, etc.) falls through to a fresh PATH walk. The hit counter is bumped only when the cache actually satisfies the lookup, so hash reflects real usage rather than just insertions.

char *find_command(t_shell *shell, const char *name)

  • If name contains ‘/’, treat it as a path directly.

    • Otherwise consult the hash cache, then search $PATH and remember the result on hit so the next lookup is O(1).

int report_command_error(const char *name)

Print the diagnostic for an unrunnable command and return its exit status.

Print the diagnostic for an unrunnable command and return its exit status: 126 if the path exists but is not executable, 127 if it could not be found.

Distinguishes “exists but not executable” (126, permission denied) from “not found” (127). Only a name containing ‘/’ can be probed for existence on disk; a bare name that PATH search failed to resolve is reported as not found.

Parameters:
  • name – The command name as typed.

Returns:

126 if the path exists but is not executable, 127 otherwise.

Utilities

Utility functions for command execution in 42sh.

Author

wengzhang, pulgamecanica

Functions

int get_exit_status(int wstatus)

get exit status from wait status

  • Normal exit: WEXITSTATUS (0-255)

    • Killed by signal: 128 + signal number

void split_assignment(const char *assign, char **name, char **value)

Caller must free both *name and *value.

If no ‘=’ found, *name = dup of assign, *value = dup of “”.