diff --git a/apps/src/sh.c b/apps/src/sh.c index 9f79a976..f06c7814 100644 --- a/apps/src/sh.c +++ b/apps/src/sh.c @@ -16,7 +16,8 @@ typedef struct char* buffer; size_t size; size_t capacity; -} command; + int interactive; +} command_t; char** split_command_into_argv(const char* cmd) { @@ -78,30 +79,19 @@ void shell_execvp(char* pathname, char* const argv[]) void show_prompt() { - if (!username) - { - struct passwd* user = getpwuid(getuid()); - if (!user) - { - if (errno) perror("getpwuid"); - username = "??"; - } - else { username = user->pw_name; } - atexit(endpwent); - } if (WEXITSTATUS(status)) { printf("%d [%s]> ", WEXITSTATUS(status), username); } else printf("[%s]> ", username); } -int command_matches(command* cmd, const char* string) +int command_matches(command_t* cmd, const char* string) { if (cmd->size <= strlen(string)) // cmd->size includes null terminator return 0; return strncmp(cmd->buffer, string, strlen(string)) == 0; } -int command_matches_exactly(command* cmd, const char* string) +int command_matches_exactly(command_t* cmd, const char* string) { if (cmd->size <= strlen(string)) // cmd->size includes null terminator return 0; @@ -109,7 +99,7 @@ int command_matches_exactly(command* cmd, const char* string) return strncmp(cmd->buffer, string, strlen(string)) == 0; } -int command_match_builtins(command* cmd) +int command_match_builtins(command_t* cmd) { if (command_matches(cmd, "exit ")) { exit(atoi(cmd->buffer + 5)); } if (command_matches_exactly(cmd, "exit")) { exit(0); } @@ -126,7 +116,7 @@ int command_match_builtins(command* cmd) return 0; } -void command_expand(command* cmd, long new_capacity) +void command_expand(command_t* cmd, long new_capacity) { char* buffer = realloc(cmd->buffer, new_capacity); if (!buffer) @@ -138,38 +128,46 @@ void command_expand(command* cmd, long new_capacity) cmd->capacity = new_capacity; } -void command_push(command* cmd, char c) +void command_push(command_t* cmd, char c) { if (cmd->size == cmd->capacity) command_expand(cmd, cmd->capacity + 8); cmd->buffer[cmd->size] = c; cmd->size++; } -void command_pop(command* cmd) +void command_pop(command_t* cmd) { cmd->size--; } -void command_init(command* cmd) +void command_init(command_t* cmd) { cmd->buffer = malloc(5); cmd->capacity = 5; cmd->size = 0; } -void command_clear(command* cmd) +void command_clear(command_t* cmd) { free(cmd->buffer); return command_init(cmd); } -void command_execute(command* cmd) +void process_execute_command(const char* command) +{ + char** argv = split_command_into_argv(command); + shell_execvp(argv[0], argv); + perror(argv[0]); + exit(127); +} + +void command_execute(command_t* cmd) { command_push(cmd, '\0'); if (command_match_builtins(cmd)) { command_clear(cmd); - show_prompt(); + if (cmd->interactive) show_prompt(); return; } pid_t child = fork(); @@ -177,60 +175,54 @@ void command_execute(command* cmd) { perror("fork"); command_clear(cmd); - show_prompt(); + if (cmd->interactive) show_prompt(); return; } - if (child == 0) - { - char** argv = split_command_into_argv(cmd->buffer); - shell_execvp(argv[0], argv); - perror(argv[0]); - exit(127); - } + if (child == 0) process_execute_command(cmd->buffer); pid_t result = waitpid(child, &status, 0); if (result < 0) { perror("waitpid"); command_clear(cmd); - show_prompt(); + if (cmd->interactive) show_prompt(); return; } int exit_status = WEXITSTATUS(status); if (exit_status == -2 || exit_status == -3) printf("(PID %ld) Segmentation fault\n", result); if (exit_status == -1) printf("(PID %ld) Aborted\n", result); command_clear(cmd); - show_prompt(); + if (cmd->interactive) show_prompt(); } -void command_concat_char(command* cmd, char c) +void command_concat_char(command_t* cmd, char c) { if (c == '\b') { if (cmd->size != 0) { - putchar(c); + if (cmd->interactive) putchar(c); command_pop(cmd); } } else if (c == '\n') { - putchar(c); + if (cmd->interactive) putchar(c); if (cmd->size == 0) { status = 0; - show_prompt(); + if (cmd->interactive) show_prompt(); } else command_execute(cmd); } else { - putchar(c); + if (cmd->interactive) putchar(c); command_push(cmd, c); } } -void command_concat(command* cmd, const char* str) +void command_concat(command_t* cmd, const char* str) { while (*str) { @@ -239,13 +231,15 @@ void command_concat(command* cmd, const char* str) } } -int main() +void shell_interactive() { show_prompt(); - command shell_command; + command_t shell_command; command_init(&shell_command); + shell_command.interactive = 1; + while (1) { int c = getchar(); @@ -254,11 +248,112 @@ int main() if (ferror(stdin)) { perror("getchar"); - return 1; + exit(EXIT_FAILURE); } - if (feof(stdin)) return 0; + if (feof(stdin)) exit(EXIT_SUCCESS); assert(false); // we should never get here } command_concat_char(&shell_command, (char)c); } +} + +void shell_read_from_file(const char* pathname) +{ + FILE* fp = fopen(pathname, "r"); + if (!fp) + { + perror("sh"); + exit(EXIT_FAILURE); + } + + command_t file_command; + command_init(&file_command); + + file_command.interactive = 0; + + char buffer[BUFSIZ]; + while (fgets(buffer, BUFSIZ, fp)) + { + command_concat(&file_command, buffer); + if (feof(fp)) break; + } + + if (file_command.size > 0) // last line of file, does not end with newline + { + command_execute(&file_command); + } + + fclose(fp); +} + +void shell_execute_command(const char* command) +{ + command_t cmd; + cmd.buffer = strdup(command); + cmd.size = strlen(command) + 1; + if (command_match_builtins(&cmd)) return; + command_clear(&cmd); + process_execute_command(command); +} + +void fetch_username() +{ + struct passwd* user = getpwuid(getuid()); + if (!user) + { + perror("getpwuid"); + exit(EXIT_FAILURE); + } + username = user->pw_name; + endpwent(); +} + +int main(int argc, char** argv) +{ + fetch_username(); + + if (argc == 1) shell_interactive(); + else if (argc == 2) + { + if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) + { + puts("Luna sh version 0.1"); // FIXME: Store the version somewhere, or use the kernel's version. + return 0; + } + + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + { + printf("To use interactively: %s\n", argv[0]); + printf("To run a script: %s [script-name]\n", argv[0]); + printf("To get help: %s --help\n", argv[0]); + printf("To show the version: %s --version\n", argv[0]); + printf("To run a command: %s -c [command]\n", argv[0]); + return 0; + } + + if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--command")) + { + fprintf(stderr, "Usage: %s %s [command]\n", argv[0], argv[1]); + fprintf(stderr, "Use the --help flag for more help.\n"); + return 1; + } + + shell_read_from_file(argv[1]); + } + else if (argc == 3) + { + if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--command")) shell_execute_command(argv[2]); + else + { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + fprintf(stderr, "Use the --help flag for more help.\n"); + return 1; + } + } + else + { + fprintf(stderr, "%s: too many arguments\n", argv[0]); + fprintf(stderr, "Use the --help flag for more help.\n"); + return 1; + } } \ No newline at end of file