204 lines
6.0 KiB
C
204 lines
6.0 KiB
C
#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:
|
|
// * signal handling
|
|
// * builtins - 'kill'
|
|
// * simple command history
|
|
// * run scripts with argv[]
|
|
// * redirect stderr with the n> operator
|
|
// * allow multiple redirects (turn chain.in and chain.out into char**)
|
|
//
|
|
// Big rewrite: create a 'shell' struct which is the actual program to run
|
|
// this struct would take care of things like command history, signal handling, etc
|
|
|
|
/**
|
|
* 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 command).
|
|
* @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;
|
|
}
|
|
|
|
|
|
#if EXT_PROMPT
|
|
int execute_script(char* script) {
|
|
FILE* file = fopen(script, "r");
|
|
if (file == NULL) {
|
|
printf("Error: provided Scriptfile, '%s', does not exist or is inaccessible!\n", script);
|
|
return 1;
|
|
}
|
|
char line[512];
|
|
|
|
while (fgets(line, sizeof(line), file)) {
|
|
printf("%s", line);
|
|
//TODO: continue working here on the script functionality later, after the Refactor
|
|
}
|
|
fclose(file);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* 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[]) {
|
|
|
|
#if EXT_PROMPT
|
|
if (argc >= 2) {
|
|
if (argc > 2) {
|
|
printf("Error: unexpected number of arguments!\n");
|
|
return 0;
|
|
} else {
|
|
if (execute_script(argv[1])) return 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
char *inputLine;
|
|
List tokenList;
|
|
int status = 0;
|
|
bool do_loop = true;
|
|
#if EXT_PROMPT
|
|
bool debug = false;
|
|
char cwd[128], *usr, prompt[256];
|
|
#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) snprintf(prompt, 200*sizeof(char), "\x1b[33m%s \x1b[36m%s \x1b[32m>\x1b[0m ", usr, cwd);
|
|
if ( status) snprintf(prompt, 200*sizeof(char), "\x1b[33m%s \x1b[36m%s \x1b[31m>\x1b[0m ", usr, cwd);
|
|
inputLine = readInputLine(prompt);
|
|
#else
|
|
inputLine = readInputLine();
|
|
#endif
|
|
|
|
// 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;
|
|
}
|