SYSTEMS PROGRAMMING

Custom OS Shell

A Unix-like command line interpreter built from scratch in C. Features robust process creation via fork/exec, signal handling, persistent history, and built-in navigation commands.

Source Code C Language POSIX API

Capabilities

  • Process Controlfork() / execvp()
  • MemoryDynamic Tokens
  • PersistenceHistory File
LIFECYCLE
Fork-Exec-Wait
Standard Process Model
NAVIGATION
Built-in CD
Supports ~, -, and ..
HISTORY
Persistent
Log file across sessions

01. Execution Lifecycle

The shell operates on a standard Read-Eval-Print Loop (REPL). For external commands, it utilizes the fork() system call to create a child process and execvp() to replace the child's memory space with the new program. The parent process uses waitpid() to pause until execution completes.

Parent (Shell)fork()
Child Processexecvp()
New Programexit()

02. Built-in Commands

While most commands (like ls or grep) are external programs, commands that modify the shell's own state must be built-in.

Directory Navigation

Implemented cd using the chdir() syscall. Supports logic for ~ (home), - (previous), and error handling for invalid paths.

History Management

Commands are appended to a hidden .history.txt file. On startup, the last 10 lines are loaded into memory for quick access.

03. Tokenizer & Parsing

The shell processes raw user input by tokenizing the command line string into a null-terminated array of arguments (`char **`). It utilizes strtok to split the string by spaces and includes a sanitization step to strip the trailing newline character typically left by input reading functions.

utils.c — Argument Parsing Strategy
char **parse_args(char *line)
{
    char **args = malloc(1024 * sizeof(char *));
    char *arg = strtok(line, " ");
    int i = 0;
    while (arg != NULL)
    {
        args[i] = arg;
        arg = strtok(NULL, " ");
        i++;
    }
    args[i] = NULL;
    
    // Remove the trailing newline from the last argument
    // so execvp doesn't fail on command lookup
    args[i - 1][strlen(args[i - 1]) - 1] = '\0';
    return args;
}