mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-03-25 08:45:52 +00:00
[c] Make tests pass and get rid of queue_message
This commit is contained in:
parent
32a4f6b894
commit
6cf858fbfc
8 changed files with 42 additions and 137 deletions
|
|
@ -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
|
||||
|
|
|
|||
35
c/builtins.c
35
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 <command>'");
|
||||
queue_message(" runs <command> in the background");
|
||||
fprintf(stderr, "bg: usage 'bg <command>'\n");
|
||||
fprintf(stderr, " runs <command> 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 <job number>'");
|
||||
queue_message(" type 'bglist' to see running jobs");
|
||||
fprintf(stderr, "bgkill: usage 'bgkill <job number>'\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;
|
||||
}
|
||||
|
||||
|
|
|
|||
10
c/exec.c
10
c/exec.c
|
|
@ -20,7 +20,6 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
|
|
|
|||
1
c/jobs.c
1
c/jobs.c
|
|
@ -15,7 +15,6 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "jobs.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
|
||||
|
|
|
|||
111
c/main.c
111
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);
|
||||
|
|
|
|||
12
c/main.h
12
c/main.h
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue