|
#include <sys/wait.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#define IN_BUFSIZE 1024 |
|
#define TOK_BUFSIZE 64 |
|
#define DELIM " \t\r\n\a" /* White-space delimiters */ |
|
|
|
/* Forward declarations */ |
|
int invoke_cmd(char *args[]); |
|
char *get_input(void); |
|
char **parse_input(char *line); |
|
|
|
char *done_str[] = { |
|
"exit", |
|
"quit"}; |
|
|
|
int done_str_size() |
|
{ |
|
return sizeof(done_str) / sizeof(char *); |
|
} |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
int status; |
|
char *line; |
|
char **args; |
|
|
|
/* Main program loop -- use infinite trick. */ |
|
for (;;) |
|
{ |
|
/* TODO: Explicitly catch Ctrl + C (SIGINT) to terminate. */ |
|
|
|
printf("eshell> "); |
|
line = get_input(); |
|
args = parse_input(line); |
|
|
|
/* Terminate on exit or quit. */ |
|
for (int i = 0; i < done_str_size(); i++) |
|
{ |
|
if (strcmp(args[0], done_str[i]) == 0) |
|
{ |
|
printf("Received %s, terminating... Bye!\n", args[0]); |
|
return 0; |
|
} |
|
} |
|
|
|
status = invoke_cmd(args); |
|
|
|
free(line); |
|
free(args); |
|
|
|
if (!status) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int invoke_cmd(char *args[]) |
|
{ |
|
int status; |
|
pid_t cpid; |
|
|
|
cpid = fork(); |
|
|
|
if (cpid == 0) |
|
{ |
|
/* This is the child process. */ |
|
if (execvp(args[0], args) == -1) |
|
{ |
|
perror("eshell"); |
|
} |
|
|
|
/* |
|
* Note: An exit code of one (1) indicates failure but is non-portable. |
|
* For POSIX compliance, use the EXIT_FAILURE constant instead. |
|
*/ |
|
exit(EXIT_FAILURE); |
|
} |
|
else |
|
{ |
|
/* This the parent process. */ |
|
do |
|
{ |
|
waitpid(cpid, &status, WUNTRACED); |
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
char *get_input(void) |
|
{ |
|
int c; |
|
int index = 0; |
|
int buf_size = IN_BUFSIZE; |
|
char *buf = malloc(sizeof(char) * buf_size); |
|
|
|
// TODO: Check to see that malloc succeded or failed. |
|
|
|
while (1) |
|
{ |
|
c = getchar(); |
|
if (c == EOF) |
|
{ |
|
exit(EXIT_SUCCESS); |
|
} |
|
else if (c == '\n') |
|
{ |
|
buf[index] = '\0'; |
|
return buf; |
|
} |
|
else |
|
{ |
|
buf[index] = c; |
|
} |
|
index++; |
|
|
|
// Check to see that we have enough space in the buffer. |
|
if (index >= buf_size) |
|
{ |
|
buf_size += IN_BUFSIZE; |
|
buf = realloc(buf, buf_size); |
|
if (!buf) |
|
{ |
|
fprintf(stderr, "eshell: buffer resize error\n"); |
|
exit(1); // EXIT_FAILURE |
|
} |
|
} |
|
} |
|
} |
|
|
|
char **parse_input(char *line) |
|
{ |
|
int index = 0; |
|
int buf_size = TOK_BUFSIZE; |
|
char *tok; |
|
char **tmp; |
|
|
|
char **list = malloc(buf_size * sizeof(char *)); |
|
|
|
/* You see this convention in C. If a pointer has a value, it evaluates to true. */ |
|
if (!list) |
|
{ |
|
fprintf(stderr, "eshell: parsing error\n"); |
|
exit(1); // EXIT_FAILURE |
|
} |
|
|
|
tok = strtok(line, DELIM); |
|
while (tok != NULL) |
|
{ |
|
list[index] = tok; |
|
index++; |
|
|
|
if (index >= buf_size) |
|
{ |
|
buf_size += TOK_BUFSIZE; |
|
tmp = list; |
|
list = realloc(list, buf_size * sizeof(char *)); |
|
if (!list) |
|
{ |
|
free(tmp); |
|
fprintf(stderr, "eshell: parsing allocation error\n"); |
|
exit(1); // EXIT_FAILURE |
|
} |
|
} |
|
|
|
tok = strtok(NULL, DELIM); |
|
} |
|
list[index] = NULL; |
|
return list; |
|
} |