A strict-norm Bash replica with predictable tooling for 42 evaluations.
- At a Glance
- About
- Subject Compliance
- Custom Tester
- Repository Layout
- Build & Integration
- Usage Guidelines
- Feature Deep Dive
- Input & Prompt Flow
- Rendering & UX
- Internal Architecture
- Tester Workflow
- Results & Reporting
- Credits & Collaboration
Highlights: What evaluators can expect in the first five minutes.
- Executes POSIX commands with pipes, redirects, and heredocs while mirroring Bash exit codes through the single global
g_exit. - Builtins (
echo,cd,pwd,export,unset,env,exit) run in-place or in child processes depending on pipeline context. - Parser defends against malformed syntax early, matching Minishell subject edge-cases (
|, dangling redirects, empty tokens). - Custom tester automates Bash parity and Valgrind sweeps so every defense starts with a reproducible baseline.
- Co-built by Paula Alexandra (
paalexan) and João Pedro (jopedro-) with clear ownership per subsystem so knowledge transfers quickly at evaluation time.
Highlights: Why this shell feels reliable in daily use.
- Interactive loop ties
readline, prompt generation, and signal choreography soCtrl-Cinterrupts safely without tearing down state. - Tokenization and expansion are staged: splitting respects quotes, then
$expansion and heredoc rules decide how much Bash-like magic to apply. - Execution reuses one command graph (
t_cmdlist) for both pipelines and single commands, cutting duplicated logic across forks.
Subsystem responsibilities
src/parser/cleans input, expands environment variables, and tags tokens.src/execution/wires pipes, coordinates forks, and applies redirections.src/redirection/centralizes permission checks and heredoc lifecycle management.
Highlights: Direct trace between subject rules and implementation.
- Mandatory builtins live under
src/builtins/and respect subject constraints (cmd_exitenforces numeric-only arguments,cmd_cdupdatesPWD/OLDPWD). - Signal handling matches expectations:
ctrl_c_handlerrefreshes the prompt, heredoc forks switch toheredoc_sigint_handler, and child processes restore default behavior. - Only one global is present (
g_exit), and every heap allocation flows through dedicated cleanup helpers (free_cmd,free_minishell,free_env_array). readlineis the sole external dependency; leaks from it are waived while project-owned allocations are released (seeclean_all).needs_pipe_continuationmirrors Bash multiline behavior, satisfying the “incomplete pipe” clause from the subject PDF.
Compliance checklist
- Redirections: input/output/append/heredoc support via
process_redirectandapply_single_redirect. - Environment editing:
set_var,env_add_var, anddel_varcover export and unset flows. - Error messaging: dedicated helpers mimic Bash wording (`print_syntax_error`, `handle_cmd_error`).
Highlights: One command to prove parity with Bash.
tester/Makefileorchestrates runs againsttest_cases_auto.txt, stores Bash and Minishell outputs, and produces human-friendly diffs.- Shell scripts (
run_minishell.sh,run_bash.sh) normalise prompts, colours, and SHLVL so comparisons stay meaningful across machines. run_valgrind.shapplies readline suppressions automatically, marking tests green only when leak summaries are clean.- Full walkthroughs, edge-case notes, and extension tips live in the tester README.
cd tester
make # run automated suite and generate diff_summary.txt
make valgrind # optional leak run across the same command listTester pipeline internals
- Outputs land in
tester/results/{minishell,bash,diff}with__CMD_START__/__CMD_END__markers for parsing. generate_summary_diff.pycorrelates failing diffs with the originating command for quick triage.- Manual scenarios live in
tester/test_cases_manual.txt; copy lines into the suite when you confirm behaviour.
Highlights: Navigate modules without grepping.
src/– project code organised by responsibility (parser, tokenizer, execution, builtins, env, redirection, signals, cleanup, error).libft/– cloned automatically whenmakeruns; exposes string helpers, list utilities, andget_next_line.tester/– parity and leak harness, including reusable scripts and sample files.subject_minishell.pdf– original requirements for cross-referencing during peer reviews.
Directory cheat sheet
| Directory | Purpose |
|---|---|
src/execution/ | Pipelines, forks, builtin routing, wait loops. |
src/redirection/ | Redirect parsing, permission checks, heredoc tempfiles. |
src/cleanup/ | Memory hygiene and descriptor sweeping before exit. |
tester/test_files/ | Fixtures (empty, large, permission-locked) for edge cases. |
Highlights: Rebuild, relaunch, and lint with two targets.
makecloneslibftif missing, compiles objects into.obj/, and links with-lreadline.make valgrindspins up an interactive session with the pre-generated suppression file, matching tester behaviour.make cleanandmake fcleansweep objects, binary, suppression file, and evenlibft/so you can benchmark fresh checkouts.
sudo apt install libreadline-dev # If not installed already
make # build minishell (libft fetched if absent)
make re # full rebuild
make norm # run norminette with coloured status outputHighlights: How to interact like a real user would.
- Launch
./minishell, type commands exactly as in Bash, and exit withCtrl-Dor the builtinexit. - Builtins mutate the active environment when they run in the parent (single command) or mutate a fork-local copy inside pipelines.
- Error codes mirror Bash: missing command returns
127, permission issues return126, and signal exits follow128 + signal.
./minishell
minishell> export FILES="src parser"
minishell> echo "$FILES" | tr ' ' '\n'
minishell> cat <<EOT | grep minishell
prompt
minishell loop
EOT
minishell> exitEveryday tips
- Use
needs_pipe_continuationbehaviour: ending a line with|keeps the prompt open for the continuation. - Heredoc delimiters honour quotes: quoted delimiters disable expansion, unquoted pass through
handle_expansion. - Unset
PATHto test fallback error paths (`minishell: command not found`).
Highlights: From raw input to child processes.
| Stage | Key files | Notes |
|---|---|---|
| Tokenization | src/tokenizer/tokenizer_split.c, tokenizer_utils.c |
split_input, get_next_segment, and unescape_token turn raw lines into typed t_token nodes ready for expansion. |
| Expansion | src/parser/parser_expansion.c, tokenizer_lst.c |
expand_token, handle_all, and handle_dollar apply env lookups while preserving quote flags for later decisions. |
| Command graph | src/builtins/builtins_utils.c, execution/exec_cmd.c |
build_cmd_list, process_token, and cmd_from_tokens consolidate tokens into t_cmd nodes with redirect chains. |
| Execution | src/execution/exec_pipes.c, exec_child.c |
exec_pipeline, run_single_cmd, and exec_child coordinate pipes, builtins, forks, and exit propagation. |
| Cleanup | src/cleanup/cleanup_cmd.c, cleanup_utils.c |
clean_all, free_cmd, and free_hc_minishell release argv, env copies, and heredoc tmpfiles to finish cleanly. |
- Control keeps looping through
main→loopuntil EOF; each iteration builds tokens, compiles commands, feeds them toexec_pipeline, and updatesg_exitfor the next prompt. - Redirection helpers (
process_redirect,setup_redirections,apply_single_redirect) sit between parsing and execution so every child inherits pre-vetted descriptors.
Redirection specifics
check_output_permissioninspects parent directories so messages stay informative even before a file exists.write_heredoc_to_tmpcreates/tmp/.minishell_heredoc_*files and tracks them for removal infree_heredoc_tmpfiles.apply_single_redirectcentralisesdup2and marks failures for later error printing.
Environment lifecycle
- `copy_env_array` and `init_env` duplicate the inherited environment so mutating builtins never touch the parent process env.
- `export_assign` handles both direct replacement and `+=` concatenation (`export VAR+=suffix`).
- `cmd_unset` rebuilds the env array minus the target key, ensuring no dangling strings remain.
Highlights: Keeping the REPL responsive.
build_promptcomposesuser@hostname:cwd$using cached env data (sh->home,sh->user) and collapses$HOMEto~for readability.- Before each
readline,set_interactive_signalsarmsSIGINTandSIGQUIT; after reading, handlers flip to child-friendly defaults. handle_input_linestores tokens on the shell struct, runs syntax guards, executes, and frees everything before the next loop.
Continuation & history logic
- Empty lines are ignored; non-empty lines hit `add_history` so testers can arrow through commands like Bash.
- When heredocs are detected, the shell flags `is_heredoc` to disable further expansions inside delimiters.
- Pipe continuations stay open because `needs_pipe_continuation` trims trailing whitespace before checking for `|`.
Highlights: Output designed for evaluators reading diffs.
- Error printers prefix with
minishell:and reuse Bash wording, ensuring the tester diff is only triggered by behavioural differences. - Redirect failures are deferred until after execution so the parent can report them once per pipeline (
print_redirect_error). prompt_utils.ctrims hostname newlines and handles emptyPWDgracefully to keep the interface tidy on remote hosts.
Message helpers
print_syntax_errorandprint_syntax_error_eofcentralise subject messaging for malformed inputs.handle_cd_errordifferentiates between invalid options, missing HOME, and `chdir` failures.exec_invalid_cmdensures blank commands return `127` without leaking file descriptors.
Highlights: Data structures and lifetime management.
t_msh(singleton returned byget_shell) caches env copies, prompt metadata, heredoc bookkeeping, and the running command list.t_cmdnodes form a doubly linked list with argv arrays, redirect chains, builtin flags, and cached open file descriptors.t_tokenkeeps raw/expanded strings plus metadata (quoted,expanded_empty) so later stages can decide how to treat empty strings.t_redirectcaptures redirect type and target path, chained per command for sequential application.
Global state discipline
- `g_exit` is updated on every command; builtins and child processes set it before returning or exiting.
- `free_final_minishell` and `free_hc_minishell` variants tailor cleanup for graceful exits versus heredoc aborts.
- `clean_fds` closes descriptors from `3` onward before exiting to avoid stray handles between tests.
Highlights: How to run, inspect, and iterate quickly.
make -C testerwipes previous results, executes the auto list, and prints OK/KO per command with orange indexes.- Review mismatches in
tester/results/diff/test*.diffor opendiff_summary.txtfor side-by-side comparisons. - Extend coverage by appending commands to
tester/test_cases_auto.txt; scripts automatically renumber outputs.
make -C tester clean # optional: nuke previous run without touching binaries
make -C tester run # alias: only regenerate minishell/bash outputsManual scenarios
- Lines under "To be Fixed" in `test_cases_manual.txt` capture behaviours observed during development.
- Use the included fixtures (`big_file`, `invalid_permission`) to check performance and error reporting.
- Valgrind logs store per-command traces in `tester/results/valgrind_logs/` for deep dives.
Highlights: Actionable, low-noise logs.
- The tester prefixes every command with markers so Python scripts can slice outputs without guessing prompt lines.
diff_summary.txtlists command numbers, the original input, both outputs, and the diff chunk for fast triage.- Valgrind runs append
OK/KOlines intoresults/valgrind_results.txtwith the offending command when leaks appear. - Console summaries emphasise counts (
pass/fail/total) instead of raw diff dumps, keeping defences focused on high-signal issues.
Next steps after a KO
- Open the matching `testN.diff` and reproduce the command manually inside `./minishell`.
- Use `tester/scripts/run_minishell.sh` directly if you need to tweak sed cleaning or prompts.
- Capture Bash behaviour with `tester/scripts/run_bash.sh` to confirm whether the subject expects the deviation.
Highlights: Two minds, one shell.
- Paulo Alexandre (
paalexan) drove the parsing, execution, and error-handling layers, making sure everyt_cmdis battle-tested before it forks. - João Pedro (
jopedro-) focused on environment management, cleanup strategy, and user-facing polish, locking down memory hygiene and prompt comfort. - Pair-programming sessions aligned architecture decisions; we reviewed each module before merging so any 42 evaluation can call on either of us for clarifications.
- Special thanks to our peers who stress-tested the tester farm and uncovered early heredoc quirks.
