#include #include #include #include #include #include #include #include #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; }