# Unix Shell This project is inspired by the assignment taken from the great textbook Operating Systems: Three Easy Pieces (OSTEP); written by Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau from UW-Madison. ## License MIT License Copyright (c) [2023] [Nguyen Ho] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## Overview A *command line interpreter (CLI)* or, as it is more commonly known, a *shell* is custom-built using C. The shell should operate in this basic way: when user type in a command (in response to its prompt), the shell creates a child process that executes the command the user entered and then prompts for more user input when it has finished. ## Program Specifications ### Basic Shell: `wish` The basic shell, called `wish` (short for Wisconsin Shell, naturally), is basically an interactive loop: it repeatedly prints a prompt `wish> ` (note the space after the greater-than sign), parses the input, executes the command specified on that line of input, and waits for the command to finish. This is repeated until the user types `exit`. The name of the final executable should be `wish`. The shell can be invoked with either no arguments or a single argument; anything else is an error. Here is the no-argument way: ``` prompt> ./wish wish> ``` At this point, `wish` is running, and ready to accept commands. Type away! The mode above is called *interactive* mode, and allows the user to type commands directly. The shell also supports a *batch mode*, which instead reads input from a batch file and executes commands from therein. Here is how to run the shell with a batch file named `batch.txt`: ``` prompt> ./wish batch.txt ``` One difference between batch and interactive modes: in interactive mode, a prompt is printed (`wish> `). In batch mode, no prompt should be printed. ## Structure ### Basic Shell The shell is very simple (conceptually): it runs in a while loop, repeatedly asking for input to tell it what command to execute. It then executes that command. The loop continues indefinitely, until the user types the built-in command `exit`, at which point it exits. That's it! For reading lines of input, use `getline()`. This allows us to obtain arbitrarily long input lines with ease. Generally, the shell will be run in *interactive mode*, where the user types a command (one at a time) and the shell acts on it. However, the shell will also support *batch mode*, in which the shell is given an input file of commands; in this case, the shellshould not read user input (from `stdin`) but rather from this file to get thecommands to execute. In either mode, if it hitd the end-of-file marker (EOF), call `exit(0)`. To parse the input line into constituent pieces, use `strsep()`. To execute commands, use `fork()`, `exec()`, and `wait()/waitpid()`. If necessary, read the relevant [book chapter](http://www.ostep.org/cpu-api.pdf) for a brief overview. ### Paths A **path** variable to describe the set of directories to search for executables; the set of directories that comprise the path are sometimes called the *search path* of the shell. The path variable contains the list of all directories to search, in order, when the user types a command. **Important:** Note that the shell itself does not *implement* `ls` or other commands (except built-ins). All it does is find those executables in one of the directories specified by `path` and create a new process to run them. To check if a particular file exists in a directory and is executable, consider the `access()` system call. For example, when the user types `ls`, and path is set to include both `/bin` and `/usr/bin`, try `access("/bin/ls", X_OK)`. If that fails, try "/usr/bin/ls". If that fails too, it is an error. The initial shell path contains one directory: `/bin' ### Built-in Commands Whenever the shell accepts a command, it should check whether the command is a **built-in command** or not. If it is, it should not be executed like other programs. Instead, the shell will invoke the implementation of the built-in command. For example, to implement the `exit` built-in command, simply call `exit(0);` in wish source code, which then will exit the shell. In this project, we implement `exit`, `cd`, and `path` as built-in commands. * `exit`: When the user types `exit`, the shell should simply call the `exit` system call with 0 as a parameter. It is an error to pass any arguments to `exit`. * `cd`: `cd` always take one argument (0 or >1 args should be signaled as an error). To change directories, use the `chdir()` system call with the argument supplied by the user; if `chdir` fails, that is also an error. * `path`: The `path` command takes 0 or more arguments, with each argument separated by whitespace from the others. A typical usage would be like this: `wish> path /bin /usr/bin`, which would add `/bin` and `/usr/bin` to the search path of the shell. If the user sets path to be empty, then the shell should not be able to run any programs (except built-in commands). The `path` command always overwrites the old path with the newly specified path. ### Redirection Many times, a shell user prefers to send the output of a program to a file rather than to the screen. Usually, a shell provides this nice feature with the `>` character. Formally this is named as redirection of standard output. This custom will implement this feature as well. For example, if a user types `ls -la /tmp > output`, nothing should be printed on the screen. Instead, the standard output of the `ls` program should be rerouted to the file `output`. In addition, the standard error output of the program should be rerouted to the file `output` (the twist is that this is a little different than standard redirection). If the `output` file exists before running the program, it will overwrite the existing file (after truncating it). The exact format of redirection is a command (and possibly some arguments) followed by the redirection symbol followed by a filename. Multiple redirection operators or multiple files to the right of the redirection sign are errors. Note: no redirection for built-in commands (e.g., no such thing as `path /bin > file`). ### Parallel Commands The shell will also allow the user to launch parallel commands. This is accomplished with the ampersand operator as follows: ``` wish> cmd1 & cmd2 args1 args2 & cmd3 args1 ``` In this case, instead of running `cmd1` and then waiting for it to finish, the shell should run `cmd1`, `cmd2`, and `cmd3` (each with whatever arguments the user has passed to it) in parallel, *before* waiting for any of them to complete. Then, after starting all such processes, it must make sure to use `wait()` (or `waitpid`) to wait for them to complete. After all processes are done, return control to the user as usual (or, if in batch mode, move on to the next line). ### Program Errors Print this one and only error message whenever an error is encountered: ``` char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); ``` The error message should be printed to stderr (standard error), as shown above. After ~~*any*~~ most errors, the shell simply *continue processing* after printing the one and only error message. However, if the shell is invoked with more than one file, or if the shell is passed a bad batch file, it should exit by calling `exit(1)`. There is a difference between errors that shell catches and those that the program catches. The shell should catch all the syntax errors specified in this project page. If the syntax of the command looks perfect, the shell simply run the specified program. If there are any program-related errors (e.g., invalid arguments to `ls` when running it, for example), the shell does not have to worry about that (rather, the program will print its own error messages and exit).