csnotes/334/homework/msh/msh.c

152 lines
3.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <sys/types.h>
#define PROMPT "msh> "
#define EXIT_CMD "exit"
#define MAX_BUF 121
// Statuses from builtin responses
#define RESP_HELP 1
#define RESP_DATE 2
#define RESP_ECHO 3
#define RESP_SHELL 4
#define RESP_CD_FAIL -1
#define RESP_CD_SUCCESS 6
// Sub statuses that are generally nice to have
#define CD_NOP 0x1
void echo(const char*);
void remove_newline(char*);
void exit_branch(const char*);
int builtin_response(char*);
int cd_handler(char**, const int);
int main(int argc, char** argv) {
FILE* file = NULL;
if(argc == 2) {
file = fopen(argv[1], "r");
if(!file) { return 1; }
}
char buffer[MAX_BUF];
while(1) {
// determine if we're reading from stdin or a stream of some sort
if(file == NULL && isatty(STDIN_FILENO)) {
printf("%s", PROMPT);
fgets(buffer, MAX_BUF, stdin);
}
else {
fgets(buffer, MAX_BUF, file);
printf("buffer: %s\n", buffer);
}
// Deal with EOF input
if(!strlen(buffer)) {
exit(0);
}
exit_branch(buffer);
remove_newline(buffer);
int status = builtin_response(buffer);
// the switch in case we need to add more response flags later
switch(status) {
case RESP_ECHO:
echo(buffer);
case RESP_SHELL: // shell command handler was used
continue;
case RESP_CD_FAIL:
printf("msh: cd: No such file or directory\n");
}
memset(buffer, 0x00, MAX_BUF); // reset buffer after each usage
}
return 0;
}
void echo(const char* buf) {
printf("%s\n", buf);
}
void remove_newline(char* buf) {
char* c = buf;
while(*c != '\n') { c++; }
*c='\0';
}
void exit_branch(const char* buf) {
if(!strcmp(buf, EXIT_CMD)) {
exit(0);
}
}
int builtin_response(char* buffer) {
#define STRING_DELIMITER " "
#define TIME_FMT "%D"
exit_branch(buffer); // exit is builtin so here we are
if(!strcmp(buffer, "help")) {
echo("enter Linux commands, or exit to exit");
return RESP_HELP;
}
// Printout the date today
if(!strcmp(buffer, "date")) {
static char date[64];
time_t now = time(0);
strftime(date, sizeof(date), TIME_FMT, localtime(&now));
echo(date);
return RESP_DATE;
}
// Populate tokens
char *tokens[64];
int idx = 0;
for(char* cur = strtok(buffer, STRING_DELIMITER); cur != NULL; cur=strtok(NULL, STRING_DELIMITER)) {
#ifdef DBG_LINE
printf("%s ", cur);
#endif
tokens[idx] = cur;
idx++;
}
// Handling director changes
int cd_result = cd_handler(tokens, idx);
if(cd_result != CD_NOP) {
return cd_result;
}
// Execute child process
pid_t child = fork();
if(child != 0) {
int status_child = wait(NULL);
if(status_child == 127) {
printf("msh: %s: %d", tokens[0], status_child);
}
}
else {
execvp(tokens[0], tokens);
}
// Cleanup pointers
for(int i = 0; i< 64; i++) {
tokens[i] = NULL;
}
}
int cd_handler(char** tokens, const int t_count) {
if(!strcmp(tokens[0], "cd")) {
if(t_count < 2) {
int ret = chdir(getenv("HOME"));
return ret;
}
return chdir(tokens[1]);
}
return CD_NOP;
}