Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
@@ -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.