|
1 | | -[](https://app.codecrafters.io/users/MohaIfk?r=2qF) |
2 | | - |
3 | | -# codecrafters-shell-cpp |
4 | | - |
5 | | -An enhanced C++ implementation of the "Build Your Own Shell" challenge from |
6 | | -CodeCrafters. This repository contains a small interactive shell (REPL) that |
7 | | -supports builtin commands, executing external programs, basic redirections, and |
8 | | -cross-platform process launching on both POSIX and Windows. |
9 | | - |
10 | | -This README documents how to build and run the project, shows usage examples |
11 | | -and explains a number of extensions implemented beyond the minimal starter |
12 | | -requirements. |
| 1 | +# codecrafters-shell-cpp — C++ interactive shell |
13 | 2 |
|
14 | | -## Highlights / Features |
| 3 | +[](https://app.codecrafters.io/users/MohaIfk?r=2qF) |
15 | 4 |
|
16 | | -- Cross-platform: builds and runs on POSIX (Linux/WSL/macOS) and Windows. |
17 | | -- Builtin commands: `echo`, `cd` (supports `~`), `pwd`, `type`, `exit`. |
18 | | -- External program execution (resolves PATH entries). |
19 | | -- Redirections: supports `>`, `>>` and numeric fds like `2>err.txt`. |
20 | | -- Robust argument parsing: single/double quotes, backslash escapes inside and |
21 | | - outside quotes, and error on unclosed quotes. |
22 | | -- Windows-specific support: `.exe/.bat/.cmd` resolution, CreateProcessW based |
23 | | - spawning, and careful argument quoting and UTF-8 -> UTF-16 conversion. |
24 | | -- Scoped redirections for builtins (so builtins honor redirected stdout/stderr). |
25 | | -- `cd` resolves and canonicalizes the target directory and supports `~`. |
| 5 | +This repository contains an enhanced C++ implementation of a small interactive |
| 6 | +shell (REPL). It started from the CodeCrafters "Build Your Own Shell" |
| 7 | +challenge and was extended with command history, tab completion, pipelines, |
| 8 | +redirections, cross-platform process spawning and several developer-friendly |
| 9 | +helpers. |
26 | 10 |
|
27 | | -The implementation files of note are in `src/`: |
28 | 11 |
|
29 | | -- `src/main.cpp` — program entrypoint and minimal runner. |
30 | | -- `src/shell.h`, `src/shell.cpp` — shell class, parsing, dispatching, PATH |
31 | | - resolution and builtin implementations. |
32 | | -- `src/utils.h` — process launching (POSIX fork/exec and Windows CreateProcess), |
33 | | - argument parser, home directory lookup, redirection helpers, etc. |
| 12 | +## Key features (current) |
34 | 13 |
|
35 | | -## What I implemented beyond the starter (quick summary) |
| 14 | +- Cross-platform process spawning: POSIX `fork`+`execv` and Windows |
| 15 | + `CreateProcessW` implementations. |
| 16 | +- Pipeline support (`|`), including proper pipe wiring on POSIX and Windows. |
| 17 | +- Redirections: `>`, `>>` and numeric fd redirections (e.g. `2>err.txt`). |
| 18 | +- Builtin commands: `echo`, `cd` (supports `~`), `pwd`, `type`, `exit`, |
| 19 | + `history` (with `-c`, `-d`, `-r`, `-w`, `-a` subcommands). |
| 20 | +- Command history: in-memory list plus optional file persistence controlled by |
| 21 | + the `HISTFILE` environment variable. |
| 22 | +- Tab completion for commands (uses a cached set of executables + builtins). |
| 23 | +- Robust argument parsing including single/double quotes and backslash |
| 24 | + escapes; throws on unclosed quotes. |
| 25 | +- Terminal raw-mode input and arrow-key navigation for history (POSIX via |
| 26 | + `termios`, Windows via `_getch`). |
| 27 | +- Scoped redirections for builtins so builtins honor redirection during their |
| 28 | + execution. |
36 | 29 |
|
37 | | -If you or someone on your team extended the starter, the most notable additions |
38 | | -in this codebase are: |
| 30 | +## Project layout (important files) |
39 | 31 |
|
40 | | -- Full cross-platform process launching: CreateProcessW for Windows with |
41 | | - careful quoting and UTF-8 to UTF-16 conversion. POSIX uses `fork` + `execv`. |
42 | | -- Argument parser that closely mirrors shell-like quoting behavior (single |
43 | | - quotes, double quotes with limited escaping, and backslash escapes). |
44 | | -- Redirection parsing and handling that supports numeric fds and append |
45 | | - (`>>`) and a `ScopedRedir` helper that temporarily redirects file |
46 | | - descriptors for builtin commands. |
47 | | -- PATH-resolution that tries platform-appropriate executable extensions on |
48 | | - Windows (.exe, .bat, .cmd) and checks executable bit on POSIX. |
49 | | -- Improved `cd` handling: supports `~` and canonicalizes the working |
50 | | - directory (resolving symlinks where possible). |
| 32 | +- `src/main.cpp` — program entrypoint. |
| 33 | +- `src/shell.h`, `src/shell.cpp` — main shell class, parsing, dispatching, |
| 34 | + builtins, pipeline execution, completion and history. |
| 35 | +- `src/utils.h` — helpers: `getenv_safe`, `get_home_directory`, `run_program_and_wait`, |
| 36 | + `parse_args`, and `ScopedRedir`. |
| 37 | +- `src/inputHelper.h` — small cross-platform helper for raw-mode input and |
| 38 | + `get_char_raw()` abstraction used by the REPL. |
| 39 | +- `CMakeLists.txt` — build configuration. |
51 | 40 |
|
52 | | -These features make the shell more robust and suitable for manual testing on |
53 | | -both Windows and POSIX environments. |
| 41 | +## Quick developer contract |
54 | 42 |
|
55 | | -## Requirements |
| 43 | +- Input: user-entered command lines (strings) via a REPL prompt. |
| 44 | +- Output: stdout/stderr of builtins and spawned processes; exit status comes |
| 45 | + from executed programs (pipelines are executed and waited upon). |
| 46 | +- Error modes: parsing errors (e.g. unclosed quotes) raise runtime errors |
| 47 | + printed to stderr; spawn errors return messages to stderr. |
56 | 48 |
|
57 | | -- C++17 compatible compiler (MSVC, g++, clang++) |
58 | | -- CMake (3.10+ recommended) |
59 | | -- On Windows: Visual Studio or Build Tools (so CMake can generate a Visual |
60 | | - Studio solution or Ninja files). On Linux/WSL: make/ninja + standard build |
61 | | - toolchain. |
| 49 | +Edge cases considered: empty lines, unclosed quotes, missing redirection |
| 50 | +targets, trying to run non-executable files, and PATH entries with permission |
| 51 | +errors (these are skipped during executable cache population). |
62 | 52 |
|
63 | | -## Build and run |
| 53 | +## Build (Windows and POSIX) |
64 | 54 |
|
65 | | -The repository contains a CMake-based build. Example commands below assume you |
66 | | -run them from the repository root. |
| 55 | +Requirements: C++20 compiler, CMake (3.10+ recommended). |
67 | 56 |
|
68 | | -Windows (recommended via Developer Command Prompt or PowerShell): |
| 57 | +Windows (Developer Command Prompt / PowerShell): |
69 | 58 |
|
70 | 59 | ```powershell |
71 | 60 | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release |
72 | 61 | cmake --build build --config Release |
73 | | -# Executable will usually be at build\Release\shell.exe (or the configured target dir) |
74 | | -. |
| 62 | +# Executable typically: build\Release\shell.exe |
75 | 63 | ``` |
76 | 64 |
|
77 | 65 | WSL / Linux / macOS: |
78 | 66 |
|
79 | 67 | ```bash |
80 | 68 | cmake -S . -B cmake-build-debug-wsl -DCMAKE_BUILD_TYPE=Release |
81 | 69 | cmake --build cmake-build-debug-wsl -- -j$(nproc) |
82 | | -# Executable will be at cmake-build-debug-wsl/shell |
| 70 | +# Executable typically: cmake-build-debug-wsl/shell |
83 | 71 | ``` |
84 | 72 |
|
85 | | -You can also use the provided `your_program.sh` wrapper if present and |
86 | | -appropriate for your environment (it simply runs the built executable). |
| 73 | +There is a helper script `your_program.sh` that can be adapted to run the |
| 74 | +built binary in your environment. |
87 | 75 |
|
88 | | -## Usage examples |
| 76 | +## Runtime usage (examples) |
89 | 77 |
|
90 | | -Run the shell (example path depends on your build): |
| 78 | +Start the shell and try commands: |
91 | 79 |
|
92 | 80 | ```bash |
93 | 81 | ./cmake-build-debug-wsl/shell |
94 | 82 | # or on Windows |
95 | 83 | build\Release\shell.exe |
96 | 84 | ``` |
97 | 85 |
|
98 | | -Once inside the prompt, try examples: |
99 | | - |
100 | | -- Builtins: |
101 | | - |
102 | | -- echo: `echo hello world` |
103 | | -- pwd: `pwd` |
104 | | -- cd: `cd /tmp` or `cd ~` |
105 | | -- type: `type ls` or `type echo` |
106 | | -- exit: `exit` or `exit 2` |
107 | | - |
108 | | -- External commands: |
109 | | - |
110 | | -- `ls -la` (POSIX) |
111 | | -- `dir` (Windows cmd builtin via external program resolution if available) |
112 | | - |
113 | | -- Redirection examples: |
114 | | - |
115 | | -- `echo hi > out.txt` (overwrite) |
116 | | -- `echo again >> out.txt` (append) |
117 | | -- `ls 2> err.txt` (redirect stderr to file) |
118 | | -- `myprog 1>out.txt 2>>err.txt` (mix stdout overwrite + stderr append) |
| 86 | +Examples inside the prompt: |
| 87 | + |
| 88 | +- Simple builtins: |
| 89 | + - echo: `echo hello world` |
| 90 | + - pwd: `pwd` |
| 91 | + - cd: `cd /tmp` or `cd ~` |
| 92 | + - type: `type ls` or `type echo` |
| 93 | + - exit: `exit` or `exit 2` |
| 94 | + |
| 95 | +- History: |
| 96 | + - up/down arrows to navigate previous commands (raw-mode input) |
| 97 | + - `HISTFILE` env var controls persisted history file path; history is read |
| 98 | + at startup and appended on exit (if configured). |
| 99 | + - `history` builtin examples: `history`, `history 10`, `history -c`, |
| 100 | + `history -d 5`, `history -w historyfile`, `history -r historyfile`, |
| 101 | + `history -a historyfile`. |
| 102 | + |
| 103 | +- External programs and completion: |
| 104 | + - tab completion for commands (builtins + cached executables from `PATH`). |
| 105 | + - `type <name>` reports whether a name is a builtin or prints the resolved |
| 106 | + path to an executable. |
| 107 | + |
| 108 | +- Pipelines and redirections: |
| 109 | + - `ls -la | grep txt | wc -l` |
| 110 | + - `echo hi > out.txt` |
| 111 | + - `echo again >> out.txt` |
| 112 | + - `ls 2> err.txt` |
| 113 | + - `myprog 1>out.txt 2>>err.txt` |
119 | 114 |
|
120 | 115 | Notes: |
121 | 116 |
|
122 | 117 | - Numeric fd redirections are supported (e.g. `2>file` targets stderr). |
123 | 118 | - Builtins use `ScopedRedir` so redirection affects builtin output as expected. |
124 | 119 |
|
125 | | -## Manual tests to verify functionality |
| 120 | +## Developer notes — where to look |
| 121 | + |
| 122 | +- Argument parsing: `parse_args` in `src/utils.h`. |
| 123 | +- Line parsing into pipeline + redirections: `parse_line_to_pipeline` in |
| 124 | + `src/shell.cpp`. |
| 125 | +- Pipeline execution: `execute_pipeline` in `src/shell.cpp` (separate |
| 126 | + implementations for POSIX and Windows). |
| 127 | +- Single-command execution: `execute_simple_command` and `run_program_and_wait` |
| 128 | + (`src/utils.h`). |
| 129 | +- History and completion: `populate_executable_cache`, `read_history`, |
| 130 | + `write_history`, `handle_completion` in `src/shell.cpp`. |
| 131 | +- Terminal input: `enableRawMode`, `disableRawMode`, and `get_char_raw` in |
| 132 | + `src/inputHelper.h`. |
| 133 | + |
| 134 | +## Limitations and next improvements |
126 | 135 |
|
127 | | -1. Build the project (see Build instructions). |
128 | | -2. Start the shell executable. |
129 | | -3. Verify builtins: run `echo`, `pwd`, `cd`, `type`, `exit`. |
130 | | -4. Verify external programs: run `which`/`type` or `ls`/`dir` depending on |
131 | | - platform (or run a small program you compiled and put on PATH). |
132 | | -5. Verify redirections: `echo hello > /tmp/test.txt`, then `cat /tmp/test.txt`. |
133 | | -6. On Windows, test an `.exe` in PATH and a `.bat` script; `type` should show |
134 | | - resolved paths. |
| 136 | +- No job control (background `&`, `fg`, `bg`) yet. |
| 137 | +- Environment variable and tilde expansion is limited (tilde `~` support in |
| 138 | + `cd` only). No `$VAR` expansion or globbing implemented. |
| 139 | +- Quoting and escaping aims to be shell-like but is not a full POSIX shell |
| 140 | + parser; edge cases may differ from bash/zsh. |
135 | 141 |
|
136 | | -## Where to look in the code |
| 142 | +Planned/possible next steps (I can implement any of these on request): |
137 | 143 |
|
138 | | -- `parse_args` in `src/utils.h` — argument parsing. |
139 | | -- `parse_command_with_redirect` in `src/shell.cpp` — builds `Command` with |
140 | | - argument vector and redirections parsed. |
141 | | -- `run_program_and_wait` in `src/utils.h` — platform-specific spawning. |
142 | | -- `ScopedRedir` in `src/utils.h` — temporarily redirects fds for builtins. |
| 144 | +- Add `$VAR` expansion and filename globbing. |
| 145 | +- Implement job control and background/foreground management. |
| 146 | +- Add unit tests for `parse_args`, `parse_line_to_pipeline` and |
| 147 | + `execute_pipeline` behavior. |
143 | 148 |
|
144 | 149 | ## License & attribution |
145 | 150 |
|
146 | | -This solution is based on the CodeCrafters Shell challenge. Follow the |
147 | | -original challenge terms where applicable when publishing or sharing your |
148 | | -solution. |
| 151 | +This project was started from the CodeCrafters Shell challenge. Respect the |
| 152 | +original challenge terms when sharing or publishing derived work. |
0 commit comments