/** @file 410user/progs/shell.c * @author ? * @brief The shell. * @public yes * @for p2 p3 * @covers fork exec wait set_status vanish print ls * @status done */ /* --- Since we're not calling thr_init() --- */ /* (this is sleazy violence, *not* good form) */ #define malloc _malloc #define calloc _calloc #define realloc _realloc #define free _free /* --- Includes --- */ #include #include #include #include #include #include /* --- Defines --- */ #define MAX_LENGTH 1024 /* --- Initialized Global Strings --- */ char prompt[] = "[410-shell]$ "; char startmsg[] = "Starting shell...\n"; char exitmsg[] = "Exiting shell...\n"; char forkerrmsg[] = "Shell: Cannot fork process.\n"; char waitfailed[] = "wait() failed\n"; char finished[] = "Process finished\n"; char too_long[] = "That string is too long.\n"; char null_check[] = "I've lost null termination!\n"; char nothing[] = "Readline returned nothing!\n"; /* --- These are global only to avoid stack growth --- */ /* (unclean, but lets the shell run before swexn() and new_pages()) */ char buf[MAX_LENGTH]; char *cmd_argv[MAX_LENGTH]; char separators[4] = { ' ', '\t', '\n' }; static void print_ls(void); static void sort_names(char **names, int num_entries); static int mystical_readline(int len, char *buf); static int console_height(void); /* Not all kernels support getchar(). * * Try to use it, but if it doesn't work fall back to readline(). */ static int fallback_getchar(void) { char buf[2]; mystical_readline(2, buf); return (unsigned char)buf[0]; // cast prevents sign extension } static int try_getchar(void) { static int missing_getchar = 0; int c; if (missing_getchar || (c = getchar()) < 0) { missing_getchar = 1; return fallback_getchar(); } return c; } #define getchar try_getchar /* --- Main --- */ int main(int argc, char *argv[]) { int n; int pid; int res; int j; int ret; print(strlen(startmsg), startmsg); while(1) { bzero(buf, MAX_LENGTH); print(strlen(prompt), prompt); n = mystical_readline(MAX_LENGTH - 1, buf); if (n == 0) { /* Empty result; this shouldn't happen. */ print(strlen(nothing), nothing); continue; } else if(n >= MAX_LENGTH) { /* line too big -- kernel declared that it clobbered memory */ print(strlen(too_long), too_long); exit(-1); } else if(buf[MAX_LENGTH - 1] != '\0') { /* Overflow -- kernel tapdanced on random memory! */ print(strlen(null_check), null_check); exit(-1); } buf[n] = '\0'; j = 0; if(!(cmd_argv[j++] = strtok(buf, separators))) { continue; } if((strcmp(cmd_argv[0], "exit") == 0)) { print(strlen(exitmsg), exitmsg); exit(0); } if((strcmp(cmd_argv[0], "ls") == 0)) { print_ls(); continue; } while((cmd_argv[j++] = strtok(NULL, separators))); pid = fork(); if(pid < 0) { print(sizeof(forkerrmsg), forkerrmsg); continue; } if(pid == 0) { exec(cmd_argv[0], cmd_argv); exit(-1); } else { if((ret = wait(&res)) < 0) { printf("\nshell: wait on process %d failed!\n", pid); } else { printf("\nshell: process %d finished with exit status %d\n", ret, res); } } } /* while loop */ } /* emulate the old ls() system call using readfile() */ int ls(int size, char *buf) { int i; int num_files = 0; int amt_read; amt_read = readfile(".", buf, size, 0); if (amt_read < 0) return amt_read; /* ls() returned failure if the buffer wasn't big enough for the * entire listing. So if readfile() didn't return short (that is, * there might be more), fail. */ if (amt_read == size) return -1; /* Now scan through the buffer counting null terminators. * Skip the one at the end. */ for (i = 0; i < amt_read - 1; i++) { if (buf[i] == '\0') { num_files++; } } return num_files; } static void print_ls() { char *buf = NULL; static int size = 16; int num_entries; char ** names = NULL; char *bufptr; int index; int len; /* get the array of entries */ buf = (char *)malloc(sizeof(char) * size); if (buf == NULL) { printf("Can't malloc(%d)!\n", sizeof(char) * size); goto cleanup; } while ((num_entries = ls(size, buf)) < 0) { size *= 2; char *nbuf = (char *)realloc(buf, sizeof(char) * size); if (nbuf == NULL) { printf("Can't realloc(%d)!\n", sizeof(char) * size); goto cleanup; } else { buf = nbuf; } } names = (char **)calloc(num_entries, sizeof(char *)); if (names == NULL) { printf("Can't calloc(%d)!\n", num_entries * sizeof(char *)); goto cleanup; } /* get pointers to each string in the array */ bufptr = buf; index = 0; while ((len = strlen(bufptr)) > 0) { names[index++] = bufptr; bufptr += len + 1; } sort_names(names, num_entries); printf("Listing the user tests\n"); for (index = 0; index < num_entries; index++) { if (index != 0 && index % (console_height() - 2) == 0) { printf("Hit a key to continue\n"); getchar(); } printf("%s\n", names[index]); } cleanup: if (buf) { free(buf); buf = NULL; } if (names) { free(names); names = NULL; } } static void sort_names(char **names, int num_entries) { /* I invented this cool new sorting algorithm! It's really fast! */ int swapped; int i; do { swapped = 0; for (i = 0; i < num_entries - 1; i++) { if (strcmp(names[i], names[i+1]) > 0) { char *tmp = names[i]; names[i] = names[i+1]; names[i+1] = tmp; swapped = 1; } } } while (swapped); } static int mystical_readline(int len, char *buf) { if (len <= 0) { return 0; } buf[0] = '\0'; /* If we're in the test harness, this does something very special! */ sim_call(0x04108005, buf, len); return (buf[0] ? strlen(buf) : readline(len, buf)); } static int console_height() { static int console_height = -1; if (console_height == -1) { /* determine the height of the console */ int curstartrow, curstartcol; get_cursor_pos(&curstartrow, &curstartcol); console_height = 0; while (set_cursor_pos(console_height, 0) == 0) { console_height++; } set_cursor_pos(curstartrow, curstartcol); } return console_height; }