diff --git a/c/Makefile b/c/Makefile index 2673d9b..fd8d3f8 100644 --- a/c/Makefile +++ b/c/Makefile @@ -9,5 +9,8 @@ all: a1 a1: $(OBJS) $(CC) $(CFLAGS) -o a1 $(OBJS) $(LDFLAGS) -lreadline -lhistory -ltermcap +test: a1 + cd ../ruby && A1_PATH=../c/a1 bundle exec rake test + clean: rm -rf $(OBJS) a1 diff --git a/c/builtins.c b/c/builtins.c index 46c20b0..d6e0224 100644 --- a/c/builtins.c +++ b/c/builtins.c @@ -22,46 +22,39 @@ #include "builtins.h" #include "exec.h" #include "jobs.h" -#include "main.h" #include "utils.h" int builtin_bg(int argc, char **argv) { if (argc < 2) { - queue_message("bg: usage 'bg '"); - queue_message(" runs in the background"); + fprintf(stderr, "bg: usage 'bg '\n"); + fprintf(stderr, " runs in the background\n"); return -1; } pid_t pid = exec_command(&argv[1], 1); /* &argv[1] skips 'bg' */ if (pid > 0) { job j = add_job(pid, &argv[1]); - - char *message = (char *)myxmalloc(MSGLEN); - snprintf(message, MSGLEN, "Running job " YELLOW "%i" WHITE " (pid %i) in background" CLEAR, j->id, pid); - queue_message(message); - free(message); + printf("Running job " YELLOW "%i" WHITE " (pid %i) in background\n" CLEAR, j->id, pid); } return pid; } int builtin_bgkill(int argc, char **argv) { if (argc != 2) { - queue_message("bgkill: usage 'bgkill '"); - queue_message(" type 'bglist' to see running jobs"); + fprintf(stderr, "bgkill: usage 'bgkill '\n"); + fprintf(stderr, " type 'bglist' to see running jobs=n"); return -1; } int job_id = atoi(argv[1]); job j = job_with_id(job_id); if (!j) { - queue_message(YELLOW "Invalid job number"); - queue_message("(type 'bglist' to see running jobs)"); + fprintf(stderr, YELLOW "Invalid job number\n"); + fprintf(stderr, "(type 'bglist' to see running jobs)\n"); return -1; } kill(j->pid, SIGTERM); - /*delete_job (j);*/ - /* queue_message ("Job killed");*/ return 1; } @@ -71,14 +64,10 @@ int builtin_bglist(void) { return 0; job j; - char *message = (char *)myxmalloc(MSGLEN); for (j = get_job_list(); j; j = j->next) { - snprintf(message, MSGLEN, YELLOW "%i" WHITE ": (pid %i)" YELLOW " %s" CLEAR, j->id, j->pid, j->cmdline); - queue_message(message); + printf(YELLOW "%i" WHITE ": (pid %i)" YELLOW " %s\n" CLEAR, j->id, j->pid, j->cmdline); } - snprintf(message, MSGLEN, GREEN "Total: %i background job(s) running" CLEAR, num_jobs); - queue_message(message); - free(message); + printf(GREEN "Total: %i background job(s) running\n" CLEAR, num_jobs); return num_jobs; } @@ -99,11 +88,7 @@ int builtin_cd(int argc, char **argv) { } if (chdir(dir) < 0) { /* error */ - size_t len = strlen(dir); - char *message = (char *)myxmalloc(len + MSGLEN); - (void)snprintf(message, len + MSGLEN, RED "cd: %s: %s" CLEAR, strerror(errno), dir); - queue_message(message); - free(message); + fprintf(stderr, RED "cd: %s: %s" CLEAR, strerror(errno), dir); return -1; } diff --git a/c/exec.c b/c/exec.c index 8e8fd1b..8a928df 100644 --- a/c/exec.c +++ b/c/exec.c @@ -20,7 +20,6 @@ #include #include "exec.h" -#include "main.h" #include "utils.h" char *is_executable(char *file) { @@ -54,10 +53,7 @@ pid_t exec_command(char **argv, int background) { char *filename; if (!(filename = is_executable(argv[0]))) { /* error, not executable */ - char *msg = (char *)myxmalloc(MSGLEN); - sprintf(msg, RED "%s: %s" CLEAR, argv[0], strerror(errno)); - queue_message(msg); - free(msg); + fprintf(stderr, RED "%s: %s\n" CLEAR, argv[0], strerror(errno)); return -1; } @@ -76,11 +72,11 @@ pid_t exec_command(char **argv, int background) { execv(filename, argv); /* if we get here there was an error, display it */ - printf(RED "\nCannot execute '%s' (%s)\n" CLEAR, argv[0], strerror(errno)); + fprintf(stderr, RED "\nCannot execute '%s' (%s)\n" CLEAR, argv[0], strerror(errno)); free(filename); _exit(EXIT_FAILURE); } else { /* error, pid < 0 */ - queue_message(RED "Unable to fork(), uh oh..." CLEAR); + fprintf(stderr, RED "Unable to fork(), uh oh...\n" CLEAR); } free(filename); return pid; diff --git a/c/jobs.c b/c/jobs.c index e7c743d..cf80837 100644 --- a/c/jobs.c +++ b/c/jobs.c @@ -15,7 +15,6 @@ #include #include "jobs.h" -#include "main.h" #include "utils.h" #define MIN(a, b) ((a) < (b)) ? (a) : (b) diff --git a/c/main.c b/c/main.c index c6707cf..f726f16 100644 --- a/c/main.c +++ b/c/main.c @@ -45,63 +45,6 @@ struct options { }; typedef struct options *options_t; -struct message { - char *data; - struct message *next; -}; -typedef struct message *message; - -static message msg_queue_head = NULL; /* message queue */ - -/** queue messages so they don't mix with a running program's output - * instead they're only displayed while waiting on input w/ readline - * message m: freed in print_messages() - */ -void queue_message(char *msg) { - message i, m = (message)myxmalloc(sizeof(struct message)); - m->data = strdup(msg); - m->next = NULL; - for (i = msg_queue_head; i && i->next; i = i->next) - ; - - if (!i) /* if i is NULL, then i == msg_queue_head == NULL */ - msg_queue_head = m; - else /* queue m */ - i->next = m; -} - -void free_message_queue(void) { - message m, n = msg_queue_head; - while ((m = n)) { - n = m->next; - xfree(m->data); - free(m); - } - msg_queue_head = NULL; -} - -int print_messages(void) { - if (!msg_queue_head) - return 0; - - /* there must be an easier way to interrupt readline... */ - char *old = rl_line_buffer; - rl_line_buffer = strdup(""); - rl_save_prompt(); - rl_clear_message(); - - message m; - for (m = msg_queue_head; m; m = m->next) - printf(WHITE "%s\n" CLEAR, m->data); - free_message_queue(); - - xfree(rl_line_buffer); - rl_line_buffer = old; - rl_restore_prompt(); - rl_forced_update_display(); - return 1; -} - /* like strerror for waitpid */ char *strsignal(int status) { char *str = (char *)myxmalloc(MSGLEN); @@ -154,11 +97,7 @@ void child_state_changed(int signum) { if (j) { char *strstatus = strsignal(status); /* alert the user of the termination, and delete the job */ - size_t len = strlen(j->cmdline); - char *msg = (char *)myxmalloc(len + MSGLEN); - snprintf(msg, len + MSGLEN, YELLOW "%i" WHITE ": (pid %i) %s" YELLOW ": %s" CLEAR, j->id, j->pid, strstatus, j->cmdline); - queue_message(msg); - xfree(msg); + fprintf(stderr, YELLOW "%i" WHITE ": (pid %i) %s" YELLOW ": %s\n" CLEAR, j->id, j->pid, strstatus, j->cmdline); xfree(strstatus); delete_job(j); } @@ -179,42 +118,39 @@ char *get_prompt(void) { int cmd_matches(char *trigger, char *cmd) { return !strcmp(trigger, cmd); } int handle_wordexp_result(int result, char *cmd) { + if (result == 0) /* success */ + return 1; + switch (result) { - case WRDE_BADCHAR: - /* gcc chokes if the int decl is first, lame */ - ; + case WRDE_BADCHAR: { int invalid_char = strcspn(cmd, INVALID_CHARS); char *msg = (char *)myxmalloc(strlen(cmd) + MSGLEN); int i; for (i = 0; i < invalid_char; i++) *(msg + i) = ' '; sprintf(msg + invalid_char, RED "^ invalid character in column %i", invalid_char + 1); - queue_message(cmd); - queue_message(msg); + fprintf(stderr, "%s\n", cmd); + fprintf(stderr, "%s\n", msg); xfree(msg); - result = 0; break; + } case WRDE_BADVAL: - queue_message("undefined variable"); - result = 0; + fprintf(stderr, "undefined variable\n"); break; case WRDE_CMDSUB: - queue_message("no command substitution allowed"); - result = 0; + fprintf(stderr, "no command substitution allowed\n"); break; case WRDE_NOSPACE: - queue_message("not enough memory"); - result = 0; + fprintf(stderr, "not enough memory\n"); break; case WRDE_SYNTAX: - queue_message("syntax error"); - result = 0; + fprintf(stderr, "syntax error\n"); break; default: - /* success */ - result = 1; + fprintf(stderr, "wordexp return an unknown error code %d\n", result); + break; } - return result; + return 0; } int process_command(char *line, options_t options) { @@ -276,20 +212,19 @@ void repl_start(options_t options) { int main(int argc, char *argv[]) { signal(SIGCHLD, child_state_changed); /* catch SIGCHLD */ - /* while waiting for input, display messasges */ - rl_event_hook = print_messages; - /* register clean-up function */ atexit(free_job_list); - atexit(free_message_queue); - struct options options; + struct options options = { + NULL, /* command */ + false /* verbose */ + }; /* parse command line arguments, skipping over the program name at index 0 */ for (int i = 1; i < argc; i++) { if (strncmp("-c", argv[i], 2) == 0) { if (i == argc - 1) { - printf(RED "[ERROR] " CLEAR "Expected string after -c\n"); + fprintf(stderr, RED "[ERROR] " CLEAR "Expected string after -c\n"); return 1; } else { i++; @@ -298,15 +233,13 @@ int main(int argc, char *argv[]) { } else if (strncmp("-v", argv[i], 2) == 0 || strncmp("--verbose", argv[i], 9) == 0) { options.verbose = true; } else { - printf(RED "[ERROR] " CLEAR "Unknown argument: %s\n", argv[i]); + fprintf(stderr, RED "[ERROR] " CLEAR "Unknown argument: %s\n", argv[i]); return -1; } } if (options.command) { - int retval = process_command(options.command, &options); - print_messages(); - return retval; + return process_command(options.command, &options); } repl_start(&options); diff --git a/c/main.h b/c/main.h deleted file mode 100644 index 063c4ba..0000000 --- a/c/main.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * A simple shell with some basic features. - * - * Sami Samhuri 0327342 - * January 31, 2006 - * CSC 360, Assignment 1 - * - * main.h - * $Id: main.h 183 2006-01-27 11:24:52Z sjs $ - */ - -void queue_message(char *message); diff --git a/ruby/shell/job_control.rb b/ruby/shell/job_control.rb index a598e6a..5c95cb1 100644 --- a/ruby/shell/job_control.rb +++ b/ruby/shell/job_control.rb @@ -25,7 +25,7 @@ module Shell if background job = Job.new(next_job_id, pid, cmd, args) @jobs_by_pid[pid] = job - puts "Background job #{job.id} (pid #{pid})" + puts white('Running job ') + yellow(job.id) + white(" (pid #{pid}) in background") Process.waitpid(pid, Process::WNOHANG) 0 else diff --git a/ruby/test/shell_test.rb b/ruby/test/shell_test.rb index 2b2eb2f..7500ae2 100644 --- a/ruby/test/shell_test.rb +++ b/ruby/test/shell_test.rb @@ -31,8 +31,9 @@ class ShellTest < Minitest::Test ################################# def test_background_job - output = `#{A1_PATH} -c 'bg echo hello'` - assert output.match?(/\ABackground job 1 \(pid \d+\)\nhello\n\z/m), "'#{output}' is unexpected" + output = `#{A1_PATH} -c 'bg echo hello'`.gsub(/\e\[([;\d]+)?m/, '') + pid = /\(pid (\d+)\)/.match(output)[1] + assert_equal "Running job 1 (pid #{pid}) in background\nhello\n", output end def test_resolves_executables_with_absolute_paths