diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..9c98538 --- /dev/null +++ b/PLAN.md @@ -0,0 +1,66 @@ +# C-Shell Implementation Plan + +## 1. `csh_readline` Implementation +**Missing Features & Issues:** +- The prompt `printf("> ");` is currently inside the `while(1)` loop, meaning it prints for every character read instead of once per line. +- The loop does not terminate when `\n` or `EOF` is reached. It just assigns `\0` and continues. +- Buffer resizing logic is not implemented. + +**Implementation Details:** +- **Prompt:** Move `printf("> ");` outside of the `while(1)` loop. +- **Buffer Resizing:** When `n_char >= buff_size`, use `realloc` to double the `buff_size`. If `realloc` fails, print an error and exit. +- **Termination:** When `ch == EOF` or `ch == '\n'`, append `\0` to the buffer and `return buffer;`. If `EOF` is read and `n_char == 0`, it may be appropriate to handle shell exit. + +## 2. `csh_format_args` Implementation +**Missing Features & Issues:** +- `csh_format_args` currently has no implementation (it is only declared). +- Needs to parse the line string into a null-terminated array of arguments. + +**Implementation Details:** +- **Tokenization:** Use `strtok` (or `strtok_r`) to split the `line` string by standard delimiters (space, tab, newline, carriage return: `" \t\r\n\a"`). +- **Dynamic Resizing:** Start with a fixed initial array size (e.g., 64). As tokens are parsed and added to the array, track the current count. If `count >= buff_size`, use `realloc` to increase the array capacity. +- **Null-Termination:** The argument list must be `NULL`-terminated for compatibility with `execvp`. Append `NULL` to the position after the last token before returning. + +## 3. `csh_exec` Implementation +**Missing Features & Issues:** +- Not implemented (only declared). Needs to spawn processes and execute built-ins. + +**Implementation Details:** +- **Built-in Handling:** Check `args[0]`. If it matches `"cd"`, `"help"`, or `"exit"`, execute the built-in function instead of creating a new process. + - `cd`: Use `chdir(args[1])`. Print error if it fails. + - `exit`: Return `0` (so `csh_loop` exits). + - `help`: Print a short message with the list of built-ins. +- **Process Creation:** If not a built-in, use `pid_t pid = fork()`. +- **Child Execution:** In the child process (`pid == 0`), call `execvp(args[0], args)`. If `execvp` returns, an error occurred (print with `perror`). Then `exit(EXIT_FAILURE)`. +- **Parent Waiting:** In the parent process (`pid > 0`), loop with `waitpid(pid, &status, WUNTRACED)` until the process finishes (`WIFEXITED(status)` or `WIFSIGNALED(status)`). + +## 4. Command History Implementation +**Implementation Details:** +- **Terminal Settings:** Use `termios` (`tcgetattr`, `tcsetattr`) to turn off canonical mode and echo (`ICANON`, `ECHO`), allowing characters to be processed as soon as they are typed, which is necessary for detecting arrow keys (`\033[A` and `\033[B`). +- **Data Structure:** Maintain a circular buffer or a dynamic array of strings to store executed commands. Keep track of the total history count and the current index while browsing history. +- **Escape Sequences:** In `csh_readline`, if `\033` (Escape) is detected, read the next two characters (usually `[` and `A` or `B` for up/down). Replace the current input buffer with the selected history command, clear the current line on the terminal (`\r\033[K`), and print the history command. +- **Saving History:** Only save non-empty commands to the history buffer after execution. + +## 5. Input/Output Redirection Implementation +**Implementation Details:** +- **Parsing:** Before executing, scan the parsed argument array `args` for `>` or `<`. + - If `>` is found, the next argument is the output file name. + - If `<` is found, the next argument is the input file name. + - Remove the redirection operators and file names from the `args` array (replace them with `NULL`) before calling `execvp`. +- **Execution (`csh_exec`):** In the child process (after `fork()`, before `execvp()`): + - For `>`: Call `open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0644)`. If successful, use `dup2(fd, STDOUT_FILENO)` to redirect stdout, then `close(fd)`. + - For `<`: Call `open(input_file, O_RDONLY)`. If successful, use `dup2(fd, STDIN_FILENO)` to redirect stdin, then `close(fd)`. + +## 6. Background Process Execution Implementation +**Implementation Details:** +- **Parsing:** In `csh_format_args` or before executing `csh_exec`, check if the last argument is `&`. +- **Logic modification:** If `&` is found, remove it from the arguments list (replace with `NULL`). +- **Execution (`csh_exec`):** Pass a flag (e.g. `is_bg`) down to the execution logic. If `is_bg` is true, the parent process does not wait for the child process. It skips the `waitpid()` call or uses `WNOHANG`. +- **Reaping Zombies:** If processes run in the background, they must be reaped to prevent zombie processes. This can be achieved by periodically calling `waitpid(-1, NULL, WNOHANG)` in the main loop or using `SIGCHLD` signal handling. + +## 7. Environment Variable Support Implementation +**Implementation Details:** +- **Expansion Parsing:** In `csh_format_args` (or during a specific expansion pass over the parsed arguments), check if an argument starts with `$`. +- **Function Call:** If it starts with `$`, strip the `$` to get the variable name (e.g. from `$HOME`, extract `HOME`). +- **Retrieval:** Use the `getenv(var_name)` C library function. +- **Replacement:** If `getenv` returns a value, duplicate that string (e.g. using `strdup`) and replace the original `$VAR` argument in the `args` array. If `getenv` returns `NULL` (variable not found), replace it with an empty string or remove the argument entirely depending on the desired semantics.