diff options
Diffstat (limited to '02_exercise')
-rw-r--r-- | 02_exercise/CMakeLists.txt | 27 | ||||
-rw-r--r-- | 02_exercise/process.c | 173 | ||||
-rw-r--r-- | 02_exercise/process.h | 29 | ||||
-rw-r--r-- | 02_exercise/process_test.c | 63 | ||||
-rw-r--r-- | 02_exercise/prog.c | 2 | ||||
-rw-r--r-- | 02_exercise/prompt_utils.c | 32 | ||||
-rw-r--r-- | 02_exercise/prompt_utils.h | 9 | ||||
-rw-r--r-- | 02_exercise/prompt_utils_test.c | 18 | ||||
-rw-r--r-- | 02_exercise/prompt_utils_test.h | 5 | ||||
-rw-r--r-- | 02_exercise/shell.c | 140 |
10 files changed, 340 insertions, 158 deletions
diff --git a/02_exercise/CMakeLists.txt b/02_exercise/CMakeLists.txt index c2fa8d5..ca67d1a 100644 --- a/02_exercise/CMakeLists.txt +++ b/02_exercise/CMakeLists.txt @@ -1,9 +1,14 @@ cmake_minimum_required(VERSION 3.5) -project(shell) +project(shell C) +set(CMAKE_C_COMPILER gcc) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED True) + +add_executable(prog prog.c) add_executable(shell shell.c) -target_link_libraries(shell PRIVATE array prompt_utils) +target_link_libraries(shell PRIVATE array prompt_utils process) add_compile_definitions(_GNU_SOURCE) add_library(array array.c) @@ -11,8 +16,17 @@ add_library(array array.c) add_library(prompt_utils prompt_utils.c) target_link_libraries(prompt_utils PRIVATE array) -set(CMAKE_C_STANDARD gnu11) -set(CMAKE_C_STANDARD_REQUIRED True) +add_executable(prompt_utils_test prompt_utils_test.c) +target_link_libraries(prompt_utils_test PRIVATE prompt_utils array) + +add_library(process process.c) +target_link_libraries(process PRIVATE array) + +add_executable(process_test process_test.c) +target_link_libraries(process_test PRIVATE process) + + + set(CLANG_WARNINGS -Wall -Wextra # reasonable and standard @@ -20,8 +34,6 @@ set(CLANG_WARNINGS # parent context -Wcast-align # warn for potential performance problem casts -Wunused # warn on anything being unused - -Woverloaded-virtual # warn if you overload (not override) a virtual - # function -Wpedantic # warn if non-standard C++ is used -Wconversion # warn on type conversions that may lose data -Wsign-conversion # warn on sign conversions @@ -39,8 +51,7 @@ set(GCC_WARNINGS -Wduplicated-branches # warn if if / else branches have duplicated code -Wlogical-op # warn about logical operations being used where bitwise were # probably wanted - -Wuseless-cast # warn if you perform a cast to the same type ) -set(PROJECT_WARNINGS ${CLANG_WARNINGS}) +set(PROJECT_WARNINGS ${GCC_WARNINGS}) target_compile_options(shell INTERFACE ${PROJECT_WARNINGS}) target_compile_options(prompt_utils INTERFACE ${PROJECT_WARNINGS})
\ No newline at end of file diff --git a/02_exercise/process.c b/02_exercise/process.c new file mode 100644 index 0000000..093b712 --- /dev/null +++ b/02_exercise/process.c @@ -0,0 +1,173 @@ +#include <wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "array.h" +#include "process.h" + +// given a substring of a line tries to parse out as much information as possible +int parse_command(char const *line, char const *end, Process *p) { + char *part; + char **local_parts; + if (arrayInit(part) != 0 || arrayInit(local_parts) != 0) { + fprintf(stderr, "Failed to prepare new part / parts array whilst parsing line"); + return -1; + } + + for (size_t i = 0; line + i < end; ++i) { + char c = line[i]; + if (c == ' ' || c == '\0') { + if (arrayLen(part) == 0) + continue; + + arrayPush(part) = '\0'; + arrayPush(local_parts) = part; + + if (c == '\0') { + arrayPush(local_parts) = NULL; + break; + } else { + arrayInit(part); + } + } else { + arrayPush(part) = c; + } + } + + if (arrayLen(part) == 0) { + arrayRelease(part); + } else { + arrayPush(local_parts) = part; + } + + arrayPush(local_parts) = NULL; + p->argc = arrayLen(local_parts) - 1; + p->argv = local_parts; + + return 0; +} + +int parse_line(char const *const line, Process **processes) { + // splits the line at | and then parses the commands + int ret_code; + + if (arrayInit(*processes) != 0) { + perror("Failed to initialize processes array"); + return -1; + } + + bool done = false; + char const *cursor = line; + while (!done) { + Process p = {.in_fd = 0, .out_fd = 0, .pid = 0, .blocking = true}; + char const *end = strchr(cursor, '|'); + + if (end == NULL) { + done = true; + end = line + strlen(line); + } + + if ((ret_code = parse_command(cursor, end, &p)) != 0) { + return ret_code; + } + + arrayPush(*processes) = p; + cursor = end + 1; + } + + size_t p_len = arrayLen(*processes); + Process *last = *processes + (p_len - 1); + + // linking up all processes as we currently only have pipes for multiple commands + for (size_t i = 0; i < p_len - 1; ++i) { + int fds[2]; + if (pipe(fds) != 0) { + perror("Failed to create pipe"); + return -1; + } + (*processes)[i].out_fd = fds[1]; + (*processes)[i + 1].in_fd = fds[0]; + } + + // setting all processes to non blocking when + if (strcmp(last->argv[last->argc - 1], "&") == 0) { + arrayPop(last->argv) = NULL; + last->argc = last->argc - 1; + last->argv[last->argc] = NULL; + + for (size_t i = 0; i < p_len; ++i) { + (*processes)[i].blocking = false; + } + } + return ret_code; +} + +int exec_command(Process p) { + int status; + + if (!p.blocking) { + // if the process is to be put in the background let a fork handle it + if (fork() != 0) + return 0; + } + + if ((p.pid = fork()) == 0) { + if (p.in_fd != 0) { + dup2(p.in_fd, 0); + close(p.in_fd); + } + if (p.out_fd != 0) { + dup2(p.out_fd, 1); + close(p.out_fd); + } + execvp(p.argv[0], p.argv); + fprintf(stderr, "could not execute \"%s\"\n", p.argv[0]); + fflush(stderr); + exit(-1); + } + + if (p.pid < 0) { + fprintf(stderr, "no fork\n"); + exit(-2); + } + + if (p.in_fd != 0) { + close(p.in_fd); + } + + if (p.out_fd != 0) { + close(p.out_fd); + } + + if (!p.blocking) + printf("[%i]\n", p.pid); + + if (waitpid(p.pid, &status, 0) < 0) { + fprintf(stderr, "wait error'ed out\n"); + } + + if (!p.blocking) { + printf("[%i] TERMINATED\n", p.pid); + printf("[%i] EXIT STATUS: %i\n", p.pid, status); // TODO: exit status is wrong + exit(0); + } + + return WEXITSTATUS(status); +} + +void free_processes(Process **pr) { + Process *processes = *pr; + while (!arrayIsEmpty(processes)) { + Process p = arrayPop(processes); + while (!arrayIsEmpty(p.argv)) { + char *tmp = arrayPop(p.argv); + if (tmp) + arrayRelease(tmp); + } + arrayRelease(p.argv); + } + arrayRelease(processes); + *pr = NULL; +}
\ No newline at end of file diff --git a/02_exercise/process.h b/02_exercise/process.h new file mode 100644 index 0000000..3b7e833 --- /dev/null +++ b/02_exercise/process.h @@ -0,0 +1,29 @@ +#ifndef SHELL_PROCESS_H +#define SHELL_PROCESS_H +#include <stdbool.h> +#include <stddef.h> + +typedef struct { + char **argv; + size_t argc; + int in_fd; + int out_fd; + int pid; + bool blocking; +} Process; + +/* + * Parses the given line and creates an array of processes at *processes + * Expects tail -F file | grep panic & to mean that both processes should + * run in the background + */ +int parse_line(char const *line, Process ** processes); + +/* + * returns the return code of the executed program + */ +int exec_command(Process p); + +void free_processes(Process ** processes); + +#endif // SHELL_PROCESS_H diff --git a/02_exercise/process_test.c b/02_exercise/process_test.c new file mode 100644 index 0000000..a7b0b9c --- /dev/null +++ b/02_exercise/process_test.c @@ -0,0 +1,63 @@ +#include <assert.h> +#include <string.h> + +#include "array.h" +#include "process.h" + +void test_simple_case() { + Process *processes = NULL; + parse_line("cat my_txt ", &processes); + assert(arrayLen(processes) == 1); + assert(arrayLen(processes[0].argv) == 3); + assert(strcmp(processes[0].argv[0], "cat") == 0); + assert(strcmp(processes[0].argv[1], "my_txt") == 0); + free_processes(&processes); +} + +void test_detached() { + Process *processes = NULL; + parse_line("cat my_txt &", &processes); + assert(arrayLen(processes) == 1); + assert(arrayLen(processes[0].argv) == 3); + assert(strcmp(processes[0].argv[0], "cat") == 0); + assert(strcmp(processes[0].argv[1], "my_txt") == 0); + assert(processes[0].blocking == false); + free_processes(&processes); +} + + +void test_pipe() { + Process *processes = NULL; + parse_line("echo my_txt | grep txt", &processes); + assert(arrayLen(processes) == 2); + assert(arrayLen(processes[0].argv) == 3); + assert(strcmp(processes[0].argv[0], "echo") == 0); + assert(strcmp(processes[0].argv[1], "my_txt") == 0); + assert(processes[0].out_fd != 0); + assert(arrayLen(processes[1].argv) == 3); + assert(strcmp(processes[1].argv[0], "grep") == 0); + assert(strcmp(processes[1].argv[1], "txt") == 0); + assert(processes[1].in_fd != 0); + free_processes(&processes); +} + +void test_ls(){ + Process *processes = NULL; + parse_line("ls", &processes); + assert(arrayLen(processes) == 1); + Process p = processes[0]; + assert(arrayLen(p.argv)==2); + assert(p.argc == 1); + assert(strcmp(p.argv[0], "ls") == 0); + assert(p.argc[p.argv] == NULL); + free_processes(&processes); +} + +/* +int main() { + test_ls(); + test_simple_case(); + test_detached(); + test_pipe(); + return 0; +}*/ diff --git a/02_exercise/prog.c b/02_exercise/prog.c index 41c0545..efd2527 100644 --- a/02_exercise/prog.c +++ b/02_exercise/prog.c @@ -1,6 +1,6 @@ +#include <time.h> #include <stdio.h> #include <stdlib.h> -#include <time.h> #include <unistd.h> static unsigned int hash(const void* key, size_t size) { diff --git a/02_exercise/prompt_utils.c b/02_exercise/prompt_utils.c index d43a6b6..168c088 100644 --- a/02_exercise/prompt_utils.c +++ b/02_exercise/prompt_utils.c @@ -1,13 +1,10 @@ -#include "prompt_utils.h" - -#include "array.h" -#include <errno.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> -char const *relative_path(char const *const from_dir, char const *const to_dir) { +#include "array.h" +#include "prompt_utils.h" +char const *relative_path(char const *const from_dir, char const *const to_dir) { // easiest cases first { if (strcmp(from_dir, to_dir) == 0) { @@ -24,9 +21,10 @@ char const *relative_path(char const *const from_dir, char const *const to_dir) return return_value; } } - // Splitting path into pieces so we can do strcmp on each of them - size_t const *const from_dir_indeces = get_seperator_indeces(from_dir, '/'); - size_t const *const to_dir_indeces = get_seperator_indeces(to_dir, '/'); + + // splitting path into pieces so we can do strcmp on each of them + size_t const *const from_dir_indeces = get_separator_indices(from_dir, '/'); + size_t const *const to_dir_indeces = get_separator_indices(to_dir, '/'); size_t from_dir_len = arrayLen(from_dir_indeces); size_t to_dir_len = arrayLen(to_dir_indeces); @@ -50,7 +48,7 @@ char const *relative_path(char const *const from_dir, char const *const to_dir) size_t levels_up = from_dir_len - i - 1; char *return_value; if (levels_up == 0) { - // Equal dirs for whole length of path and not equal => subdir + // equal dirs for whole length of path and not equal => subdir size_t length = strlen(to_dir + common_position) + strlen("./") + 1; return_value = malloc(length * sizeof(char)); strcpy(return_value, "."); @@ -75,18 +73,18 @@ char const *relative_path(char const *const from_dir, char const *const to_dir) return return_value; } -size_t *get_seperator_indeces(char const *const text, char seperator) { - size_t *indeces; - arrayInit(indeces); +size_t *get_separator_indices(char const *const text, char seperator) { + size_t *indices; + arrayInit(indices); char const *current = text; if (strchr(current, seperator) == NULL) { - arrayRelease(indeces); + arrayRelease(indices); return NULL; } while ((current = strchr(current, seperator)) != NULL) { - arrayPush(indeces) = current - text; + arrayPush(indices) = (size_t) (current - text); ++current; } - arrayPush(indeces) = strlen(text); - return indeces; + arrayPush(indices) = strlen(text); + return indices; } diff --git a/02_exercise/prompt_utils.h b/02_exercise/prompt_utils.h index a3a9d9e..36d065d 100644 --- a/02_exercise/prompt_utils.h +++ b/02_exercise/prompt_utils.h @@ -1,11 +1,10 @@ #ifndef PROMPT_UTILS_H_INCLUDED #define PROMPT_UTILS_H_INCLUDED -#include <stddef.h> - -// Returns the relative path to get from the `from_dir` to the `to_dir` +// returns the relative path to get from the `from_dir` to the `to_dir` char const *relative_path(char const *from_dir, char const *to_dir); -// Returns the position of each occurence of the seperator -size_t *get_seperator_indeces(char const *text, char seperator); +// returns the position of each occurrence of the separator +size_t *get_separator_indices(char const *text, char separator); + #endif // PROMPT_UTILS_H_INCLUDED diff --git a/02_exercise/prompt_utils_test.c b/02_exercise/prompt_utils_test.c index f648275..8bc29de 100644 --- a/02_exercise/prompt_utils_test.c +++ b/02_exercise/prompt_utils_test.c @@ -4,20 +4,20 @@ #include <stdio.h> #include <string.h> -void test_get_seperator_indeces() { +void test_get_separator_indices() { char const *const test_str = "/This/is/a/test"; - size_t const *const result = get_seperator_indeces(test_str, '/'); + size_t const *const result = get_separator_indices(test_str, '/'); for (size_t i = 0; i < arrayLen(result); i++) { printf("%ld : %s \n", result[i], test_str + result[i]); } } void test_paths(char const *const from, char const *const to, char const *const expected) { - size_t const *const result = get_seperator_indeces(from, '/'); + size_t const *const result = get_separator_indices(from, '/'); for (size_t i = 0; i < arrayLen(result); i++) { printf("%ld : %s \n", result[i], from + result[i]); } - size_t const *const result2 = get_seperator_indeces(to, '/'); + size_t const *const result2 = get_separator_indices(to, '/'); for (size_t i = 0; i < arrayLen(result2); i++) { printf("%ld : %s \n", result2[i], to + result2[i]); } @@ -34,4 +34,12 @@ void test_relative_path() { test_paths("/", "/Test/a/asd", "./Test/a/asd"); test_paths("/Test/c", "/Test/b", "../b"); test_paths("/B/C", "/", "../.."); -}
\ No newline at end of file +} + +/* +int main(void) { + test_relative_path(); + test_get_separator_indices(); + return 0; +} +*/ diff --git a/02_exercise/prompt_utils_test.h b/02_exercise/prompt_utils_test.h index fbaabde..59717d0 100644 --- a/02_exercise/prompt_utils_test.h +++ b/02_exercise/prompt_utils_test.h @@ -1,6 +1,7 @@ #ifndef PROMPT_UTILS_TEST_H_INCLUDED #define PROMPT_UTILS_TEST_H_INCLUDED -void test_get_seperator_indeces(); + +void test_get_separator_indices(); void test_relative_path(); -#endif
\ No newline at end of file +#endif diff --git a/02_exercise/shell.c b/02_exercise/shell.c index 7032a99..f456465 100644 --- a/02_exercise/shell.c +++ b/02_exercise/shell.c @@ -3,27 +3,23 @@ #include <string.h> #include <unistd.h> #include <stdbool.h> -#include <sys/wait.h> #include "array.h" +#include "process.h" #include "prompt_utils.h" -int parse_line(char const *line, char ***parts, size_t *part_count); - -// returns the return code of the executed program -int exec_command(const char *path, char *const argv[], bool background); int main(void) { + chdir("."); setvbuf(stdout, NULL, _IONBF, 0); char const *const original_wd = get_current_dir_name(); char const *prompt = relative_path(original_wd, original_wd); bool done = false; while (!done) { - bool bg = false; char *line = NULL; size_t cap = 0; - __ssize_t length; + __ssize_t length = 0; printf("%s > ", prompt); if ((length = getline(&line, &cap, stdin)) < 0) { @@ -37,29 +33,20 @@ int main(void) { line[length - 1] = '\0'; // cut the line feed - char **arguments = NULL; - size_t argument_count; - parse_line(line, &arguments, &argument_count); - - if (argument_count <= 0) - goto clean; + Process * processes = NULL; + parse_line(line, &processes); - char *command = arguments[0]; - char *last_arg = arguments[argument_count - 2]; + if (strcmp(processes[0].argv[0], "cd") == 0) { - if (strcmp(last_arg, "&") == 0) { - bg = true; - arrayRelease(arguments[argument_count - 2]); - arguments[argument_count - 2] = NULL; - } - - if (strcmp(command, "cd") == 0) { - if (arrayLen(arguments) != 3) { + if(arrayLen(processes) != 1) { + perror("Can't chain cd with other processes"); + } + if (arrayLen(processes[0].argv) != 3) { fprintf(stderr, "usage: cd <path>"); goto clean; } - int ret = chdir(arguments[1]); + int ret = chdir(processes[0].argv[1]); if (ret) printf("[%i] ", ret); @@ -67,110 +54,23 @@ int main(void) { char const *current_wd = get_current_dir_name(); prompt = relative_path(original_wd, current_wd); free((void *)current_wd); - } else if (strcmp(command, "exit") == 0) { + } else if (strcmp(processes[0].argv[0], "exit") == 0) { done = true; } else { - int ret = exec_command(command, arguments, bg); - if (ret) - printf("[%i] ", ret); + int ret; + for (size_t i = 0; i < arrayLen(processes); ++i) { + ret = exec_command(processes[i]); + if (ret) + printf("[%i] ", ret); + } + } clean: free((void *)line); - while (arrayLen(arguments) > 0) { - char *tmp = arrayPop(arguments); - if (tmp) - arrayRelease(tmp); - } - arrayRelease(arguments); + free_processes(&processes); } free((void *)original_wd); free((void *)prompt); } - -int parse_line(char const *line, char ***parts, size_t *part_count) { - char *part; - char **local_parts; - - if (arrayInit(part) != 0 || arrayInit(local_parts) != 0) { - fprintf(stderr, "Failed to prepare new part / parts array whilst parsing line"); - return -1; - } - - for (size_t i = 0; i < strlen(line) + 1; ++i) { - char c = line[i]; - if (c == ' ' || c == '\0') { - if (arrayLen(part) == 0) - continue; - arrayPush(part) = '\0'; - arrayPush(local_parts) = part; - if (c == '\0') { - arrayPush(local_parts) = NULL; - break; - } else { - arrayInit(part); - } - } else { - arrayPush(part) = c; - } - } - - *part_count = arrayLen(local_parts); - *parts = local_parts; - - return 0; -} - -int exec_command(const char *path, char *const argv[], bool background) { - int pid; - int pipefd[2]; - int status; - char buf[512]; - - pipe(pipefd); - if ((pid = fork()) == 0) { - close(pipefd[0]); - dup2(pipefd[1], 1); - close(pipefd[1]); - execvp(path, argv); - fprintf(stderr, "could not execute \"%s\"\n", path); - fflush(stderr); - exit(-1); - } - - if (pid < 0) { - fprintf(stderr, "no fork\n"); - exit(-2); - } - - close(pipefd[1]); - - int new_pid; - if (background) { - new_pid = fork(); - } - - if (!background || new_pid == 0) { - if (background) - printf("[%i]\n", pid); - - __ssize_t length; - while ((length = read(pipefd[0], buf, 10)) > 0) { - buf[length] = '\0'; - printf("%s", buf); - } - - waitpid(pid, &status, 0); - - if (background) { - printf("[%i] TERMINATED\n", pid); - printf("[%i] EXIT STATUS: %i\n", pid, status); - exit(0); - } - } - - close(pipefd[0]); - - return WEXITSTATUS(status); -} |