opSys-shell/main.c

167 lines
5.0 KiB
C

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "scanner.h"
#include "shell.h"
#include "cmd.h"
// general TODO:
// * properly bind stdin/stdout for piped commands
// * run chains in bg if given '&'
// * manage < and > redirects
// * signal handling
// * builtins - 'kill'
/**
* This function skips one COMMAND, including redirects and pipes (if any).
* This is used to move to the next command when a COMPOSITION operator fails.
* @param lp ListPointer containing remaining Tokens,
*/
void skipCommand(List *lp) {
*lp = (*lp)->next;
while (*lp != NULL) {
switch ((*lp)->type) {
case PIPELINE:
case REDIRECT:
case FILENAME:
case OPTION:
case EXECUTABLE:
case BUILTIN:
*lp = (*lp)->next;
break;
case COMPOSITION:
case BACKGROUND:
*lp = (*lp)->next;
return;
}
}
}
/**
* This function checks whether the current COMPOSITION operator passes (and thus should execute the next function).
* @param s the COMPOSITION operator.
* @param status the exit status of the last ran command.
* @return whether the COMPOSITION succeeds/whether to execute the next command.
*/
bool compositionPasses(char *s, int status) {
if (!strcmp(s, "&&") && ! status) return true;
else if (!strcmp(s, "||") && status) return true;
else if (s[0] == ';' || s[0] == '\n') return true;
return false;
}
/**
* Driver function. Contains the main loop which collects and parses input, and decides what and how to execute + output.
*/
int main(int argc, char *argv[]) {
char *inputLine;
List tokenList;
int status = 0;
bool do_loop = true;
#if EXT_PROMPT
bool debug = false;
char cwd[101], *usr;
#endif
// Disable buffering so we don't have to deal with out-of-order prints.
setbuf(stdin, NULL);
setbuf(stdout, NULL);
// The main loop body. Each iteration means one line of input.
// Will run until either EOF or the BUILTIN exit is given.
while (do_loop) {
#if EXT_PROMPT
getcwd(cwd, sizeof(cwd));
usr = getlogin();
if (!status) printf("\x1b[33m%s \x1b[36m%s \x1b[32m>\x1b[0m ", usr, cwd);
if ( status) printf("\x1b[33m%s \x1b[36m%s \x1b[31m>\x1b[0m ", usr, cwd);
#endif
inputLine = readInputLine();
// We have modified the readInputLine function to return NULL on EOF.
if (inputLine == NULL) {
do_loop = false;
break;
}
tokenList = getTokenList(inputLine);
// Because lists are pointers, we need copies so we can access the list later.
List cpy = tokenList, og = tokenList;
if (parseInputLine(&tokenList) && tokenList == NULL ) {
#if EXT_PROMPT
if (debug) printList(cpy);
#endif
// Consume the entire tokenList (through cpy).
while (cpy != NULL) {
switch (cpy->type) {
// A BUILTIN needs to be executed slightly differently than a COMMAND:
// BUILTINS cannot be redirected/piped.
case BUILTIN:;
Command cmd = buildCommand(&cpy);
// If execcuteBuiltin returns false, the BUILTIN was 'exit' and we should stop the shell.
#if EXT_PROMPT
if (! executeBuiltin(cmd, &status, &debug)) {
#else
if (!executeBuiltin(cmd, &status)) {
#endif
do_loop = false;
cpy = NULL;
}
freeCommand(cmd);
break;
// Finding an EXECUTABLE means we need to construct the appropriate chain (in case of redirects/pipes), and then run it.
case EXECUTABLE:;
Chain chain = buildChain(&cpy);
#if EXT_PROMPT
if (debug) printChain(chain);
#endif
executeChain(chain, &status);
freeChain(chain);
// Status 127 is returned when an EXECUTABLE cannot be found. This requires an extra line to stdout.
if (status == 127) {
printf("Error: command not found!\n");
}
break;
// Upon finding a COMPOSITION, we check if it succeeds. if yes, we consume the operator and continue.
// If not, we skip the next command entirely.
case COMPOSITION:;
if (compositionPasses(cpy->t, status)) {
cpy = cpy->next;
} else {
skipCommand(&cpy);
}
break;
// Encountering any of BACKGROUND, OPTION, PIPELINE, REDIRECT, or FILENAME here means either we are dealing with incorrect syntax,
// or a function did not correctly consume tokens.
case BACKGROUND:
case OPTION:
case PIPELINE:
case REDIRECT:
case FILENAME:
printf("Error: invalid syntax!\n");
cpy = NULL;
break;
}
}
} else {
// If tokenList was not parsed successfully, the syntax must be incorrect.
printf("Error: invalid syntax!\n");
}
// Memory.
free(inputLine);
freeTokenList(og);
}
return 0;
}