#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "msh.h" #define clear() printf("\033[H\033[J") static char *prompt = NULL; static bool skipMath = false; static size_t mathLen = 100; static size_t commandRun = 0; static char *mathOperation = "+-"; union pipe { int fileDesc[2]; struct { int read; int write; }; }; static char *builtinFunctions[] = { "exit", "author", "cd", "nomorenumbers" }; int (*builtinFunc[]) (char **) = { &builtinExit, &builtinAuthor, &builtinCD, &builtinNMN }; int builtinExit(char **args) { #if RL_READLINE_VERSION < 0x0602 rl_clear_history(); #else clear_history(); #endif free(args); exit(EXIT_SUCCESS); } int builtinAuthor(char **args) { static const char* message = "Author: Spencer\n" "Description: A shell to promote math!\n" "Ask for the source code!\n" "\n"; printf("%s", message); free(args); return 0; } int builtinCD(char **dir) { int status = 0; char *oldPWD = getenv("PWD"); char *changeDir = malloc(sizeof(char *)); if (dir[1] != NULL) { char c = *dir[1]; switch (c) { case '~': changeDir = realloc(changeDir, strlen(getenv("HOME") + strlen(dir[1]))); strcpy(changeDir, getenv("HOME")); strcat(changeDir, dir[1]+1); break; case '-': changeDir = realloc(changeDir, strlen(getenv("OLDPWD"))); strcpy(changeDir, getenv("OLDPWD")); break; default: changeDir = dir[1]; break; } if (chdir(changeDir) != 0) { status = 1; printf("bash: cd: error\n"); } else { setenv("PWD", dir[1], 1); setenv("OLDPWD", oldPWD, 1); free(prompt); prompt = NULL; } } else { if (chdir(getenv("HOME")) != 0) { status = 1; printf("Bash: cd: error\n"); } else { setenv("PWD", getenv("HOME"), 1); setenv("OLDPWD", oldPWD, 1); free(prompt); prompt = NULL; } } free(changeDir); changeDir = NULL; free(dir); return status; } int builtinNMN(char **args) { skipMath = true; free(args); return 0; } 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(const char *line) { size_t size = 8; size_t pos = 0; char **args = malloc(size * sizeof(char*)); char *arg = NULL; char *lineTest = NULL; lineTest = strdup(line); if(line == NULL) { *args=NULL; return args; } arg = strtok(lineTest, " \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); } } free(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 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; long numAnswer = 0; int num1 = randomNum(mathLen); int num2 = randomNum(mathLen); long numSolution = 0; char *numLine = NULL; int randProblem = randomNum(strlen(mathOperation)) - 1; if (skipMath) { return 0; } for(int i = 0; i < 1; i++) { switch (mathOperation[randProblem]) { case '+': numSolution = num1 + num2; break; case '-': numSolution = num1 - num2; break; case '*': numSolution = num1 * num2; break; case '/': numSolution = num1 / num2; break; default: numSolution = 0; break; } #ifndef RELEASEBUILD printf("Answer: %ld\n", numSolution); #endif while(numLine == NULL) { printf("%d %c %d = ", num1, mathOperation[randProblem], num2); numLine = readline(""); if (numLine != NULL && strlen(numLine) > 0) { sscanf(numLine, "%ld", &numAnswer); } free(numLine); } if (numSolution != numAnswer) { // If wrong answer status = 1; } } #ifdef RELEASEBUILD printf("Calculating"); for (int i = 0; i < 5; i++){ printf("."); fflush(stdout); sleep(2); } printf("\n"); #endif return status; } void postCommand() { commandRun++; if ((commandRun % 3) == 0) { mathLen *= 10; } if (commandRun == 3) { mathOperation = "+-*/"; } return; } void startShell() { char *line = NULL; int status = 0; while ((line = readline(getPrompt())) != NULL) { if (strlen(line) != 0) { // Checking if prompt returns empty if (strcmp(line, "nomorenumbers")) { status = mathTest(); } else { status = 0; } if (status == 0) { // Passed math test add_history(line); execCommand(line); postCommand(); } else { // Failed math test failedTest(); } } free(line); } printf("Exiting Shell\n"); sleep(2); free(prompt); prompt = NULL; } void handleBreak(){ signal(SIGINT, handleBreak); printf("\n"); rl_on_new_line(); rl_replace_line("", 0); rl_redisplay(); } int main() { initShell(); signal(SIGINT, handleBreak); startShell(); #if RL_READLINE_VERSION < 0x0602 rl_clear_history(); #else clear_history(); #endif exit(EXIT_SUCCESS); }