[c] Make tests pass and get rid of queue_message

This commit is contained in:
Sami Samhuri 2022-01-22 17:07:12 -08:00
parent 32a4f6b894
commit 6cf858fbfc
No known key found for this signature in database
GPG key ID: 4B4195422742FC16
8 changed files with 42 additions and 137 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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
View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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