/*---------------------------------------------------------*\
| DESCRIPTION:  Major Assignment 02 - Main file             |
| CLASS:        CSCE 3600.002                               |
| GROUP 3:      David C., Ajelet S., Blake A., and Alex D.  |
| FILE AUTHORS: Alex D.                                     |
| DATE:         November 15, 2021                           |
\*---------------------------------------------------------*/

/*"must be invoked exactly like newshell [batchfile]" means the compiled program must be named
"newshell", where batchfile is an optional file of commands.*/
//Referencing this to get started: https://www.geeksforgeeks.org/making-linux-shell-c/ #include <stdio.h> #include <stdlib.h> //For system("clear"). #include <string.h> //For strcpy and strcmp #include <signal.h> #include <unistd.h> //For execvp() #include <sys/types.h> #include <sys/wait.h> //Both of these for wait() #include <stdbool.h> #include "alias.h" // header for alias function #include "shell.h" //Includes all function prototypes and important constants #include "history.h" /*The number of built-in commands.*/ #define NUM_COMMANDS 5 //User max input should be 512 #define MAX 512 /*The built-in commands for the shell.*/ const char* shellCommands[NUM_COMMANDS] = {"alias", "cd", "exit", "myhistory", "path"}; /*Where we're getting input from.*/ FILE* instream; /*Where we're sending output to.*/ FILE* outstream; /*Where we're printing errors to.*/ FILE* errstream; //function declaration and char* for path function void pathFunction(char *args[], int arg_count); char *PATH; const char *ORIGINALPATH; /*The indicator if the program should terminate. 0 (false) means an exit command was not read.*/ int exitFlag = 0; //The main shell engine. int main(int argc, char* argv[]) { //If too many args used to run the shell... if (argc > 2) { //Exit as per the assignment instructions. fprintf(stderr, "Err: Too many arguments for command \"%s\" - unexpected argument \"%s\".\n", argv[0], argv[2]); return 0; } PATH = (char*) malloc(1024); memset(PATH, '\0', sizeof(PATH)); //sets original path ORIGINALPATH = getenv("PATH"); //saves the original path strcpy(PATH, ORIGINALPATH); //sets the path as path setenv("PATH", PATH, 1); char input[MAX_COMMAND_LENGTH]; char* prompt = "Janky Shell > "; /*Holds all the individual commands parsed from the input line. command - The full c-string of the command. Does not contain a newline. args - The 2D c-string array to hold the command and its flags individually. size - The number of elements in args. Currently unused - may or may not actually be needed.*/ struct Command commandTable[MAX_NUM_COMMANDS]; /*Holds piped commands other than the first (i.e., cmd | pipeCommands[0] | pipeCommands[1]).*/ struct Command pipeCommands[2]; int amtCommands = 0; clearScreen(); registerSignalHandlers(); //If batch mode... if (argv[1] != NULL) { //Open the batch file in read mode. I am assuming we're not saving anything back into the file. instream = fopen(argv[1], "r"); outstream = stdout; errstream = stderr; if (instream == NULL) { fprintf(errstream, "Err: Could not open batch file \"%s\". Exiting newshell.\n", argv[1]); return 0; } while (!exitFlag) { //If the input was a valid length and we're not at the end of the file... if (handleInput(input, instream) && !feof(instream)) { //Echo the line back to the user as per the glorious instructions. fprintf(outstream, "\necho: %s\n", input); amtCommands = parseAndExecuteCommand(input, commandTable, pipeCommands); //Clear the args of each used command to prevent execvp from trying to use blank '' flags on the next run. clearCommandTable(commandTable, amtCommands); clearCommandTable(pipeCommands, amtCommands); } if (feof(instream)) { exitFlag = 1; break; } //Else, re-loop for more input. } fclose(instream); } //Else, run in interactive mode. else { instream = stdin; outstream = stdout; errstream = stderr; fprintf(outstream, "WELCOME TO HELL, TELL ME YOUR WISHES\n\n"); while (!exitFlag) { fprintf(outstream, "%s", prompt); //If the input was a valid length... if (handleInput(input, instream)) { amtCommands = parseAndExecuteCommand(input, commandTable, pipeCommands); //Clear the args of each used command to prevent execvp from trying to use blank '' flags on the next run. clearCommandTable(commandTable, amtCommands); clearCommandTable(pipeCommands, amtCommands); } //Else, re-loop for more input. } } //reset path back to original before exiting program setenv("PATH", ORIGINALPATH, 1); free(PATH); return 0; } /*Reads input from the user. Valid input is added to the history and returned using the pointer parameter. Newlines are removed. An error is printed if the input is longer than MAX_COMMAND_LENGTH. input - The pointer to store the raw user input in. Must be able to hold MAX_COMMAND_LENGTH total characters. instream - The file stream to get input from, like stdin. return - Returns 1 (true) if the input was a valid length and 0 if otherwise.*/ int handleInput(char* input, FILE* instream) { //Reference: https://www.cplusplus.com/reference/cstdio/fgets/ //Reads MAX_COMMAND_LENGTH-1 characters (including \n), then appends \0. So, "[510 chars]\n\0" //fgets won't indicate if it reads more than MAX_COMMAND_LENGTH characters. //Make a larger buffer to catch this so we can handle the error. char buffer [MAX_COMMAND_LENGTH+1]; //"[511]\n\0" fgets(buffer, MAX_COMMAND_LENGTH+1, instream); if (strlen(buffer) > MAX_COMMAND_LENGTH) { fprintf(errstream, "Err: Input exceeded %d characters.", MAX_COMMAND_LENGTH); return 0; } else if (strlen(buffer) != 0) { //Remove newlines so we don't have to check for them in string comparisions later. int i; for (i = 0; buffer[i] != '\n'; i++); buffer[i] = '\0'; strcpy(input, buffer); addToHistory(input); return 1; } else //No input. return 0; } /*Parses the raw user input and determines how to execute the commands. input - The raw user input with the newline removed. commandTable - The array of Commands to hold each command extracted from the input. extractedPipeCommands - The array to hold the commands for the read end of a piped command, if present. return - Returns the number of commands extracted.*/ int parseAndExecuteCommand(char* input, struct Command* commandTable, struct Command* extractedPipeCommands) { if (strlen(input) == 0) return 0; char pipe[3][MAX_COMMAND_LENGTH]; int numCommands = separateCommands(input, commandTable); int isPiped; //For each semicolon-separated command... //(If separateCommands returns an error, this does not run.) for (int i = 0; i < numCommands; i++) { //Find any pipes in each command. isPiped = findPipe(commandTable[i].command, pipe); if (isPiped) { strcpy(commandTable[i].command, pipe[0]); strcpy(extractedPipeCommands[0].command, pipe[1]); //Extract the args from each end (pipedCommands). extractArgs(pipe[0], commandTable[i].args); extractArgs(pipe[1], extractedPipeCommands[0].args); if (isPiped == 1) { //Execute the read end (0) of the piped command using commandTable[i].args //and the write end (1) using the extractedPipedCommands[0].args. executePipedCommand2(commandTable[i].args, extractedPipeCommands[0].args); } else { //TODO: This still needs work. strcpy(extractedPipeCommands[1].command, pipe[2]); extractArgs(pipe[2], extractedPipeCommands[1].args); executePipedCommand3(commandTable[i].args, extractedPipeCommands[0].args, extractedPipeCommands[1].args); } } else { extractArgs(commandTable[i].command, commandTable[i].args); if (executeShellCommand(&commandTable[i]) != 1) { //If a built-in command does not run, execute the system command. executeSystemCommand(commandTable[i].args); } } } return numCommands; } /*Splits the input by semicolons and places each portion into the splitCommands array. Returns the number of commands split (the size of splitCommands). input - The raw user input c-string with the newline removed. splitCommands - The array of Commands to store the input after splitting. The command will be stored in the "command" field. Assumed to be of size 512. return - Returns the number of elements in splitCommands after finding semicolons. This is the same as number of semicolons found + 1. If the input length is invalid, -1 is returned instead.*/ int separateCommands(char* input, struct Command* splitCommands) { if (strlen(input) == 0) return -1; //Make a copy of the input since strsep will change it. char strsepIsWeird[MAX_COMMAND_LENGTH]; strcpy(strsepIsWeird, input); //strsep requires a pointer to a pointer (not a pointer to an array): https://stackoverflow.com/questions/55543717/why-doesnt-strsep-work-with-pointers-to-the-stack char* inputCopy = strsepIsWeird; //While strsep doesn't return NULL, iterate through the command table and copy the strings separated by strsep. int size = -1; do { size++; if (inputCopy == NULL) break; //Copy the individual commands into "command" and split it by spaces later. strcpy(splitCommands[size].command, strsep(&inputCopy, ";")); } while (splitCommands[size].command != NULL); return size; } /*Separates the command and arguments into their own arrays. Spaces are removed, but all other characters are kept. This does not handle identifying pipes or commands separated with ;. command - A copy of the command to extract args from after handling semicolons, pipes, and newlines. This will get destroyed! commandArgs - The array of c-strings to hold the extracted command, arguments, and special characters. Assumed to be 256 x 512. return - Returns the number of arguments extracted.*/ int extractArgs(char* command, char** commandArgs) { if (strlen(command) == 0) return 0; //TODO: Maybe check the command for aliases here, while it's whole, and then replace them with the real command? //Do we need to worry about semicolons being included in aliases?? expandAliases(command); //Note to self: Don't to this - strsep will return a pointer into the original array, //which is currently a local variable. Get the string to split from the same place as //commandArgs to avoid freaky brain-melting memory bugs. //Make a copy of the command since strsep will change it. //char strsepIsWeird[MAX_COMMAND_LENGTH]; //strcpy(strsepIsWeird, command); //strsep requires a pointer to a pointer (not a pointer to an array): https://stackoverflow.com/questions/55543717/why-doesnt-strsep-work-with-pointers-to-the-stack //char* inputCopy = strsepIsWeird; //While strsep has not eaten through the input, split the input by spaces and delete spaces. int i = -1; while (command != NULL) { i++; commandArgs[i] = strsep(&command, " "); if (commandArgs[i] == NULL) break; else if (strlen(commandArgs[i]) == 0) i--; } i++; //If we ended on a space... if (commandArgs[i] != NULL && strlen(commandArgs[i]) == 0) { commandArgs[i] = NULL; //Add one since that loop will have ended on i--. return i + 1; } else return i; /*int i = -1; do { i++; if(inputCopy == NULL) break; commandArgs[i] = strsep(&inputCopy, " "); if (commandArgs[i] == NULL) break; else if (strlen(commandArgs[i]) == 0) //If we got a space, the string length will be 0. Reset the counter and try again to write a non-space to this index. i--; } while (1);*/ } //TODO void expandAliases(char* input) { //Expanding an alias could cause the input to become longer than MAX_COMMAND_LENGTH grrr //How do how do } /*Determines if the command is pipped and if so, returns the ends via pippedCommands. command - The single command to find the pipe in, after handling semicolons and newlines. pipedCommands - The array to store the pipped commands into. Must be able to hold 3 c-strings. If no pipe exists, one or both of these may be NULL. return - Returns 1 (true) if the command contains a pipe and 0 if not.*/ int findPipe(char* command, char pipedCommands[][512]) { //Get the index of the |, if any. int i; for (i = 0; command[i] != '|'; i++) { //If we reached the end without finding a pipe... if (command[i] == '\0') return 0; } //Else, we found a pipe at index i. //Move past the | index. i++; //Copy the command from the start through to the |. strncpy(pipedCommands[0], command, i); //Manually terminate the copied string, overwriting the |. pipedCommands[0][i-1] = '\0'; //Copy the command from past the | to the end. strcpy(pipedCommands[1], &command[i]); //Check for a second pipe. for ( ; command[i] != '|'; i++) { if (command[i] == '\0') return 1; } i++; pipedCommands[1][i-1] = '\0'; strcpy(pipedCommands[2], &command[i]); return 2; } //Built-in commands don't need piping or redirection. /*Determines if the command is a built-in shell command and if so, executes it. commands - The array with the command and arugments to execute. command[0] is used as the command: the remainder are considered flags. return - Returns 1 (true) if a built-in command ran and 0 if not.*/ int executeShellCommand(struct Command* command) { //Error handling here is a mess - basically, return 0 if a system command should try to run after this, and print your own errors. if ((*command).args[0] == NULL) return -1; //Yeet //Iterate to get the index of the command to use. int i; int count = 0, j; char s[100]; for (i = 0; i < NUM_COMMANDS; i++) { if (strcmp((*command).args[0], shellCommands[i]) == 0) break; } //If the loop runs all the way without matching a built-in command, i will be 5. //The default case in the switch will then run. switch(i) { //alias case 0: //TODO: Add function(s) to execute the alias command. //David, (*command).command should give your function what it needs, though you'll obviously need an alias structure saved in this file. //I'd put the structure definition in shell.h, or separate header and #include it here. //You'll also need to edit remove your code that removes the newline from the input - it's already gone alias_exe(); return 1; //cd case 1: //TODO: Add function(s) to execute the cd command. return 1; //exit case 2: exitFlag = 1; return 1; //myhistory case 3: return executeMyHistory((*command).args, outstream, errstream); //path case 4: for (j = 0; (*command).args[j] != NULL; j++) //used to get count of string from arguments sent { if ((*command).args[j] != NULL){ count++; } } pathFunction((*command).args, count); //path function given arguments and arg count to decide what to print for user return 1; default: return 0; } } /*Executes the system command. args - The array with the command (args[0]) and the arguments (args[1], args[2]...).*/ void executeSystemCommand(char** args) { //Fork a new process to handle executing the command. pid_t processID = fork(); //If the fork failed... if(processID == -1) { fprintf(errstream, "Err: Could not fork a child. System command \"%s\" not executed.\n", args[0]); return; } //Else, if the fork was successful and we're in the child... else if (processID == 0) { //TODO: For signal handling, set process group = pid and set process to foreground. if (execvp(args[0], args) < 0) { fprintf(errstream, "Err: System command \"%s\" failed to execute.\n", args[0]); } exit(0); } //Else, if the fork was successful and we're in the parent.... else { //Wait for the child to finish running. wait(NULL); return; } } /*Executes a single pipe command (i.e., command1 | command2). command1Args - The command (arg[0]) and arguments (args[1], args[2]...) for the write end of the pipe. command2Args - The command (arg[0]) and arguments (args[1], args[2]...) for the read end of the pipe.*/ void executePipedCommand2(char** command1Args, char** command2Args) { int pipeFD[2]; pid_t writeProcess, readProcess; const int readEnd = 0; const int writeEnd = 1; pid_t wpid; int status; if (pipe(pipeFD) < 0) { fprintf(errstream, "Err: Could not create pipe.\n"); return; } //Create processes for the write end (first part) of the pipe. writeProcess = fork(); //If the fork failed... if (writeProcess < 0) { fprintf(errstream, "Err: Could not fork a child for the write end of the pipe for system command \"%s\".\n", command1Args[0]); return; } //Else, if we're now in the child... else if (writeProcess == 0) { //TODO: For signal handling, set process group = pid and set process to foreground. close(pipeFD[readEnd]); dup2(pipeFD[writeEnd], fileno(outstream)); close(pipeFD[writeEnd]); if (execvp(command1Args[0], command1Args) < 0) { fprintf(errstream, "Err: System command \"%s\" failed to execute on write end of pipe.\n", command1Args[0]); } exit(0); } //Else, if we're in the parent... else { //Wait for the previous child to finish. do { wpid = waitpid(writeProcess, &status, WUNTRACED); }while(!WIFEXITED(status) && !WIFSIGNALED(status)); if (WIFEXITED(status)) { //Create process for the read end (second part) of the pipe. readProcess = fork(); //If the fork failed... if (readProcess < 0) { fprintf(errstream, "Err: Could not fork a child for the read end of the pipe for system command \"%s\".\n", command2Args[0]); return; } else if (readProcess == 0) { //TODO: For signal handling, set process group = pid and set process to foreground. close(pipeFD[writeEnd]); dup2(pipeFD[readEnd], fileno(instream)); close(pipeFD[readEnd]); if (execvp(command2Args[0], command2Args) < 0) { fprintf(errstream, "Err: System command \"%s\" failed to execute on read end of pipe.\n", command2Args[0]); } exit(0); } //Else, if we're still in the original parent from the main shell... else { close(pipeFD[readEnd]); close(pipeFD[writeEnd]); //Wait for the last child process to finish executing. do { wpid = waitpid(readProcess, &status, WUNTRACED); } while (!WIFEXITED(status) && !WIFSIGNALED(status)); } } } } /*Executes a doubly piped command (i.e., command1 | command2 | command3). command1Args - The command (arg[0]) and arguments (args[1], args[2]...) for the start of the pipe. command2Args - The command (arg[0]) and arguments (args[1], args[2]...) for the middle of the pipe. command3Args - The command (arg[0]) and arguments (args[1], args[2]...) for the end of the pipe.*/ void executePipedCommand3(char **command1Args, char ** command2Args, char** command3Args) { int pipeFD[2]; pid_t processID; int inputFD = fileno(instream); const int readEnd = 0; const int writeEnd = 1; char** commands[4] = {command1Args, command2Args, command3Args, NULL}; int i = 0; while (commands[i] != NULL) { if (pipe(pipeFD) < 0) { fprintf(errstream, "Err: Could not create pipe.\n"); return; } processID = fork(); if (processID < 0) { fprintf(errstream, "Err: Could not fork a child for the write end of the pipe for system command \"%s\".\n", command1Args[0]); return; } //Else, if we're in the child... else if (processID == 0) { //TODO: For signal handling, set process group = pid and set process to foreground. dup2(inputFD, fileno(instream)); if (commands[i+1] != NULL) { dup2(pipeFD[writeEnd], fileno(outstream)); } close(pipeFD[readEnd]); if (execvp(commands[0][0], commands[0]) < 0) { fprintf(errstream, "Err: System command \"%s\" failed to execute on read end of pipe.\n", command2Args[0]); } exit(0); } //Else, if we're in the parent... else { wait(NULL); close(pipeFD[writeEnd]); inputFD = pipeFD[readEnd]; i++; } } } /* To handle signals, we need A unique groupID for our shell A unique groupID for each process created by our shell (easier) The easiest way to make a unique process group is by copying the pid.*/ //TODO: Ajelet: Add handlers for all signals here and change processes group IDs in the execute functions above (see TODOs). /*Initializes / registers custom signal handlers for the main shell engine.*/ void registerSignalHandlers() { signal(SIGINT, sigint_handler); signal(SIGTSTP, sigtstp_handler); return; //This will (maybe?) work by having the main shell catch the signal and manually pass it to the sub-processes. //I think this works by passing the signal to the foreground process, which we'll set in the execute functions (see TODOs). //Ambiguous: If no other processes are running, should our shell accept the signal for itself? //Add handlers for each type of singal we need to pass to the processes, like Ctrl-C and Ctrl-Z //signal(SIGINT, sigint_handler); //Make sure the function prototype is added to shell.h //I'd also use fprintf(errstream, "Error message.") so that errors wind up in the right output stream. //We can't guarantee we're working with stderr/stdout/stdin due to batch mode. //Also remove your main, por favor. } /*Resets all used elements to NULL. The table is assumed to be MAX_NUM_COMMANDS long. In actuality, this just clears the second command argument so execvp() stops thinking it's not NULL despite being blank.*/ void clearCommandTable(struct Command* commandTable, int amtCommands) { for (int i = 0; i < amtCommands && i < MAX_NUM_COMMANDS; i++) { commandTable[i].args[1] = NULL; } } /*Clears the screen without using system("clear").*/ void clearScreen() { pid_t processID = fork(); if (processID == -1) { //Not throwing an error 'cause it's not a big deal if this fails. return; } //If this is the child... if (processID == 0) { char* args[] = {"", NULL}; execvp("clear", args); exit(0); } //Else, wait in the parent for the child to finish executing. else { wait(NULL); } } //this is the path function that takes an argument or more, and updates the path as requested by the user void pathFunction(char *args[], int arg_count) { //printf("%s\n",args[1]); if(arg_count == 1) { //print current path printf("%s\n", PATH); }else if(arg_count == 3) { if(strcmp(args[1], "+") == 0) { //append to path name //printf("worked"); strcat(PATH, ":"); if(strcmp(args[2], "$HOME") == 0) { //append the home directory char *home = getenv("HOME"); strcat(PATH, home); }else if(args[2][1] == '~') { //append home directory and any others char *home = getenv("HOME"); strcat(PATH, home); strcat(PATH, strchr(args[2], '/')); }else { //append something else strcat(PATH, args[2]); } setenv("PATH", PATH, 1); printf("Updated path: %s\n", PATH); //print new updated path }else if(strcmp(args[1], "-") == 0) { //remove path char *pathName[MAX]; //create char* for current path and new path, and allocate memory for them char *newPath; newPath = (char*) malloc(1024); memset(newPath, '\0', sizeof(newPath)); int i = 0; //split path into different names char *strings = strtok(PATH, ":"); while(strings != NULL) { //keep getting path name while string is not empty pathName[i] = strings; strings = strtok(NULL, ":"); i++; } pathName[i] = NULL; //add NULL terminator //find path name and remove it for(int j = 0; j < i; j++) { if(strcmp(pathName[j], args[2]) == 0) { pathName[j] = NULL; if(j == i - 1) { i--; } } } //redo path for(int j = 0; j < i - 1; j++) { if(pathName[j] != NULL) { strcat(newPath, pathName[j]); strcat(newPath, ":"); } } strcat(newPath, pathName[i-1]); //take off the $ from path name strcpy(PATH, newPath); setenv("PATH", PATH, 1); //set the new path printf("Updated path: %s\n", PATH); //print new path free(newPath); } }else { //prints error message fprintf(stderr, "Error: Please try again\n"); } } //Alias functions //pointer to a heap allocated alias struct // contains a copy of aliases and assigned commands alias_ptr_t alias_create(char* name, char* A_command) { alias_ptr_t new_alias_ptr = (alias_ptr_t) malloc(sizeof(alias_t)); *new_alias_ptr = (alias_t) { .name = name, .A_command = A_command, .next = NULL }; return new_alias_ptr; } // remove destroy and free are all responsible functions to remove all or a particular alias //stored in the linked list---------------------------------------------------------------------- // frees the particular alias struct void alias_free(alias_ptr_t alias_ptr) { //as long as the list containing the name and the command are not empty, remove the pointer from the name and the commands if(alias_ptr != NULL) { free(alias_ptr->name); free(alias_ptr->A_command); //sets the pointer back to its original state *alias_ptr = (alias_t) { .name = NULL, .A_command = NULL, .next = NULL }; free(alias_ptr); } } // frees the entire alias data structure with the // passed argument as the head of the list using recursion alias_ptr_t alias_destroy(alias_ptr_t alias_ptr) { if(alias_ptr) { alias_destroy(alias_ptr->next); alias_free(alias_ptr); } return NULL; } // removes elements from the alias list that have a matching name alias_ptr_t alias_remove(alias_ptr_t alias_ptr, const char* name) { if(alias_ptr == NULL) { // empty list return NULL; } if(strcmp(alias_ptr->name, name) == 0) { // first element matches alias_ptr_t next = alias_ptr->next; alias_free(alias_ptr); return next; } // removing from the middle to the end of the list alias_ptr_t iterator = alias_ptr; //while not empty search for repeated aliases until each alias is freed or until the list is empty while(iterator->next != NULL) { if(strcmp(iterator->next->name, name) == 0) { alias_ptr_t next = iterator->next->next; alias_free(iterator->next); iterator->next = next; break; } iterator = iterator->next; } return alias_ptr; } //-------------------------------------------------------------------------------- // adds a new alias and command pair to the list // returns the new head of the list of aliases alias_ptr_t alias_add(alias_ptr_t alias_ptr, const char* name, const char* A_command) { alias_ptr = alias_remove(alias_ptr, name); alias_ptr_t new_alias_ptr = alias_create(strdup(name), strdup(A_command)); if(alias_ptr == NULL) { // list was empty return new_alias_ptr; } // end of the alias list alias_ptr_t end_ptr = alias_ptr; while(end_ptr->next) end_ptr = end_ptr->next; // adding new_alias_ptr to the end end_ptr->next = new_alias_ptr; return alias_ptr; } //displays the entire alias data structure void alias_display(const alias_ptr_t alias_ptr) { if(alias_ptr) { printf("%s=\"%s\"\n", alias_ptr->name, alias_ptr->A_command); alias_display(alias_ptr->next); } } // searches for matching name, returns null if none found char* alias_search(const alias_ptr_t alias_ptr, const char* name) { if(alias_ptr == NULL) { // not found return NULL; } if(strcmp(alias_ptr->name, name) == 0) { // match found return alias_ptr->A_command; } // search in the rest return alias_search(alias_ptr->next, name); } // executes non-alias-prefixed commands using system void execute_other_command(char* A_command, const alias_ptr_t alias_ptr)//keeps pointers fixed with current values { char* search = alias_search(alias_ptr, A_command); if(search != NULL) { A_command = search; } system(A_command); } // executes alias commands alias_ptr_t execute_alias_command(char* A_command, alias_ptr_t alias_ptr) { bool incorrect_usage = false;//initialize incorrect usage as false //if the command line reads alias as the first string , continue to read next string argument if(strncmp(A_command, "alias -", strlen("alias -")) == 0) { // alias with options if(A_command[7] == 'c')//if c is used, execute corresponding function { // remove all aliases alias_ptr = alias_destroy(alias_ptr); } else if(A_command[7] == 'r')//if r is used, execute corresponding function { // remove a single alias char alias_name[BUFFER_SIZE]; sscanf(A_command, "alias -r %s", alias_name); alias_ptr = alias_remove(alias_ptr, alias_name); } else { incorrect_usage = true;//if nothing is passed or read after "-" command, sets incorrect usage to true triggering how-to for user } } else if(A_command[5] != '\0') { // set alias char* alias_name; char* alias_command; // start of alias name alias_name = A_command + 6; //if statements that insure that the format of the command is correct else it triggers incorrect usage to prompt the user with the how-to // finds assignment to set up new alias to the corresponding command char* iterator = alias_name + 1; bool assignment_found = false; while(*iterator != '\0') { if(*iterator == '=') { assignment_found = true;// break; } ++iterator; } if(assignment_found) { *iterator = '\0'; // replacing assignment operator with '\0' ++iterator; // quote at start of alias command if(*iterator == '\'') { alias_command = ++iterator; // finding ending quote after alias command bool quote_found = false; while(*iterator != '\0') { if(*iterator == '\'') { quote_found = true; break; } ++iterator; } if(quote_found) { *iterator = '\0'; // replacing quote with '\0' // adding alias alias_ptr = alias_add(alias_ptr, alias_name, alias_command); } else { incorrect_usage = true; } } else { incorrect_usage = true; } } else { incorrect_usage = true; } } else { // display aliases alias_display(alias_ptr); } // displays information on how to use alias correctly if user incorrectly uses the command for alias if(incorrect_usage) { // incorrect usage puts("Incorrect usage."); puts(ALIAS_USAGE); } return alias_ptr; } //executes alias int alias_exe() { //sets both ptrs, ptr is the copy to null // declaring the alias alias_ptr_t alias_ptr = NULL; // declaring the input buffer char A_command[BUFFER_SIZE]; // shell loop for(;;) { // reading in the command if(fgets(A_command, BUFFER_SIZE, stdin) == NULL) { break; } A_command[strlen(A_command) - 1] = '\0'; // if beginning starts with alias go through execution to make sure the format is correct //other wise if it isnot an alias command, execute other shell command if(strncmp(A_command, "alias", strlen("alias")) == 0) { alias_ptr = execute_alias_command(A_command, alias_ptr); } else { execute_other_command(A_command, alias_ptr); } } // destroying the alias alias_ptr = alias_destroy(alias_ptr); return 0; }