Skip to main content

System Overview

Minishell is a Unix shell implementation in C that follows a modular architecture with clear separation between parsing, execution, and built-in command handling. The system processes user input through a pipeline of tokenization, parsing, environment variable expansion, and command execution.

Input Processing

Handles readline integration, quote parsing, and input validation

Command Parsing

Tokenizes input, expands variables, and builds command structures

Execution Engine

Manages process creation, pipes, and redirections

Built-ins

Implements shell built-in commands (cd, echo, export, etc.)

Core Data Structures

The shell’s architecture revolves around three primary data structures defined in minishell.h:

t_mini - Main Shell Context

The central data structure that holds the entire shell state:
minishell.h:50
typedef struct s_mini
{
    char        **commands;       // Tokenized command array
    char        *full_path;       // Full path storage
    int         splits;           // Number of command splits
    int         ft_count_pipes;   // Pipe count in command
    char        **execute_envp;   // Environment for execve
    t_node      **nodes;          // Array of command nodes
    int         nbr_nodes;        // Number of nodes to execute
    char        **bin_path;       // PATH directories array
    t_prompt    *env;             // Environment variable list
} t_mini;
This structure is initialized once per shell session and updated for each command execution.

t_node - Command Execution Unit

Each command in a pipeline is represented by a node:
minishell.h:40
typedef struct s_node
{
    char    **full_cmd;     // Command with arguments
    char    *full_path;     // Absolute path to executable
    int     infile;         // Input file descriptor
    int     outfile;        // Output file descriptor
    int     is_set;         // Validity flag (1 if ready)
    pid_t   n_pid;          // Process ID after fork
} t_node;
The is_set flag indicates whether the node is valid and ready for execution. Invalid redirections or syntax errors set this to 0.

t_prompt - Environment Variables

Environment variables are stored in a linked list:
minishell.h:34
typedef struct s_prompt
{
    char            *envp;      // "KEY=value" format
    struct s_prompt *next;      // Next environment variable
} t_prompt;
This linked list structure allows dynamic modification of the environment without array reallocation.

Main Execution Flow

The shell operates in a read-eval-print loop defined in main.c:
1

Initialization

Setup signals, initialize environment, print welcome logo
main.c:105
print_logo();
setup_signals();
init_data(&data, envp);
2

Read Input

Display prompt and read user input via readline
main.c:109
prompt = ft_print_user();
line = readline(prompt);
3

Parse & Execute

Process the input through the enterdata function
main.c:20
line = prepare_line(line, -1, 0);
if (!ft_check_input(line))
{
    data->commands = ft_split(line, ' ');
    // Environment variable expansion
    while (data->commands[++i])
    {
        if (ft_check_dolars(data->commands[i]) == 1)
            data->commands[i] = vars(data->commands[i], data->env, 0, 0);
    }
    // Execution pipeline
    data->ft_count_pipes = ft_count_pipes(data->commands);
    data->nbr_nodes = ft_count_pipes(data->commands) + 1;
    set_bin_path(data);
    if (!ft_prepare_nodes(data))
        ft_execute_commands(data);
}
4

Update Status

Extract exit status from child processes
main.c:118
if (g_status != 2 && g_status != 1)
    g_status = (g_status >> 8) & 0xFF;

Component Interaction

The major components interact in this sequence:

Module Responsibilities

  • prepare_line(): Adds spaces around redirections
  • ft_split(): Quote-aware tokenization
  • vars(): Environment variable expansion
  • check_wrong_pipes() / check_wrong_redir(): Syntax validation
  • ft_prepare_nodes(): Creates execution nodes from tokens
  • ft_execute_commands(): Main execution dispatcher
  • execute_simple_command(): Single command execution
  • excecute_pipe_sequence(): Pipeline execution with pipes
  • asignenvp(): Initializes environment linked list
  • set_bin_path(): Extracts PATH directories
  • set_full_path(): Resolves command paths

Memory Management

Minishell follows strict memory management patterns:
  • Command arrays: Freed after each command execution via ft_free_nodes()
  • Environment list: Persists for shell lifetime, freed only on exit
  • Temporary strings: Freed immediately after use (e.g., in vars() expansion)
  • Nodes: Allocated per-command, cleaned up in ft_free_nodes()
The shell uses a global variable g_status to track the exit status of the last executed command, accessible from any module.

Global State

main.c:15
int g_status = 0;
This global variable stores:
  • Exit codes from child processes (0-255)
  • Signal-related status codes (130 for SIGINT)
  • Built-in command return values
  • Special values: 2 for syntax errors, 127 for command not found

Build docs developers (and LLMs) love