//Add your code here #include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<ctype.h> #include<errno.h> #define MAXCOM 1000 // max length of command line #define MAX_ARGS 100 // max number of commands to be supported // function for finding ampersand (parallel commands) int findParallelCmds(char* str, char** parallelStr) { int i; // to be used as parallel cmds counter for (i = 0; i < MAX_ARGS; i++) { parallelStr[i] = strsep(&str, "&"); //printf("i value here: %d, cmd: %s\n", i, parallelStr[i]); if (parallelStr[i] == NULL) { break; } } if (i > 1) return i; else { return 0; // returns zero if no pipe is found. } } // function to handle built-in commands // if 0 is returned, it means that none of // the built-in commands was executed int useBuiltInCmds(int argc, char **args) { int noOfBuiltInCmds = 3; int chosenCmd = 0; char* listOfBuiltInCmds[noOfBuiltInCmds]; listOfBuiltInCmds[0] = "exit"; listOfBuiltInCmds[1] = "cd"; listOfBuiltInCmds[2] = "path"; for (int i = 0; i < noOfBuiltInCmds; i++) { if (strcmp(args[0], listOfBuiltInCmds[i]) == 0) { chosenCmd = i + 1; break; } } switch (chosenCmd) { case 1: // exit if (argc > 1) { printf("Error occurs\n"); char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); return 1; } exit(0); case 2: // cd if (argc == 1 || argc > 2) { char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); return 1; } if (chdir(args[1]) != 0) { char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); } return 1; case 3: // path if (argc == 1) { // set PATH to an empty string if no path passed in setenv("PATH", "", 1); } else { char *path = (char*) malloc(255); // allocate initial buffer memory path[0] = '\0'; // initialize the buffer with a null terminator for (int i = 1; i < argc; i++) { if (strlen(path) + strlen(args[i]) > strlen(path)) { path = realloc(path, (strlen(path) + strlen(args[i])) * 2 + 1); } path = strcat(path, args[i]); path = strcat(path, ":"); } path[strlen(path) - 1] = '\0'; // replace the last colon with a null terminator setenv("PATH", path, 1); // set the PATH environment variable //printf("PATH=%s\n", getenv("PATH")); // print the PATH environment variable free(path); } return 1; default: break; } return 0; } // Function where the system command execv is executed void execvArgs(char **args) { // Forking a child pid_t pid = fork(); if (pid == -1) { char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); return; } else if (pid == 0) { char *path = getenv("PATH"); // get the current search path char *p = strtok(path, ":"); // tokenize the search path while (p != NULL) { // allocate memory for the executable path char *exe = malloc(strlen(p) + strlen(args[0]) + 2); // construct the full path to the executable sprintf(exe, "%s/%s", p, args[0]); // check if the executable exists and is executable if (access(exe, X_OK) == 0) { //printf("Found %s\n", exe); // execute the command if (execv(p, args) < 0) { perror("execv"); // exe error occurred } else { return; // exe successfully } //break; //if successful } free(exe); // free the allocated memory p = strtok(NULL, ":"); // get the next token } } else { // waiting for child to terminate wait(NULL); return; } } // function for parsing parallel command words int parseSpaceParallel(char **parallelStr, int **parallelArgsCounter, char ***parallelArgs) { return 1; } // function for parsing single command words int parseSpace(char *str, int argsCounter, char **args) { int i; for (i = 0; i < MAX_ARGS; i++) { args[i] = strsep(&str, " \t"); if (args[i] == NULL) { break; } if (strlen(args[i]) == 0) { i--; } } argsCounter = i; return argsCounter; } // this function executes when the user does not put // any argument after ./wish, meaning they use stdin void takeStdInput(char* str, int argsCounter, char **args, int **parallelArgsCounter, char ***parallelArgs) { while(1) { // print shell prompt to terminal printf("wish> "); char *line = NULL; // buffer to store each line read size_t len = 0; // length of the line read ssize_t read; // number of characters read by getline // take input line by line while ((read = getline(&line, &len, stdin)) != -1) { // remove newline character from command string if (line[strlen(line) - 1] == '\n') { line[strlen(line) - 1] = '\0'; } // copy to pointer str strcpy(str, line); // Free the line buffer free(line); // Reset the line pointer and size for the next line line = NULL; len = 0; break; } // check for parallel cmds char *parallelStr[MAX_ARGS]; int parallelCmdsNum = findParallelCmds(str, parallelStr); if (parallelCmdsNum) { // parallel cmds found, return a number greater than 0 parseSpaceParallel(parallelStr, parallelArgsCounter, parallelArgs); } else { // single cmd found argsCounter = parseSpace(str, argsCounter, args); if (useBuiltInCmds(argsCounter, args) == 0) { //printf("Did not execute with built-in cmd\n"); execvArgs(args); } } } } // function to execute given batch file void takeBatchInput(char *inputFile, char* str, int argsCounter, char **args, int **parallelArgsCounter, char ***parallelArgs) { FILE * fp = fopen(inputFile, "r"); if (fp == NULL) { char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); exit(1); } char *line = NULL; // buffer to store each line read size_t len = 0; // length of the line read ssize_t read; // number of characters read by getline // take input line by line while ((read = getline(&line, &len, fp)) != -1) { // remove newline character from command string if (line[strlen(line) - 1] == '\n') { line[strlen(line) - 1] = '\0'; } // Copy to pointer str strcpy(str, line); // Free the line buffer free(line); // Reset the line pointer and size for the next line line = NULL; len = 0; // check for parallel cmds char *parallelStr[MAX_ARGS]; int parallelCmdsNum = findParallelCmds(str, parallelStr); if (parallelCmdsNum) { // parallel cmds found, return a number greater than 0 parseSpaceParallel(parallelStr, parallelArgsCounter, parallelArgs); } else { // single cmd found argsCounter = parseSpace(str, argsCounter, args); if (useBuiltInCmds(argsCounter, args) == 0) { //printf("Did not execute with built-in cmd\n"); execvArgs(args); } } } } int main(int argc, char *argv[]) { setenv("PATH", "/bin", 1); // initialize shell path to default char str[MAXCOM]; // ptr to store command line input string char *args[MAX_ARGS]; // ptr to a list of arguments int argsCounter = 0; // number of arguments, including the cmd arg char **parallelArgs[MAX_ARGS]; int *parallelArgsCounter[MAX_ARGS]; // If given more than ./wish and a file if (argc > 2) { char error_message[30] = "An error has occurred\n"; write(STDERR_FILENO, error_message, strlen(error_message)); } // If given just ./wish -> prompt input else if (argc == 1) { takeStdInput(str, argsCounter, args, parallelArgsCounter, parallelArgs); } // If given ./wish and a batch file -> read and execute commands in file else { takeBatchInput(argv[1], str, argsCounter, args, parallelArgsCounter, parallelArgs); } return 0; }