#include "cmd.h" #include #include #include #include #include #include "scanner.h" /** * This function instantiates an empty command. */ Command _newCommand() { Command cmd; cmd.capacity = INITIAL_ARRAY_SIZE; cmd.arguments = calloc(INITIAL_ARRAY_SIZE, sizeof(char**)); cmd.numArguments = 0; return cmd; } /** * This function instantiates an empty commandlist. */ CommandList _newCommandList() { CommandList list; list.capacity = INITIAL_ARRAY_SIZE; list.commands = calloc(INITIAL_ARRAY_SIZE, sizeof(Command)); list.numCommands = 0; return list; } /** * This function instantiates an empty chain. */ Chain _newChain() { Chain chain; chain.commands = _newCommandList(); chain.runInBackground = false; return chain; } /** * This function frees a command. * Note that it only frees the char**, not the strings/char*s themselves, as these are part of the TokenList. */ void freeCommand(Command cmd) { free(cmd.arguments); } /** * This function frees a commandlist. * To do so, it frees all commands in .numCommands */ void _freeCommandList(CommandList list) { for (int i=0; i < list.numCommands; i++) { freeCommand(list.commands[i]); } free(list.commands); } // This function frees a chain. void freeChain(Chain chain) { _freeCommandList(chain.commands); } /** * This function inserts a given argument into a given command. * @param cmd the command to insert the argument into. * @param str the argument to be inserted. */ void insertArgument_(Command *cmd, char* str) { //resize arguments pointer if necessary. if (cmd->numArguments >= cmd->capacity) { cmd->arguments = realloc(cmd->arguments, 2*cmd->capacity*sizeof(Command)); cmd->capacity *= 2; } cmd->arguments[cmd->numArguments++] = str; } /** * This function builds a command from a given listPointer. * It expects the current listPointer to be either of type EXECUTABLE of BUILTIN, * followed by any number of OPTIONs. * @param lp the list to read from. * @return a new Command. */ Command buildCommand(List *lp) { Command cmd = _newCommand(); //exit on invalid syntax. This should not be reachable, if our parser is correct. if ((*lp)->type != EXECUTABLE && (*lp)->type != BUILTIN) exit(1); // insert command as first entry insertArgument_(&cmd, (*lp)->t); *lp = (*lp)->next; //insert rest of arguments while (*lp != NULL && (*lp)->type == OPTION) { insertArgument_(&cmd, (*lp)->t); *lp = (*lp)->next; } // execvp() expects a NULL terminated list of options. insertArgument_(&cmd, NULL); //for ease of access we define the command attribute as pointing towards the first arguments entry (which contains the command to be executed) cmd.command = cmd.arguments[0]; return cmd; } /** * This function inserts a command into a given commandList. * @param cmd the Command to insert. * @param List the commandList to insert the command into. */ void _insertCommand(Command cmd, CommandList *list) { //resize commands pointer if necessary. if (list->numCommands >= list->capacity) { list->commands = realloc(list->commands, 2*list->capacity*sizeof(Command)); list->capacity *= 2; } list->commands[list->numCommands++] = cmd; } /** * This function build up one chain of commands from a given listPointer. * It parses one 'unit' of executables, meaning any number of commands+options chained together by pipes and redirects. * It closely models the given grammar for a chain, witth the exception of also parsing the background operator '&'. (and it doesn't like builtins either, we handle those seperately). * @param lp the list to construct a chain from. * @return a new Chain. */ Chain buildChain(List *lp) { Chain chain = _newChain(); Command cmd; //exit on invalid syntax. This should not be reachable, if our parser is correct. if ((*lp) == NULL || (*lp)->type != EXECUTABLE) exit(1); // so long as we have tokens to parse, do so. We exit from within the loop if: // we encounter a syntax error (OPTION, BUILTIN, or FILENAME - these should be consumed as we construct the Chain) or // we encounter a COMPOSITION operator - meaning this chain is complete, and the remainder of lp is to be processed elsewhere. while ((*lp) != NULL) { switch ((*lp)->type) { // finding a command means we must insert it into the commandList. case EXECUTABLE: cmd = buildCommand(lp); _insertCommand(cmd, &chain.commands); break; //finding a chain operator means we want to continue evaluating, so we consume the operator. case PIPELINE: *lp = (*lp)->next; break; // finding a redirect means a bunch of things we haven't thought out yet. case REDIRECT: //TODO: implement redirect break; // finding a background operator means two things: // 1. we must run this chain in the background. // 2. we have reached the end of this chain. case BACKGROUND: chain.runInBackground = true; *lp = (*lp)->next; // finding a COMPOSITION operator indicates we reached the end of this chain. case COMPOSITION: return chain; // finding any of an OPTION, BUILTIN, or FILENAME means a syntax error (and thus a faulty parser)- these should not be reachable. case OPTION: case BUILTIN: case FILENAME: freeChain(chain); exit(1); } } return chain; } /** * This function executes a given command in a child process. * To make this a blocking operation, the parent process will wait until the child exits - simultaneously updating the status variable. * @param cmd the Command to execute. * @param status variable which will contain the exit status of the command. */ void _executeCommand(Command cmd, int* status) { pid_t pid = fork(); switch (pid) { // fork failed. case -1: printf("ERROR: failed to create child!\n"); *status = -1; break; case 0: // child process execvp(cmd.command, cmd.arguments); //this line is only ever reached if execvp fails (for example, when an executable can't be found). exit(127); break; default:; // parent process; wait for child and update status. int stat; wait(&stat); if (WIFEXITED(stat)) { *status = WEXITSTATUS(stat); } } } // TODO: extend to include I/O redirection and background operation /** * This function executes all commands in a given chain. * It updates the status as it does so. * This function is barebones at the moment, but will be expanded to include support for redirection and non-blocking execution. * @param chain the Chain to execute. * @param status variable which will contain the exit status after execution. */ void executeChain(Chain chain, int* status) { for (int i=0; i < chain.commands.numCommands; i++) { _executeCommand(chain.commands.commands[i], status); } } /** * This function executes a BUILTIN. * BUILTINs are pre-defined commands ran within the shell itself, as opposed to executables found in $PATH or $CWD. * @param cmd the BUILTIN to execute. * @param status the return status of the last run command. * @return a boolean which indicates whether to keep running the shell or not. */ #if EXT_PROMPT bool executeBuiltin(Command cmd, int *status, bool *debug) { #else bool executeBuiltin(Command cmd, int *status) { #endif if (!strcmp(cmd.command, "status")) { printf("The most recent exit code is: %d.\n", *status); } else if (!strcmp(cmd.command, "exit")) { return false; } else if (!strcmp(cmd.command, "true")) { *status = 0; } else if (!strcmp(cmd.command, "false")) { *status = 1; #if EXT_PROMPT } else if (!strcmp(cmd.command, "debug")) { *debug = ! *debug; printf("Toggled debug to %d.\n", *debug); } else if (!strcmp(cmd.command, "cd")) { if (cmd.numArguments == 2) { char *PWD = getenv("HOME"); *status = chdir(PWD); } else { *status = chdir(cmd.arguments[1]); } #endif }// can be expanded by growing the if/else chain. return true; } /** * This function prints one Command in json. * Intended to be used in tandem with printChain and printCommandList. * @param cmd the command to print. */ void _printCommand(Command cmd) { printf("{\n \"arguments\": [\n"); for (int i=0; i < cmd.numArguments; i++) { printf(" \"%s\"", cmd.arguments[i]); if (i < cmd.numArguments-1) printf(",\n"); } printf("\n ],\n \"command\": \"%s\",\n \"capacity\": %d,\n \"numArguments\": %d\n }", cmd.command, cmd.capacity, cmd.numArguments); } /** * This function prints a commandList in json. * Intended to be used with printCommand and printChain. * @param list the list to print. */ void _printCommandList(CommandList list) { printf("{\n \"commands\": [\n "); for (int i=0; i < list.numCommands; i++) { _printCommand(list.commands[i]); if (i < list.numCommands-1) printf(", "); } printf("],\n \"capacity\": %d,\n \"numCommands\": %d\n }", list.capacity, list.numCommands); } /** * This function prints a Chain in json. * Intended to be used with printCommand and printCommandList. * @param chain the chain to print. */ void printChain(Chain chain) { printf("{\n\"commandList\": "); _printCommandList(chain.commands); printf(",\n\"runInBackground\": %d\n}\n", chain.runInBackground); }