mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-04-27 14:57:43 +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)
|
a1: $(OBJS)
|
||||||
$(CC) $(CFLAGS) -o a1 $(OBJS) $(LDFLAGS) -lreadline -lhistory -ltermcap
|
$(CC) $(CFLAGS) -o a1 $(OBJS) $(LDFLAGS) -lreadline -lhistory -ltermcap
|
||||||
|
|
||||||
|
test: a1
|
||||||
|
cd ../ruby && A1_PATH=../c/a1 bundle exec rake test
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(OBJS) a1
|
rm -rf $(OBJS) a1
|
||||||
|
|
|
||||||
35
c/builtins.c
35
c/builtins.c
|
|
@ -22,46 +22,39 @@
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
#include "jobs.h"
|
#include "jobs.h"
|
||||||
#include "main.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
int builtin_bg(int argc, char **argv) {
|
int builtin_bg(int argc, char **argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
queue_message("bg: usage 'bg <command>'");
|
fprintf(stderr, "bg: usage 'bg <command>'\n");
|
||||||
queue_message(" runs <command> in the background");
|
fprintf(stderr, " runs <command> in the background\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t pid = exec_command(&argv[1], 1); /* &argv[1] skips 'bg' */
|
pid_t pid = exec_command(&argv[1], 1); /* &argv[1] skips 'bg' */
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
job j = add_job(pid, &argv[1]);
|
job j = add_job(pid, &argv[1]);
|
||||||
|
printf("Running job " YELLOW "%i" WHITE " (pid %i) in background\n" CLEAR, j->id, pid);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
int builtin_bgkill(int argc, char **argv) {
|
int builtin_bgkill(int argc, char **argv) {
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
queue_message("bgkill: usage 'bgkill <job number>'");
|
fprintf(stderr, "bgkill: usage 'bgkill <job number>'\n");
|
||||||
queue_message(" type 'bglist' to see running jobs");
|
fprintf(stderr, " type 'bglist' to see running jobs=n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int job_id = atoi(argv[1]);
|
int job_id = atoi(argv[1]);
|
||||||
job j = job_with_id(job_id);
|
job j = job_with_id(job_id);
|
||||||
if (!j) {
|
if (!j) {
|
||||||
queue_message(YELLOW "Invalid job number");
|
fprintf(stderr, YELLOW "Invalid job number\n");
|
||||||
queue_message("(type 'bglist' to see running jobs)");
|
fprintf(stderr, "(type 'bglist' to see running jobs)\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
kill(j->pid, SIGTERM);
|
kill(j->pid, SIGTERM);
|
||||||
/*delete_job (j);*/
|
|
||||||
/* queue_message ("Job killed");*/
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,14 +64,10 @@ int builtin_bglist(void) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
job j;
|
job j;
|
||||||
char *message = (char *)myxmalloc(MSGLEN);
|
|
||||||
for (j = get_job_list(); j; j = j->next) {
|
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);
|
printf(YELLOW "%i" WHITE ": (pid %i)" YELLOW " %s\n" CLEAR, j->id, j->pid, j->cmdline);
|
||||||
queue_message(message);
|
|
||||||
}
|
}
|
||||||
snprintf(message, MSGLEN, GREEN "Total: %i background job(s) running" CLEAR, num_jobs);
|
printf(GREEN "Total: %i background job(s) running\n" CLEAR, num_jobs);
|
||||||
queue_message(message);
|
|
||||||
free(message);
|
|
||||||
return num_jobs;
|
return num_jobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,11 +88,7 @@ int builtin_cd(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chdir(dir) < 0) { /* error */
|
if (chdir(dir) < 0) { /* error */
|
||||||
size_t len = strlen(dir);
|
fprintf(stderr, RED "cd: %s: %s" CLEAR, strerror(errno), 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);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
c/exec.c
10
c/exec.c
|
|
@ -20,7 +20,6 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
#include "main.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
char *is_executable(char *file) {
|
char *is_executable(char *file) {
|
||||||
|
|
@ -54,10 +53,7 @@ pid_t exec_command(char **argv, int background) {
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
if (!(filename = is_executable(argv[0]))) { /* error, not executable */
|
if (!(filename = is_executable(argv[0]))) { /* error, not executable */
|
||||||
char *msg = (char *)myxmalloc(MSGLEN);
|
fprintf(stderr, RED "%s: %s\n" CLEAR, argv[0], strerror(errno));
|
||||||
sprintf(msg, RED "%s: %s" CLEAR, argv[0], strerror(errno));
|
|
||||||
queue_message(msg);
|
|
||||||
free(msg);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,11 +72,11 @@ pid_t exec_command(char **argv, int background) {
|
||||||
execv(filename, argv);
|
execv(filename, argv);
|
||||||
|
|
||||||
/* if we get here there was an error, display it */
|
/* 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);
|
free(filename);
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
} else { /* error, pid < 0 */
|
} 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);
|
free(filename);
|
||||||
return pid;
|
return pid;
|
||||||
|
|
|
||||||
1
c/jobs.c
1
c/jobs.c
|
|
@ -15,7 +15,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "jobs.h"
|
#include "jobs.h"
|
||||||
#include "main.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
|
#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;
|
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 */
|
/* like strerror for waitpid */
|
||||||
char *strsignal(int status) {
|
char *strsignal(int status) {
|
||||||
char *str = (char *)myxmalloc(MSGLEN);
|
char *str = (char *)myxmalloc(MSGLEN);
|
||||||
|
|
@ -154,11 +97,7 @@ void child_state_changed(int signum) {
|
||||||
if (j) {
|
if (j) {
|
||||||
char *strstatus = strsignal(status);
|
char *strstatus = strsignal(status);
|
||||||
/* alert the user of the termination, and delete the job */
|
/* alert the user of the termination, and delete the job */
|
||||||
size_t len = strlen(j->cmdline);
|
fprintf(stderr, YELLOW "%i" WHITE ": (pid %i) %s" YELLOW ": %s\n" CLEAR, j->id, j->pid, strstatus, 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);
|
|
||||||
xfree(strstatus);
|
xfree(strstatus);
|
||||||
delete_job(j);
|
delete_job(j);
|
||||||
}
|
}
|
||||||
|
|
@ -179,42 +118,39 @@ char *get_prompt(void) {
|
||||||
int cmd_matches(char *trigger, char *cmd) { return !strcmp(trigger, cmd); }
|
int cmd_matches(char *trigger, char *cmd) { return !strcmp(trigger, cmd); }
|
||||||
|
|
||||||
int handle_wordexp_result(int result, char *cmd) {
|
int handle_wordexp_result(int result, char *cmd) {
|
||||||
|
if (result == 0) /* success */
|
||||||
|
return 1;
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case WRDE_BADCHAR:
|
case WRDE_BADCHAR: {
|
||||||
/* gcc chokes if the int decl is first, lame */
|
|
||||||
;
|
|
||||||
int invalid_char = strcspn(cmd, INVALID_CHARS);
|
int invalid_char = strcspn(cmd, INVALID_CHARS);
|
||||||
char *msg = (char *)myxmalloc(strlen(cmd) + MSGLEN);
|
char *msg = (char *)myxmalloc(strlen(cmd) + MSGLEN);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < invalid_char; i++)
|
for (i = 0; i < invalid_char; i++)
|
||||||
*(msg + i) = ' ';
|
*(msg + i) = ' ';
|
||||||
sprintf(msg + invalid_char, RED "^ invalid character in column %i", invalid_char + 1);
|
sprintf(msg + invalid_char, RED "^ invalid character in column %i", invalid_char + 1);
|
||||||
queue_message(cmd);
|
fprintf(stderr, "%s\n", cmd);
|
||||||
queue_message(msg);
|
fprintf(stderr, "%s\n", msg);
|
||||||
xfree(msg);
|
xfree(msg);
|
||||||
result = 0;
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case WRDE_BADVAL:
|
case WRDE_BADVAL:
|
||||||
queue_message("undefined variable");
|
fprintf(stderr, "undefined variable\n");
|
||||||
result = 0;
|
|
||||||
break;
|
break;
|
||||||
case WRDE_CMDSUB:
|
case WRDE_CMDSUB:
|
||||||
queue_message("no command substitution allowed");
|
fprintf(stderr, "no command substitution allowed\n");
|
||||||
result = 0;
|
|
||||||
break;
|
break;
|
||||||
case WRDE_NOSPACE:
|
case WRDE_NOSPACE:
|
||||||
queue_message("not enough memory");
|
fprintf(stderr, "not enough memory\n");
|
||||||
result = 0;
|
|
||||||
break;
|
break;
|
||||||
case WRDE_SYNTAX:
|
case WRDE_SYNTAX:
|
||||||
queue_message("syntax error");
|
fprintf(stderr, "syntax error\n");
|
||||||
result = 0;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* success */
|
fprintf(stderr, "wordexp return an unknown error code %d\n", result);
|
||||||
result = 1;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int process_command(char *line, options_t options) {
|
int process_command(char *line, options_t options) {
|
||||||
|
|
@ -276,20 +212,19 @@ void repl_start(options_t options) {
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
signal(SIGCHLD, child_state_changed); /* catch SIGCHLD */
|
signal(SIGCHLD, child_state_changed); /* catch SIGCHLD */
|
||||||
|
|
||||||
/* while waiting for input, display messasges */
|
|
||||||
rl_event_hook = print_messages;
|
|
||||||
|
|
||||||
/* register clean-up function */
|
/* register clean-up function */
|
||||||
atexit(free_job_list);
|
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 */
|
/* parse command line arguments, skipping over the program name at index 0 */
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strncmp("-c", argv[i], 2) == 0) {
|
if (strncmp("-c", argv[i], 2) == 0) {
|
||||||
if (i == argc - 1) {
|
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;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
i++;
|
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) {
|
} else if (strncmp("-v", argv[i], 2) == 0 || strncmp("--verbose", argv[i], 9) == 0) {
|
||||||
options.verbose = true;
|
options.verbose = true;
|
||||||
} else {
|
} else {
|
||||||
printf(RED "[ERROR] " CLEAR "Unknown argument: %s\n", argv[i]);
|
fprintf(stderr, RED "[ERROR] " CLEAR "Unknown argument: %s\n", argv[i]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.command) {
|
if (options.command) {
|
||||||
int retval = process_command(options.command, &options);
|
return process_command(options.command, &options);
|
||||||
print_messages();
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repl_start(&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
|
if background
|
||||||
job = Job.new(next_job_id, pid, cmd, args)
|
job = Job.new(next_job_id, pid, cmd, args)
|
||||||
@jobs_by_pid[pid] = job
|
@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)
|
Process.waitpid(pid, Process::WNOHANG)
|
||||||
0
|
0
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,9 @@ class ShellTest < Minitest::Test
|
||||||
#################################
|
#################################
|
||||||
|
|
||||||
def test_background_job
|
def test_background_job
|
||||||
output = `#{A1_PATH} -c 'bg echo hello'`
|
output = `#{A1_PATH} -c 'bg echo hello'`.gsub(/\e\[([;\d]+)?m/, '')
|
||||||
assert output.match?(/\ABackground job 1 \(pid \d+\)\nhello\n\z/m), "'#{output}' is unexpected"
|
pid = /\(pid (\d+)\)/.match(output)[1]
|
||||||
|
assert_equal "Running job 1 (pid #{pid}) in background\nhello\n", output
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resolves_executables_with_absolute_paths
|
def test_resolves_executables_with_absolute_paths
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue