FENIX_coreutils/sh.c
2020-12-23 22:32:35 -06:00

223 lines
No EOL
4.7 KiB
C
Executable file

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <libgen.h>
#include <errno.h>
#include <limits.h>
#define ARG_SEP " " /* Argument separator for commands */
#define PROMPT " $ "
char** create_argv(char* command);
int get_argc(char* command);
int get_argc_argv(char * argv[]);
char* prompt();
int cd(char* path);
int _kill(int argc, char * argv[]);
int main() {
char** argv;
char* user_input;
pid_t child_pid;
int stat_loc; /* ??? (Needed for waitpid) */
signal(SIGINT, SIG_IGN);
while(1) {
user_input = prompt();
argv = create_argv(user_input);
if(strcmp(argv[0], "cd") == 0) {
if(cd(argv[1]) < 0) {
perror(argv[1]);
}
/* causes the fork to get skipped */
continue;
}
else if(strcmp(argv[0], "kill") == 0) {
_kill(get_argc_argv(argv), argv);
continue;
}
else if(strcmp(argv[0], "exit") == 0 ||
strcmp(argv[0], "quit") == 0) {
exit(0);
}
child_pid = fork();
if(child_pid < 0) {
/* Could not start child process. Check why and error out. */
if(errno == EAGAIN) {
perror("Could not execute command: insufficient resources");
exit(1);
}
else if(errno == ENOMEM) {
/* Is this out of RAM or out of storage? */
perror("Could not execute command: insufficient storage space");
exit(1);
}
else {
perror("Could not execute command: unknown failure");
exit(1);
}
}
else if(child_pid == 0) {
/* We are the child, so allow ^C */
signal(SIGINT, SIG_DFL);
if(execvp(argv[0], argv) < 0) {
perror(argv[0]);
exit(1);
}
}
else {
/* Wait for the child to finish */
waitpid(child_pid, &stat_loc, WUNTRACED);
}
free(user_input);
free(argv);
}
return 0;
}
char** create_argv(char* command) {
int argc = get_argc(command) + 1;
char* temp;
int i = 0;
char** argv = calloc(argc, sizeof(*argv));
if(argv == NULL) {
perror("Unable to parse command: could not allocate memory");
exit(1);
}
for(temp = strtok(command, ARG_SEP); temp != NULL;
temp = strtok(NULL, ARG_SEP)) {
argv[i] = temp;
i++;
}
/* Set last item in argv to NULL, since execvp requires it */
argv[i] = NULL;
return argv;
}
char* prompt() {
char cwd[PATH_MAX];
getcwd(cwd, sizeof(cwd));
char* bname = basename(cwd);
char prompt[PATH_MAX + 5] = "";
strcat(prompt, bname);
strcat(prompt, PROMPT);
char* input = calloc(LINE_MAX, sizeof(*input));
if(input == NULL) {
perror("Unable to read command: could not allocate memory");
exit(1);
}
printf("%s", prompt);
fgets(input, LINE_MAX, stdin);
input[strcspn(input, "\r\n")] = '\0';
return input;
}
int get_argc(char* command) {
int argc = 0, word = 0;
char* command_iter = command;
do {
switch(*command_iter) {
case '\0':
case ' ':
case '\t': if(word) { word = 0; argc++; }
default: word = 1;
}
} while(*(command_iter++));
return argc;
}
int get_argc_argv(char * argv[]) {
int argc = 0;
while(argv[argc] != NULL) {
argc++;
}
return argc;
}
int cd(char* path) {
return chdir(path);
}
int get_signum(char * signame) {
if(strcmp(signame, "SIGHUP") == 0) {
return 1;
}
else if(strcmp(signame, "SIGINT") == 0) {
return 2;
}
else if(strcmp(signame, "SIGQUIT") == 0) {
return 3;
}
else if(strcmp(signame, "SIGABRT") == 0) {
return 6;
}
else if(strcmp(signame, "SIGKILL") == 0) {
return 9;
}
else if(strcmp(signame, "SIGALRM") == 0) {
return 14;
}
else if(strcmp(signame, "SIGTERM") == 0) {
return 15;
}
}
int _kill(int argc, char * argv[]) {
int signal = 0; pid_t pid; char c;
int write_signame = 0, error = 0;
while((c = getopt(argc, argv, "s:l")) != -1) {
switch(c) {
case 's': signal = get_signum(optarg); break;
case 'l': write_signame = 1; break;
}
}
if(signal == 0 && argv[1][0] == '-' && write_signame == 0) {
if(argv[1][1] >= '0' && argv[1][1] <= '9') {
signal = atoi((argv[1])+1);
}
else {
signal = get_signum((argv[1])+1);
}
}
if(write_signame) {
if(argc > 2) {
/* TODO: Figure out how to get exit status from $? */
}
else {
printf("SIGHUP\n");
printf("SIGINT\n");
printf("SIGQUIT\n");
printf("SIGABRT\n");
printf("SIGKILL\n");
printf("SIGALRM\n");
printf("SIGTERM\n");
return 0;
}
}
else {
for(int i = optind; i < argc && argv[i] != NULL; i++) {
pid = atoi(argv[optind]);
if(kill(pid, signal) != 0) {
error = 1;
}
}
}
}