#include #include #include #include #include #include #include #include "msh.h" #define clear() printf("\033[H\033[J") #define MATHLEN 10 union pipe { int fileDesc[2]; struct { int read; int write; }; }; static char *builtinFunctions[] = { "exit", "author", "cd" }; int (*builtinFunc[]) (char **) = { &builtinExit, &builtinAuthor, &builtinCD }; int builtinExit() { exit(EXIT_SUCCESS); } int builtinAuthor() { static const char* message = "Author: Spencer\n" "Description: A shell to promote math!\n" "Ask for the source code!\n" "\n"; printf("%s", message); return 0; } int builtinCD(char **dir) { int status = 0; if (dir != NULL) { if (chdir(dir[1]) != 0) { status = 1; printf("bash: cd: error\n"); } } else { status = 1; printf("bash: cd: error\n"); } return status; } void failedTest() { static const char* message = "\n" "How can you expect to execute commands without knowing any math???\n" "\n"; printf("%s", message); return; } int runCommand(char *command) { int status = 0; pid_t pid; pid = fork(); if (pid == 0) { // Child process if(execlp("bash", "bash", "-c", command, NULL) == -1) { exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } else if (pid < 0) { printf("Error forking...\n"); status = 1; } else { // Parent process do { waitpid(pid, &status, WUNTRACED); } while(!WIFEXITED(status) && !WIFSIGNALED(status)); } return status; } char **splitCMD(char *line) { size_t size = 8; size_t pos = 0; char **args = calloc(0, size * sizeof(char*)); char *arg = NULL; if(line == NULL) { *args=NULL; return args; } arg = strtok(line, " \t\r\n\a"); while (arg != NULL) { args[pos] = arg; pos++; if (pos >= size) { size += 1; args = realloc(args, size * sizeof(char*)); } arg = strtok(NULL, " \t\r\n\a"); } args[pos] = NULL; return args; } int execCommand(char *command) { char **commandARGS = splitCMD(command); for (size_t i = 0; i < (sizeof(builtinFunctions) / sizeof(char *)); i++) { if (strcmp(commandARGS[0], builtinFunctions[i]) == 0) { return (*builtinFunc[i])(commandARGS); } } return runCommand(command); } void initShell() { FILE *fd; clear(); using_history(); // Setting random time seed srand(time(0)); fflush(stdout); runCommand("uname -a"); fd = fopen("/etc/motd", "r"); if (fd != NULL) { char c; c = fgetc(fd); printf("\n"); while (c != EOF) { printf("%c", c); c = fgetc(fd); } fclose(fd); } printf("\n"); return; } char *getPrompt() { pid_t pid; union pipe input, output; FILE *outputFileD; size_t len = 0; int status = 0; static char *prompt = NULL; static size_t promptCap = 0; pipe(input.fileDesc); pipe(output.fileDesc); pid = fork(); if (pid == 0) { // Child process close(input.write); close(output.read); dup2(input.read, STDIN_FILENO); dup2(output.write, STDOUT_FILENO); close(STDERR_FILENO); // Do not print errors to the screen execlp("bash", "bash", "-i", "-c", "\ ver=$(bash --version | head -n 1 | awk \'{print $4}\' | grep -o \".\\..\");\ check_ver=$(echo -e \"${ver}\n4.3\" | sort -V | head -n 1);\ if [[ \"${ver}\" == \"${check_ver}\" ]]; then\ echo \"[${USER}@${HOSTNAME} ${PWD##*/}]$ \";\ else\ echo \"${PS1@P}\";\ fi\ ", NULL); } else if (pid < 0) { // Error forking printf("Error forking\n"); } else { // Parent process close(input.read); close(output.write); outputFileD = fdopen(output.read, "r"); len = getline(&prompt, &promptCap, outputFileD); if (prompt[len - 1] == '\n') { prompt[len - 1] = '\0'; } fclose(outputFileD); do { waitpid(pid, &status, WUNTRACED); } while(!WIFEXITED(status) && !WIFSIGNALED(status)); } return prompt; } int randomNum(int limit) { int num = 0; if (limit != 0) { num = rand()%limit +1; } else { num = rand(); } return num; } int mathTest() { int status = 0; int numAnswer = 0; int num1 = randomNum(MATHLEN); int num2 = randomNum(MATHLEN); char *numLine = NULL; printf("%d + %d = ", num1, num2); numLine = readline(""); sscanf(numLine, "%d", &numAnswer); #ifdef RELEASEBUILD printf("Calculating"); for (int i = 0; i < 3; i++){ printf("."); fflush(stdout); sleep(2); } printf("\n"); #endif if ((num1 + num2) != numAnswer) { // If wrong answer status = 1; } free(numLine); return status; } void startShell() { char *line = NULL; int status = 0; while ((line = readline(getPrompt())) != NULL) { if (strlen(line) != 0) { // Checking if prompt returns empty status = mathTest(); if (status == 0) { // Passed math test add_history(line); execCommand(line); } else { // Failed math test failedTest(); } } free(line); } } void handleBreak(){ signal(SIGINT, handleBreak); printf("\n"); rl_on_new_line(); rl_replace_line("", 0); rl_redisplay(); } int main() { initShell(); signal(SIGINT, handleBreak); startShell(); exit(EXIT_SUCCESS); }