feat: implemented pipes and redirects + background operator
This commit is contained in:
50
README.md
50
README.md
@@ -1,3 +1,5 @@
|
||||
Bonuses added for lab 3 are at the bottom of this document, starting [here](#navigable-arrow-keys).
|
||||
|
||||
# general code structure
|
||||
The main objective of this assignment was to tokenize given inputs and process the resulting tokens according to the given grammar. We decided to seperate the two steps of tokenizing and processing almost entirely, which decouples the input parser from the execution very neatly. The one drawback of this method is that each input is, more or less, processed twice - but we considered this a fair trade-off, as the input for a shell can generally be expected to not be incredulously large.
|
||||
|
||||
@@ -23,8 +25,6 @@ Builtin | Result
|
||||
--- | ---
|
||||
status | prints the return status of the last run command.
|
||||
exit | exits the shell.
|
||||
true | sets status to `0`.
|
||||
false | sets status to `-1`.
|
||||
cd | changes the current working directory.
|
||||
debug | prints debugging information to `stdout`.
|
||||
|
||||
@@ -65,11 +65,13 @@ typedef struct CommandList {
|
||||
} CommandList;
|
||||
```
|
||||
|
||||
The `Chain` struct models one complete chain to be executed. It is currently a bit barebones, but will be expanded when we implement piping, redirection, and running in the background.
|
||||
The `Chain` struct models one complete chain to be executed. It conains a list of commands to execute, and extra information related to I/O - any redirects. These are set to `NULL` if no redirect was given.
|
||||
```c
|
||||
typedef struct Chain {
|
||||
CommandList commands;
|
||||
bool runInBackground;
|
||||
char *in;
|
||||
char *out;
|
||||
} Chain;
|
||||
```
|
||||
|
||||
@@ -90,8 +92,6 @@ The execution is done within a while loop that runs until the `cpy` list is enti
|
||||
- COMPOSITION
|
||||
- BUILTIN
|
||||
|
||||
// TODO: discuss what the process is for each enum type.
|
||||
|
||||
If the code encounters any other types at this level, it means that the syntax of the input is incorrect. If this happens, an error message is printed out and `cpy` is set to NULL in order to exit the loop.
|
||||
|
||||
In the end, we use `free()` and `freeTokenList()` to free the memory.
|
||||
@@ -163,10 +163,40 @@ The end result looks like this:
|
||||
|
||||

|
||||
|
||||
## true / false
|
||||
The smallest of our extra implementations, we have added two `builtin`s which directly modify `status`.
|
||||
|
||||
* The builtin `true` will set `status` to `0`.
|
||||
* The builtin `false` will set `status` to `-1`.
|
||||
## navigable arrow keys
|
||||
Another standard shell feature, available in everything from the modern fish and zsh, to shells as far back as sh, is that of the arrow key. Because our shell takes input using `getchar()` the user is unable to navigate back in an unfinished prompt. By switching to the GNU readline library, we can make use of the `readline()` function - allowing us to navigate an unfinished prompt in the expected manner.
|
||||
|
||||
Really this was more of an implementation to allow us to do proper debugging, but it's still something we added to the shell that was not specifically part of the assignment, so we decided we may as well document it here.
|
||||
This is a bit hard to illustrate using text/images, so we recommend you build the shell yourself if you wish to play around with this a little bit.
|
||||
|
||||
|
||||
## tab completion
|
||||
Another perk of the GNU readline library is it quite neatly allows for tab completion, bash-style. Use tab while entering a command and watch as the shell tries its best to complete it for you.
|
||||
|
||||
Note that this only works on filenames - the bash-style tab-completion of variables and executable commands is not implemented. Included below is an example; after entering 'cat' we then pressed tab to showcase the completion, which prints all (matching) files in CWD to the shell.
|
||||
|
||||
|
||||

|
||||
|
||||
## background operation
|
||||
Everyone who has ever used a shell before will be familiar with the background operator, but here's a quick recap anyay: Whenever a command is suffixed with a single ampersand (`&`), it is run in the background and control of the shell is immediately returned to the user. The output streams of the command are still connected to the shell, (ergo, `echo 7 &` will still print '7' to stdout, which might mess with the prompt a little), but the command is run as non-blocking.
|
||||
|
||||
Because of the structured approach we took towards chain/command design, it was trivially easy for us to implement this bonus: a simple if statement (checking for the background operator) and we are good to go!
|
||||
|
||||
The relevant snippet of code lives in `executeChain` and reads as follows:
|
||||
```c
|
||||
// wait on children
|
||||
int stat;
|
||||
#if EXT_PROMPT
|
||||
if (chain.runInBackground) return;
|
||||
#endif
|
||||
for (int i=0; i < numCommands; i++) {
|
||||
waitpid(pids[i], &stat, 0);
|
||||
if (WIFEXITED(stat)) {
|
||||
*status = WEXITSTATUS(stat);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- ## command history -->
|
||||
<!-- Similarly, by using the GNU history header, we are able to implement a simple command history using a linked list structure. Try pressing the 'up' and 'down' arrow keys to navigate your command history. -->
|
||||
|
||||
Reference in New Issue
Block a user