#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <wait.h>
#include <errno.h>
#include "array.h"
#include "process.h"
#include "prompt_utils.h"
#include "builtins.h"
process *processes;
void signal_handler(int signal) {
if (signal == SIGINT) {
for (size_t i = 0; i < arrayLen(processes); ++i) {
pid_t pid = processes[i].pid;
if (pid != 0) {
kill(pid, SIGINT);
}
}
}
}
int main(void) {
setvbuf(stderr, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
// I don't think the shell should exit on SIG_TERM
if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
perror("Couldn't ignore sigterm");
exit(errno);
}
printf("Welcome! Available built-ins are:\n"
"cd: `cd <path>` - if no path is given, return to the current dir\n"
"wait: `wait pid1 ... pidN` - wait on the processes and report their exit conditions\n"
"fg: `fg pid` - pulls a process from the background back in the foreground\n"
"\n"
"You can put processes in the background using `&`\n"
"And with `|` you can pipe the output from one process to the input of another\n"
);
char const *const original_wd = get_current_dir_name();
char const *prompt = relative_path(original_wd, original_wd);
bool done = false;
while (!done) {
char *line = NULL;
size_t cap = 0;
__ssize_t length;
printf("%s > ", prompt);
if ((length = getline(&line, &cap, stdin)) < 0) {
fprintf(stderr, "Failed to read from STDIN");
fflush(stderr);
exit(-1);
}
if (strspn(line, " \n\t") == strlen(line)) {
// skip empty lines - empty being just spaces or tabs
continue;
}
line[length - 1] = '\0'; // cut the line feed
processes = NULL;
parse_line(line, &processes);
if (strcmp(processes[0].argv[0], "cd") == 0) {
if (arrayLen(processes) != 1) {
perror("Can't chain cd with other processes");
}
int ret;
switch (arrayLen(processes[0].argv)) {
case 3:
ret = chdir(processes[0].argv[1]);
break;
case 2:
ret = chdir(original_wd);
break;
default:
fprintf(stderr, "usage: cd <path>\n");
fflush(stderr);
ret = -1;
}
if (ret)
printf("[%i] ", ret);
free((void *) prompt);
char const *current_wd = get_current_dir_name();
prompt = relative_path(original_wd, current_wd);
free((void *) current_wd);
} else if (strcmp(processes[0].argv[0], "exit") == 0) {
done = true;
} else if (strcmp(processes[0].argv[0], "wait") == 0) {
builtin_wait(processes[0], false);
} else if (strcmp(processes[0].argv[0], "fg") == 0) {
// same behaviour as wait, just bind to shell again (i.e. terminate process on ctrl-c)
builtin_wait(processes[0], true);
} else {
if (arrayLen(processes) != 0 && processes[arrayLen(processes) - 1].blocking) {
signal(SIGINT, signal_handler);
}
for (size_t i = 0; i < arrayLen(processes); ++i) {
int ret = exec_command(processes[i]);
if (ret)
printf("[%i] ", ret);
}
signal(SIGINT, SIG_IGN);
}
free((void *) line);
line = NULL;
cap = 0;
free_processes(&processes);
}
free((void *) original_wd);
free((void *) prompt);
}