diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f47cb20 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..74a90d8 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +LIBRARIES = -lreadline +RELEASE_ARGS = -DRELEASEBUILD +SOURCES = ./msh.c + +build: + gcc -Wall -std=gnu99 ${RELEASE_ARGS} ${SOURCES} ${LIBRARIES} + +debug: + gcc -Wall -std=gnu99 ${SOURCES} ${LIBRARIES} diff --git a/msh.c b/msh.c new file mode 100644 index 0000000..67a33af --- /dev/null +++ b/msh.c @@ -0,0 +1,242 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "msh.h" + +#define clear() printf("\033[H\033[J") + +union pipe { + int fileDesc[2]; + struct { + int read; + int write; + }; +}; + +static char *builtinFunctions[] = { + "exit", + "author" +}; + +int (*builtinFunc[]) (char *) = { + &builtinExit, + &builtinAuthor +}; + +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; +} + +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; + union pipe input, output; + FILE *inputFileD, *outputFileD; + char *line = NULL; + size_t lineCap = 0; + pid_t pid; + + 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); + + 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 + close(input.read); + close(output.write); + inputFileD = fdopen(input.write, "w"); + outputFileD = fdopen(output.read, "r"); + + while (getline(&line, &lineCap, outputFileD) > 0) { + printf("%s", line); + } + + free(line); + + do { + waitpid(pid, &status, WUNTRACED); + } while(!WIFEXITED(status) && !WIFSIGNALED(status)); + fclose(inputFileD); + fclose(outputFileD); + } + + return status; +} + +int execCommand(char *command) { + for (int i = 0; i < (sizeof(builtinFunctions) / sizeof(char *)); i++) { + if (strcmp(command, builtinFunctions[i]) == 0) { + return (*builtinFunc[i])(command); + } + } + return runCommand(command); +} + +void initShell() { + FILE *fd; + + clear(); + using_history(); + + 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); + } else { + printf("MOTD is empty\n"); + } + + return; +} + +char *getPrompt() { + pid_t pid; + union pipe input, output; + FILE *outputFileD; + size_t len = 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", "echo \"${PS1@P}\"", 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); + } + + 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(10); + int num2 = randomNum(10); + printf("%d + %d = ", num1, num2); + scanf("%d", &numAnswer); + + #ifdef RELEASEBUILD + printf("Calculating"); + for (int i = 0; i < 15; i++){ + printf("."); + fflush(stdout); + sleep(2); + } + printf("\n"); + #endif + + if ((num1 + num2) != numAnswer) { // If wrong answer + status = 1; + } + 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(int sig){ + printf("\n"); + printf(getPrompt()); +} + +int main() { + initShell(); + signal(SIGINT, handleBreak); + startShell(); + exit(EXIT_SUCCESS); +} diff --git a/msh.h b/msh.h new file mode 100644 index 0000000..d544355 --- /dev/null +++ b/msh.h @@ -0,0 +1,2 @@ +int builtinExit(); +int builtinAuthor();