assignment 1
This commit is contained in:
163
main.c
Normal file
163
main.c
Normal file
@@ -0,0 +1,163 @@
|
||||
#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 ) {
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user