computing-systems-212 / Lab 5: Shell Development / task2 / crash.c
crash.c
Raw
#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();
}