Initial code commit

This commit is contained in:
Pin
2022-03-22 09:32:08 -04:00
parent 0a6f36e4ea
commit aa0528664c
5 changed files with 412 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.out
bin/

18
Makefile Normal file
View File

@@ -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

View File

@@ -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
```

378
msh.c Normal file
View File

@@ -0,0 +1,378 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
#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);
}

4
msh.h Normal file
View File

@@ -0,0 +1,4 @@
int builtinExit(char **args);
int builtinAuthor(char **args);
int builtinCD(char **dir);
int builtinNMN(char **args);