Skip to main content

Getting Started

Thank you for your interest in contributing to Minishell! This guide will help you understand the development workflow, coding standards, and best practices.
1

Fork and Clone

Fork the repository and clone your fork:
git clone <your-fork-url>
cd minishell
2

Build the Project

Ensure everything builds correctly:
make
./minishell
3

Create a Branch

Create a feature branch for your changes:
git checkout -b feature/your-feature-name
4

Make Your Changes

Implement your feature or fix following the guidelines below.
5

Test Thoroughly

Test your changes with various inputs and edge cases.
6

Submit a Pull Request

Push your branch and create a pull request with a clear description.

Code Style Guidelines

Minishell follows the 42 School Norm, a strict coding standard. Key requirements:

General Rules

  • Line Length: Maximum 80 characters per line
  • Function Length: Maximum 25 lines per function
  • Parameters: Maximum 4 parameters per function
  • Variables: Maximum 5 variables per function
  • Indentation: Use tabs (not spaces)
  • Braces: K&R style (opening brace on same line)

Naming Conventions

// Functions: snake_case with ft_ prefix
int     ft_strlen(char *str);
void    ft_execute_commands(t_mini *data);

// Structures: s_ prefix, typedef with t_ prefix
typedef struct s_node
{
    char    **full_cmd;
    int     infile;
} t_node;

// Variables: snake_case
char    **bin_path;
int     nbr_nodes;

// Constants: UPPER_SNAKE_CASE
#define TEMP_FILE ".\vtemp\th"
#define GREEN "\033[32m"

File Headers

All source files must include the 42 header:
/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   filename.c                                         :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: author <[email protected]>              +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: YYYY/MM/DD HH:MM:SS by author          #+#    #+#             */
/*   Updated: YYYY/MM/DD HH:MM:SS by author         ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

Function Formatting

Example of proper function formatting:
void	echo(char **av, int flag)
{
	int	i;

	i = 1;
	if (flag == 2)
		i = 2;
	while (av[i] && ft_strncmp(av[i], "-n", 2) == 0 && !av[i][2])
		i++;
	while (av[i])
	{
		printf("%s", av[i]);
		i++;
		if (av[i])
			printf(" ");
	}
	if (flag == 1)
		printf("\n");
	g_status = 0;
}
Use tabs for indentation, not spaces. The 42 norm is strict about this.

Project Architecture

Where to Add Code

Depending on your contribution, add code to the appropriate module:
FeatureFiles to Modify
New built-in commandbuiltins.c, builtins_utils.c, init_shell.c, minishell.h
Parsing logicparse_imput.c, more_parsing.c
Execution flowft_execute_commands.c, ft_execute_one_command.c
Environment variablesenv_parsed.c, env_parsed2.c
Signal handlingsignals.c
Utility functionsutils.c, utils2.c, utils3.c
New structure/typedefminishell.h

Main Data Structures

Understand the core structures before contributing:
// Main shell state
typedef struct s_mini
{
    char     **commands;      // Parsed command array
    char     *full_path;      // Path to executable
    int      splits;          // Argument count
    int      ft_count_pipes;  // Pipe count
    char     **execute_envp;  // Environment array
    t_node   **nodes;         // Execution nodes
    int      nbr_nodes;       // Node count
    char     **bin_path;      // PATH directories
    t_prompt *env;            // Environment list
} t_mini;

// Execution node
typedef struct s_node
{
    char  **full_cmd;   // Command + args
    char  *full_path;   // Full executable path
    int   infile;       // Input FD
    int   outfile;      // Output FD
    int   is_set;       // Validity flag
    pid_t n_pid;        // Process ID
} t_node;

// Environment variable
typedef struct s_prompt
{
    char            *envp;  // "KEY=value"
    struct s_prompt *next;  // Next variable
} t_prompt;

Adding a New Built-in Command

Follow these steps to add a new built-in command (e.g., export):
1

Implement the Command Function

Add your function to builtins.c:
void    my_builtin(char **args, t_mini **data)
{
    int i;
    
    i = 0;
    while (args[i])
    {
        // Your implementation
        i++;
    }
    g_status = 0;  // Set exit status
}
2

Add Function Prototype

Declare the function in minishell.h:
//builtins.c
void    echo(char **av, int flag);
void    pwd(int argc);
void    cd(int argc, char *av);
void    unset(char **argv, t_prompt **data);
void    my_builtin(char **args, t_mini **data);  // Add this
3

Add Built-in Detection

Update is_builtin() in set_infile_outfile.c (or wherever it’s located):
int is_builtin(char *str)
{
    if (ft_strncmp(str, "echo", 4) == 0 && !str[4])
        return (1);
    if (ft_strncmp(str, "cd", 2) == 0 && !str[2])
        return (1);
    if (ft_strncmp(str, "pwd", 3) == 0 && !str[3])
        return (1);
    if (ft_strncmp(str, "export", 6) == 0 && !str[6])
        return (1);
    if (ft_strncmp(str, "unset", 5) == 0 && !str[5])
        return (1);
    if (ft_strncmp(str, "env", 3) == 0 && !str[3])
        return (1);
    if (ft_strncmp(str, "exit", 4) == 0 && !str[4])
        return (1);
    // Add your built-in check
    if (ft_strncmp(str, "mybuiltin", 9) == 0 && !str[9])
        return (1);
    return (0);
}
4

Route Command in execute_builtin

Add routing logic in init_shell.c:
int other_actions(int argc, char **argv, t_mini **data)
{
    // ... existing code ...
    else if (ft_strncmp(argv[0], "mybuiltin", 9) == 0 && !argv[0][9])
        my_builtin(argv, data);
    else
        flag = 1;
    return (flag);
}
5

Update Makefile (if new file)

If you created a new file, add it to CFILES:
CFILES = main.c split.c utils.c ... my_builtin.c
6

Test the Built-in

make re
./minishell
minishell> mybuiltin arg1 arg2
Built-in commands should NOT fork. They run in the parent shell process to affect the shell’s state (like cd changing the working directory).

Testing Your Changes

Manual Testing

Test various scenarios:
# Basic functionality
minishell> echo hello world
minishell> pwd
minishell> cd /tmp
minishell> export TEST=value
minishell> echo $TEST

# Pipes
minishell> ls -l | grep txt | wc -l

# Redirections
minishell> echo test > output.txt
minishell> cat < output.txt
minishell> ls >> output.txt

# Here-documents
minishell> cat << EOF
> line 1
> line 2
> EOF

# Quotes
minishell> echo "hello world"
minishell> echo 'single quotes'
minishell> echo "$USER is $HOME"

# Exit status
minishell> ls /nonexistent
minishell> echo $?

# Signal handling
minishell> sleep 10
^C
minishell> cat
^C

Edge Cases to Test

  • Empty input
  • Only spaces/tabs
  • Unclosed quotes: echo "hello
  • Invalid pipes: | ls, ls |, ls | | grep
  • Invalid redirections: >, < >, >> <<
  • Very long input (> 1000 characters)
  • Multiple consecutive spaces
  • Special characters in quotes
  • Environment variable expansion: $VAR, $?, $$
  • Commands not in PATH
  • Permission denied errors
  • Directory vs file errors

Comparison Testing

Compare behavior with bash:
# Test in bash
bash$ echo hello | grep h | wc -l
1

# Test in minishell
minishell> echo hello | grep h | wc -l
1

Memory Leak Checking

Minishell must not have memory leaks. Use Valgrind:

Basic Valgrind Check

valgrind --leak-check=full --show-leak-kinds=all ./minishell

Suppressing Readline Leaks

Readline library may show “still reachable” memory. Create a suppression file:
# readline.supp
{
   readline_still_reachable
   Memcheck:Leak
   match-leak-kinds: reachable
   ...
   obj:*/libreadline.so.*
}
Run with suppressions:
valgrind --leak-check=full --suppressions=readline.supp ./minishell

Common Memory Issues

  1. Not freeing split arrays: Use free_split() or ft_free()
  2. Not freeing readline input: free(line) after use (main.c:42)
  3. Leaked environment list: Use ft_free_stack() (utils3.c)
  4. Leaked nodes: Use ft_free_nodes() (main.c:39)
Every malloc() must have a corresponding free(). Use tools like Valgrind to verify.

Debugging Tips

Using Printf Debugging

// Add debug prints
void enterdata(char *line, t_mini *data, int i)
{
    printf("DEBUG: line = '%s'\n", line);  // Debug
    add_history(line);
    line = prepare_line(line, -1, 0);
    printf("DEBUG: prepared = '%s'\n", line);  // Debug
    // ... rest of function
}

Using GDB

# Compile with debug symbols (already enabled with -g)
make

# Run with GDB
gdb ./minishell

# Set breakpoints
(gdb) break main
(gdb) break enterdata
(gdb) break ft_execute_commands

# Run and debug
(gdb) run
(gdb) next          # Step over
(gdb) step          # Step into
(gdb) print data    # Print variable
(gdb) print *data->nodes[0]  # Print structure

Debug Environment Variables

void print_env(t_prompt *env)
{
    while (env)
    {
        printf("ENV: %s\n", env->envp);
        env = env->next;
    }
}

Debug Command Nodes

void print_node(t_node *node, int index)
{
    int i = 0;
    printf("Node[%d]:\n", index);
    printf("  full_path: %s\n", node->full_path);
    printf("  full_cmd: ");
    while (node->full_cmd && node->full_cmd[i])
    {
        printf("'%s' ", node->full_cmd[i]);
        i++;
    }
    printf("\n  infile: %d, outfile: %d\n", node->infile, node->outfile);
    printf("  is_set: %d\n", node->is_set);
}

Common Pitfalls

Pitfall #1: Forgetting to free memoryEvery allocation must be freed. Use Valgrind to verify.
Pitfall #2: Modifying argv directly
// BAD - modifies original
char **args = argv;
args[0] = "modified";

// GOOD - create a copy if needed
char **args = ft_strdup2(argv, len, 0);
Pitfall #3: Not closing file descriptors
// Always close opened file descriptors
if (fd != STDIN_FILENO && fd != STDOUT_FILENO)
    close(fd);
Pitfall #4: Infinite loops in parsingEnsure loop counters advance:
while (str[i])
{
    // Must have i++ somewhere
    i++;
}
Pitfall #5: Buffer overflowsUse safe string functions:
// BAD
strcpy(dest, src);

// GOOD
ft_strlcpy(dest, src, dest_size);

Code Review Checklist

Before submitting your pull request:
  • Code compiles without warnings (-Wall -Wextra -Werror)
  • Follows 42 norm (line length, function length, etc.)
  • All functions have proper prototypes in minishell.h
  • No memory leaks (verified with Valgrind)
  • All file descriptors are closed
  • Tested with various inputs and edge cases
  • Compared behavior with bash
  • Updated documentation if needed
  • Code is properly commented for complex logic
  • Uses existing utility functions when possible
  • Global variable g_status updated appropriately

Getting Help

If you need assistance:
  1. Check the documentation: Review the building and structure guides
  2. Read the code: The codebase is well-organized and documented
  3. Open an issue: Describe your problem with code examples
  4. Ask in discussions: For general questions about contributing

Next Steps

Happy coding! We appreciate your contributions to Minishell.

Build docs developers (and LLMs) love