From aa0528664c9fb7555e125cb73cc7dcc84c510f87 Mon Sep 17 00:00:00 2001 From: Pin Date: Tue, 22 Mar 2022 09:32:08 -0400 Subject: [PATCH] Initial code commit --- .gitignore | 2 + Makefile | 18 +++ README.md | 10 ++ msh.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++ msh.h | 4 + 5 files changed, 412 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 msh.c create mode 100644 msh.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1eb181 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.out +bin/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c8475b --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +LIBRARIES = -lreadline +RELEASE_ARGS = -DRELEASEBUILD +SOURCES = ./msh.c +OUTPUT_DIR = ./bin +OUTPUT_BIN = ${OUTPUT_DIR}/PROG +OUTPUT = -o ${OUTPUT_BIN} + +build: output_dir + gcc -Wall -std=gnu99 ${RELEASE_ARGS} ${SOURCES} ${OUTPUT:PROG=mash} ${LIBRARIES} + +debug: output_dir + gcc -Wall -std=gnu99 -g ${SOURCES} ${OUTPUT:PROG=mash} ${LIBRARIES} + +output_dir: + mkdir -p ${OUTPUT_DIR} + +install: + mv ${OUTPUT_BIN:PROG=mash} /usr/sbin/mash diff --git a/README.md b/README.md index e69de29..b70cffd 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,10 @@ +**IMPORTANT** this shell is **NOT** meant to be used for any serious applications. +This is for academic purposes only! + +## Install + +``` +make +make install +``` + diff --git a/msh.c b/msh.c new file mode 100644 index 0000000..9348d8a --- /dev/null +++ b/msh.c @@ -0,0 +1,378 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "msh.h" + +#define clear() printf("\033[H\033[J") +#define MATHLEN 100000 + +static char *prompt = NULL; +static bool skipMath = false; + +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; + + const char mathOperation[] = {'+', '-', '*', '/'}; + + int randProblem = randomNum(3); + + 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; + + } + #ifndef RELEASEBUILD + printf("Answer: %ld\n", numSolution); + #endif + printf("%d %c %d = ", num1, mathOperation[randProblem], num2); + numLine = readline(""); + sscanf(numLine, "%ld", &numAnswer); + + 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 + + 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); + } + 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); +} diff --git a/msh.h b/msh.h new file mode 100644 index 0000000..5ac7419 --- /dev/null +++ b/msh.h @@ -0,0 +1,4 @@ +int builtinExit(char **args); +int builtinAuthor(char **args); +int builtinCD(char **dir); +int builtinNMN(char **args);