FENIX_coreutils/sh.c

272 lines
6.1 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 <sys/stat.h>
#include <signal.h>
#include <libgen.h>
#include <errno.h>
#include <limits.h>
#define ARG_SEP " " /* Argument separator for commands */
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[]);
mode_t _umask = 022;
char * prompt_str;
#define PROMPT prompt_str
int stat_loc; /* Used by waitpid; global because used in _kill() */
int main() {
char** argv;
char* user_input;
pid_t child_pid;
prompt_str = getenv("PS1");
prompt_str = prompt_str == NULL ? "$ " : prompt_str;
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);
}
else if(strcmp(argv[0], "umask") == 0) {
if(strcmp(argv[1], "-S") == 0) {
/* TODO: Print symbolic string. */
}
else if(argv[1] != NULL) {
/* TODO: Implement symbolic support. */
_umask = atoi(argv[1]);
umask(_umask);
}
else {
printf("%o\n", _umask);
}
continue;
}
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)) {
if(strcmp(temp, "$?") == 0) {
argv[i] = calloc(4, sizeof(**argv));
sprintf(argv[i], "%d", WEXITSTATUS(stat_loc));
}
else {
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 && atoi(argv[3]) == WEXITSTATUS(stat_loc) &&
WIFSIGNALED(stat_loc)) {
switch(WTERMSIG(stat_loc)) {
case 0: printf("0\n"); break;
case 1: printf("SIGHUP\n"); break;
case 2: printf("SIGINT\n"); break;
case 3: printf("SIGQUIT\n"); break;
case 6: printf("SIGABRT\n"); break;
case 9: printf("SIGKILL\n"); break;
case 14: printf("SIGALRM\n"); break;
case 15: printf("SIGTERM\n"); break;
}
return 0;
}
else if(argc > 2) {
switch(atoi(argv[3])) {
case 0: printf("0\n"); break;
case 1: printf("HUP\n"); break;
case 2: printf("INT\n"); break;
case 3: printf("QUIT\n"); break;
case 6: printf("ABRT\n"); break;
case 9: printf("KILL\n"); break;
case 14: printf("ALRM\n"); break;
case 15: printf("TERM\n"); break;
}
}
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;
}
}
}
}