This is a project made from the "Build Your Own Shell" Challenge on CodeCrafters
This shell follows a classic read-eval-print loop (REPL) pattern with three main components:
- Input Layer: Buffered I/O using buffers for both stdin and stdout, minimizing system calls
- Command Parser: Simple space-delimited tokenizer
- Command Dispatcher: Enum-based switch statement that routes to built-in handlers or external execution
Built-ins are handled through a compile-time generated enum (built_in_commands) mapped from command strings. The dispatcher uses std.meta.stringToEnum() for O(1) lookup, falling back to external command execution for unrecognized commands. Each built-in receives the remaining token iterator for argument processing.
For non-built-in commands, the shell:
- PATH Traversal: Parses the
PATHenvironment variable and iterates through directories - Executable Discovery: Opens each directory, checks file permissions (mode & 0o111) then runs the given executable.
- Process Spawning: Uses
std.process.Childto spawn a child process with collected arguments
Uses Zig's GeneralPurposeAllocator for all dynamic allocations. Key patterns:
- Path strings from environment variables are allocated upfront and freed after the main loop
- Executable paths discovered during PATH traversal are allocated per-search and immediately freed after use
- Process argument lists use
std.ArrayListwithtoOwnedSlice()for clean handoff to the child process
Implements buffered I/O to reduce syscalls:
- Output:
writerStreamingwith manual flush after each prompt and command output - Input:
readerStreamingwithtakeDelimiter('\n')for line-based input reading