initial code commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.out
|
||||
9
Makefile
Normal file
9
Makefile
Normal file
@@ -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}
|
||||
242
msh.c
Normal file
242
msh.c
Normal file
@@ -0,0 +1,242 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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")
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user