summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--02_exercise/CMakeLists.txt43
-rw-r--r--02_exercise/prompt_utils.c92
-rw-r--r--02_exercise/prompt_utils.h11
-rw-r--r--02_exercise/prompt_utils_test.c37
-rw-r--r--02_exercise/prompt_utils_test.h6
-rw-r--r--02_exercise/shell.c36
6 files changed, 213 insertions, 12 deletions
diff --git a/02_exercise/CMakeLists.txt b/02_exercise/CMakeLists.txt
index 0cf7c20..c2fa8d5 100644
--- a/02_exercise/CMakeLists.txt
+++ b/02_exercise/CMakeLists.txt
@@ -2,4 +2,45 @@ cmake_minimum_required(VERSION 3.5)
project(shell)
-add_executable(shell array.c shell.c) \ No newline at end of file
+add_executable(shell shell.c)
+target_link_libraries(shell PRIVATE array prompt_utils)
+add_compile_definitions(_GNU_SOURCE)
+
+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)
+set(CLANG_WARNINGS
+ -Wall
+ -Wextra # reasonable and standard
+ -Wshadow # warn the user if a variable declaration shadows one from a
+ # 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
+ -Wnull-dereference # warn if a null dereference is detected
+ -Wdouble-promotion # warn if float is implicit promoted to double
+ -Wformat=2 # warn on security issues around functions that format output
+ # (ie printf)
+ -Werror
+ )
+set(GCC_WARNINGS
+ ${CLANG_WARNINGS}
+ -Wmisleading-indentation # warn if indentation implies blocks where blocks
+ # do not exist
+ -Wduplicated-cond # warn if if / else chain has duplicated conditions
+ -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})
+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/prompt_utils.c b/02_exercise/prompt_utils.c
new file mode 100644
index 0000000..b7c836b
--- /dev/null
+++ b/02_exercise/prompt_utils.c
@@ -0,0 +1,92 @@
+#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) {
+
+ // easiest cases first
+ {
+ if (strcmp(from_dir, to_dir) == 0) {
+ char *return_value = malloc(2);
+ strcpy(return_value, ".");
+ return return_value;
+ }
+ }
+ {
+ if (strcmp("/", from_dir) == 0) {
+ char *return_value = malloc(strlen(to_dir) + 2);
+ return_value[0] = '.';
+ memcpy(return_value + 1, to_dir, strlen(to_dir) + 1);
+ 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, '/');
+
+ size_t from_dir_len = arrayLen(from_dir_indeces);
+ size_t to_dir_len = arrayLen(to_dir_indeces);
+
+ // finding the longest common substring
+ size_t array_len = from_dir_len < to_dir_len ? from_dir_len : to_dir_len;
+ size_t i = 0;
+ size_t common_position = 0;
+ for (; i < array_len - 1; ++i) {
+ if (from_dir_indeces[i + 1] != to_dir_indeces[i + 1]) {
+ break;
+ }
+ size_t index = from_dir_indeces[i];
+ size_t count = from_dir_indeces[i + 1] - from_dir_indeces[i];
+ if (strncmp(from_dir + index, to_dir + index, count) != 0) {
+ break;
+ }
+ common_position = from_dir_indeces[i + 1];
+ }
+
+ 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
+ size_t length = strlen(to_dir + common_position) + strlen("./") + 1;
+ return_value = malloc(length * sizeof(char));
+ strcpy(return_value, ".");
+ strcat(return_value, to_dir + common_position);
+ } else {
+ char const *const go_up = "/..";
+ size_t length = strlen(to_dir + common_position) + strlen("..") +
+ strlen(go_up) * (levels_up - 1);
+ return_value = malloc(length * sizeof(char));
+ strcpy(return_value, "..");
+ for (size_t j = 0; j < levels_up - 1; ++j) {
+ strcat(return_value, go_up);
+ }
+ if (strcmp("/", to_dir) != 0) {
+ strcat(return_value, to_dir + common_position);
+ }
+ }
+
+ arrayRelease((void *)from_dir_indeces);
+ arrayRelease((void *)to_dir_indeces);
+
+ return return_value;
+}
+
+size_t *get_seperator_indeces(char const *const text, char seperator) {
+ size_t *indeces;
+ arrayInit(indeces);
+ char const *current = text;
+ if (strchr(current, seperator) == NULL) {
+ arrayRelease(indeces);
+ return NULL;
+ }
+ while ((current = strchr(current, seperator)) != NULL) {
+ arrayPush(indeces) = current - text;
+ ++current;
+ }
+ arrayPush(indeces) = strlen(text);
+ return indeces;
+}
diff --git a/02_exercise/prompt_utils.h b/02_exercise/prompt_utils.h
new file mode 100644
index 0000000..a3a9d9e
--- /dev/null
+++ b/02_exercise/prompt_utils.h
@@ -0,0 +1,11 @@
+#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`
+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);
+#endif // PROMPT_UTILS_H_INCLUDED
diff --git a/02_exercise/prompt_utils_test.c b/02_exercise/prompt_utils_test.c
new file mode 100644
index 0000000..f648275
--- /dev/null
+++ b/02_exercise/prompt_utils_test.c
@@ -0,0 +1,37 @@
+#include "array.h"
+#include "prompt_utils.h"
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+void test_get_seperator_indeces() {
+ char const *const test_str = "/This/is/a/test";
+ size_t const *const result = get_seperator_indeces(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, '/');
+ 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, '/');
+ for (size_t i = 0; i < arrayLen(result2); i++) {
+ printf("%ld : %s \n", result2[i], to + result2[i]);
+ }
+ char const *const path = relative_path(from, to);
+ printf("The relative path of %s to %s is %s \n", from, to, path);
+
+ assert(strcmp(path, expected) == 0);
+}
+void test_relative_path() {
+ test_paths("/Test", "/Test/a", "./a");
+ test_paths("/abc/def/ghi", "/abc/def/ghi", ".");
+ test_paths("/Test", "/Test/a/asd", "./a/asd");
+ test_paths("/Test/b/c", "/Test/b", "..");
+ 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
diff --git a/02_exercise/prompt_utils_test.h b/02_exercise/prompt_utils_test.h
new file mode 100644
index 0000000..fbaabde
--- /dev/null
+++ b/02_exercise/prompt_utils_test.h
@@ -0,0 +1,6 @@
+#ifndef PROMPT_UTILS_TEST_H_INCLUDED
+#define PROMPT_UTILS_TEST_H_INCLUDED
+void test_get_seperator_indeces();
+void test_relative_path();
+
+#endif \ No newline at end of file
diff --git a/02_exercise/shell.c b/02_exercise/shell.c
index 1c03961..ed53897 100644
--- a/02_exercise/shell.c
+++ b/02_exercise/shell.c
@@ -1,28 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
-#include <sys/wait.h>
#include <unistd.h>
-
-#include "array.h"
#include <stdbool.h>
+#include <sys/wait.h>
-void print_prompt();
+#include "array.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[], unsigned timeout);
-int main(int argc, char* argv[]) {
+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) {
char *line = NULL;
size_t cap = 0;
- size_t length = 0;
+ __ssize_t length = 0;
- print_prompt();
+ printf("%s > ", prompt);
if ((length = getline(&line, &cap, stdin)) < 0) {
fprintf(stderr, "Failed to read from STDIN");
exit(-1);
@@ -39,9 +42,15 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "usage: cd <path>");
goto clean;
}
+
int ret = chdir(arguments[1]);
if (ret)
printf("[%i] ", ret);
+
+ free((void *)prompt);
+ char const *current_wd = get_current_dir_name();
+ prompt = relative_path(original_wd, current_wd);
+ free((void *)current_wd);
} else if (strcmp(arguments[0], "exit") == 0) {
done = true;
} else {
@@ -59,6 +68,9 @@ int main(int argc, char* argv[]) {
}
arrayRelease(arguments);
}
+
+ free((void *)original_wd);
+ free((void *)prompt);
}
int parse_line(char const *line, char ***parts, size_t *part_count) {
@@ -106,6 +118,7 @@ void print_prompt() {
}
int exec_command(const char *path, char *const argv[], unsigned timeout) {
+ timeout = timeout ^ timeout;
int pid;
int pipefd[2];
int status;
@@ -128,10 +141,11 @@ int exec_command(const char *path, char *const argv[], unsigned timeout) {
close(pipefd[1]);
- size_t length = 0;
+ __ssize_t length = 0;
while ((length = read(pipefd[0], buf, 10)) > 0) {
buf[length] = '\0';
- printf("%s", buf);
+ fprintf(stdout, "%s", buf);
+ fflush(stdout);
}
waitpid(pid, &status, 0);
@@ -139,4 +153,4 @@ int exec_command(const char *path, char *const argv[], unsigned timeout) {
close(pipefd[0]);
return WEXITSTATUS(status);
-} \ No newline at end of file
+}