#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
// For reference
// ~cpen212/Public/lab5/crash-ref
#define MAXJOBS 32
#define MAXLINE 1024
char **environ;
struct job {
pid_t pid;
char cmd[MAXLINE];
};
struct job job_list[MAXJOBS];
int num_jobs = 0;
void handle_sigchld(int sig) {
// TODO
}
void handle_sigtstp(int sig) {
// TODO
}
void handle_sigint(int sig) {
// TODO
}
void handle_sigquit(int sig) {
// TODO
}
void install_signal_handlers() {
// TODO
}
void spawn(const char **toks, bool bg) { // bg is true iff command ended with &
pid_t pid = fork(); // create a new process
int status;
if (pid < 0) {
fprintf(stderr, "ERROR: failed to fork\n");
exit(1);
}
else if (pid == 0) { // child process
execvp(toks[0], (char * const *)toks); // execute the command
fprintf(stderr, "ERROR: cannot run %s\n", toks[0]); // executes only if execvp fails
exit(1);
}
else { // parent process
if (bg) { // if running in background, add job to list
if (num_jobs == MAXJOBS) { //
fprintf(stderr, "ERROR: too many jobs\n");
}
else {
job_list[num_jobs].pid = pid;
strncpy(job_list[num_jobs].cmd, *toks, MAXLINE);
num_jobs++;
}
}
else { // waits if running in foreground
waitpid(pid, &status, 0);
}
}
}
void cmd_jobs(const char **toks) {
if (toks[1] != NULL) {
fprintf(stderr, "ERROR: jobs takes no arguments\n");
}
else {
for (int i = 0; i < num_jobs; i++) {
printf("[%d] (%d) %s\n", i + 1, job_list[i].pid, job_list[i].cmd);
}
}
}
void cmd_fg(const char **toks) {
// TODO
}
void cmd_bg(const char **toks) {
// TODO
}
void cmd_nuke(const char **toks) {
// TODO
}
void cmd_quit(const char **toks) {
if (toks[1] != NULL) {
fprintf(stderr, "ERROR: quit takes no arguments\n");
}
else {
exit(0);
}
}
void eval(const char **toks, bool bg) { // bg is true iff command ended with &
assert(toks);
if (*toks == NULL) return;
if (strcmp(toks[0], "quit") == 0) {
cmd_quit(toks);
}
else if (strcmp(toks[0], "jobs") == 0) {
cmd_jobs(toks);
}
else {
spawn(toks, bg);
}
}
// you don't need to touch this unless you want to add debugging
void parse_and_eval(char *s) {
assert(s);
const char *toks[MAXLINE + 1];
while (*s != '\0') {
bool end = false;
bool bg = false;
int t = 0;
while (*s != '\0' && !end) {
while (*s == '\n' || *s == '\t' || *s == ' ') ++s;
if (*s != ';' && *s != '&' && *s != '\0') toks[t++] = s;
while (strchr("&;\n\t ", *s) == NULL) ++s;
switch (*s) {
case '&':
bg = true;
end = true;
break;
case ';':
end = true;
break;
}
if (*s) *s++ = '\0';
}
toks[t] = NULL;
eval(toks, bg);
}
}
// you don't need to touch this unless you want to add debugging
void prompt() {
printf("crash> ");
fflush(stdout);
}
// you don't need to touch this unless you want to add debugging
int repl() {
char *buf = NULL;
size_t len = 0;
while (prompt(), getline(&buf, &len, stdin) != -1) {
parse_and_eval(buf);
}
if (buf != NULL) free(buf);
if (ferror(stdin)) {
perror("ERROR");
return 1;
}
return 0;
}
// you don't need to touch this unless you want to add debugging options
int main(int argc, char **argv) {
install_signal_handlers();
return repl();
}