From 04576dc2a3f761eb041b808b56f13a58052e7655 Mon Sep 17 00:00:00 2001 From: Stefan Zabka Date: Sun, 24 May 2020 12:19:52 +0200 Subject: Moved back to 02_exercise --- 02_exercise/.gitignore | 3 + 02_exercise/CMakeLists.txt | 34 +++++ 02_exercise/Makefile | 32 +++++ 02_exercise/array.c | 59 +++++++++ 02_exercise/array.h | 142 ++++++++++++++++++++ 02_exercise/beispiele/dup/Makefile | 18 +++ 02_exercise/beispiele/dup/dup.c | 51 ++++++++ 02_exercise/beispiele/fork_example/Makefile | 18 +++ 02_exercise/beispiele/fork_example/fork.c | 36 +++++ 02_exercise/beispiele/fork_signal/Makefile | 19 +++ 02_exercise/beispiele/fork_signal/fork.c | 39 ++++++ 02_exercise/beispiele/fork_signal/signal.c | 21 +++ 02_exercise/beispiele/pipe_bidirect/Makefile | 19 +++ 02_exercise/beispiele/pipe_bidirect/log.txt | 19 +++ 02_exercise/beispiele/pipe_bidirect/pingpong.c | 125 ++++++++++++++++++ 02_exercise/beispiele/pipe_example/Makefile | 18 +++ 02_exercise/beispiele/pipe_example/pipe.c | 60 +++++++++ 02_exercise/process.c | 173 +++++++++++++++++++++++++ 02_exercise/process.h | 29 +++++ 02_exercise/process_test.c | 63 +++++++++ 02_exercise/prog.c | 52 ++++++++ 02_exercise/prompt_utils.c | 90 +++++++++++++ 02_exercise/prompt_utils.h | 10 ++ 02_exercise/prompt_utils_test.c | 45 +++++++ 02_exercise/prompt_utils_test.h | 7 + 02_exercise/shell.c | 77 +++++++++++ CMakeLists.txt | 2 +- shell/.gitignore | 3 - shell/CMakeLists.txt | 34 ----- shell/Makefile | 32 ----- shell/array.c | 59 --------- shell/array.h | 142 -------------------- shell/beispiele/dup/Makefile | 18 --- shell/beispiele/dup/dup.c | 51 -------- shell/beispiele/fork_example/Makefile | 18 --- shell/beispiele/fork_example/fork.c | 36 ----- shell/beispiele/fork_signal/Makefile | 19 --- shell/beispiele/fork_signal/fork.c | 39 ------ shell/beispiele/fork_signal/signal.c | 21 --- shell/beispiele/pipe_bidirect/Makefile | 19 --- shell/beispiele/pipe_bidirect/log.txt | 19 --- shell/beispiele/pipe_bidirect/pingpong.c | 125 ------------------ shell/beispiele/pipe_example/Makefile | 18 --- shell/beispiele/pipe_example/pipe.c | 60 --------- shell/process.c | 173 ------------------------- shell/process.h | 29 ----- shell/process_test.c | 63 --------- shell/prog.c | 52 -------- shell/prompt_utils.c | 90 ------------- shell/prompt_utils.h | 10 -- shell/prompt_utils_test.c | 45 ------- shell/prompt_utils_test.h | 7 - shell/shell.c | 77 ----------- 53 files changed, 1260 insertions(+), 1260 deletions(-) create mode 100644 02_exercise/.gitignore create mode 100644 02_exercise/CMakeLists.txt create mode 100644 02_exercise/Makefile create mode 100644 02_exercise/array.c create mode 100644 02_exercise/array.h create mode 100644 02_exercise/beispiele/dup/Makefile create mode 100644 02_exercise/beispiele/dup/dup.c create mode 100644 02_exercise/beispiele/fork_example/Makefile create mode 100644 02_exercise/beispiele/fork_example/fork.c create mode 100644 02_exercise/beispiele/fork_signal/Makefile create mode 100644 02_exercise/beispiele/fork_signal/fork.c create mode 100644 02_exercise/beispiele/fork_signal/signal.c create mode 100644 02_exercise/beispiele/pipe_bidirect/Makefile create mode 100644 02_exercise/beispiele/pipe_bidirect/log.txt create mode 100644 02_exercise/beispiele/pipe_bidirect/pingpong.c create mode 100644 02_exercise/beispiele/pipe_example/Makefile create mode 100644 02_exercise/beispiele/pipe_example/pipe.c create mode 100644 02_exercise/process.c create mode 100644 02_exercise/process.h create mode 100644 02_exercise/process_test.c create mode 100644 02_exercise/prog.c create mode 100644 02_exercise/prompt_utils.c create mode 100644 02_exercise/prompt_utils.h create mode 100644 02_exercise/prompt_utils_test.c create mode 100644 02_exercise/prompt_utils_test.h create mode 100644 02_exercise/shell.c delete mode 100644 shell/.gitignore delete mode 100644 shell/CMakeLists.txt delete mode 100644 shell/Makefile delete mode 100644 shell/array.c delete mode 100644 shell/array.h delete mode 100644 shell/beispiele/dup/Makefile delete mode 100644 shell/beispiele/dup/dup.c delete mode 100644 shell/beispiele/fork_example/Makefile delete mode 100644 shell/beispiele/fork_example/fork.c delete mode 100644 shell/beispiele/fork_signal/Makefile delete mode 100644 shell/beispiele/fork_signal/fork.c delete mode 100644 shell/beispiele/fork_signal/signal.c delete mode 100644 shell/beispiele/pipe_bidirect/Makefile delete mode 100644 shell/beispiele/pipe_bidirect/log.txt delete mode 100644 shell/beispiele/pipe_bidirect/pingpong.c delete mode 100644 shell/beispiele/pipe_example/Makefile delete mode 100644 shell/beispiele/pipe_example/pipe.c delete mode 100644 shell/process.c delete mode 100644 shell/process.h delete mode 100644 shell/process_test.c delete mode 100644 shell/prog.c delete mode 100644 shell/prompt_utils.c delete mode 100644 shell/prompt_utils.h delete mode 100644 shell/prompt_utils_test.c delete mode 100644 shell/prompt_utils_test.h delete mode 100644 shell/shell.c diff --git a/02_exercise/.gitignore b/02_exercise/.gitignore new file mode 100644 index 0000000..819a501 --- /dev/null +++ b/02_exercise/.gitignore @@ -0,0 +1,3 @@ +prog +shell +build \ No newline at end of file diff --git a/02_exercise/CMakeLists.txt b/02_exercise/CMakeLists.txt new file mode 100644 index 0000000..9cae089 --- /dev/null +++ b/02_exercise/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.5) + +project(shell C) + +set(CMAKE_C_COMPILER gcc) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED True) + +find_package(Sanitizers) + +add_executable(prog prog.c) +add_executable(shell shell.c) +target_link_libraries(shell PRIVATE array prompt_utils process) + +add_sanitizers(shell) +add_compile_definitions(_GNU_SOURCE) + +add_library(array array.c) + +add_library(prompt_utils prompt_utils.c) +target_link_libraries(prompt_utils PRIVATE array) + +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) + +target_compile_options(shell INTERFACE ${PROJECT_WARNINGS}) +target_compile_options(prompt_utils INTERFACE ${PROJECT_WARNINGS}) +target_compile_options(process INTERFACE ${PROJECT_WARNINGS}) diff --git a/02_exercise/Makefile b/02_exercise/Makefile new file mode 100644 index 0000000..5d1b360 --- /dev/null +++ b/02_exercise/Makefile @@ -0,0 +1,32 @@ +#!/usr/bin/make +.SUFFIXES: +.PHONY: all run pack clean +TAR = shell prog +SRC = $(wildcard *.c) +OBJ = $(SRC:%.c=%.o) +PCK = lab-2.zip + +CFLAGS = -std=gnu11 -c -g -Os -Wall -Werror -MMD -MP -D=_GNU_SOURCE + +DEP = $(OBJ:%.o=%.d) +-include $(DEP) + +%.o: %.c + $(CC) $(CFLAGS) $< -o $@ + +all: $(TAR) + +prog: prog.o + $(CC) -o $@ $^ + +shell: $(filter-out prog.o,$(OBJ)) + $(CC) -o $@ $^ + +run: all + ./shell + +pack: + zip $(PCK) $(SRC) $(wildcard *.h) Makefile + +clean: + $(RM) $(RMFILES) $(TAR) $(OBJ) $(DEP) $(PCK) diff --git a/02_exercise/array.c b/02_exercise/array.c new file mode 100644 index 0000000..4f08366 --- /dev/null +++ b/02_exercise/array.c @@ -0,0 +1,59 @@ +/***************************************************************************//** + * @file array.c + * @author Dorian Weber + * @brief Implementation des generalisierten Arrays. + ******************************************************************************/ + +#include "array.h" +#include +#include +#include + +/* ********************************************************* public functions */ + +/* (die runden Klammern um einige Funktionsnamen sind notwendig, da Makros + * gleichen Namens existieren und der Präprozessor diese expandieren würde) */ + +void* (arrayInit)(size_t capacity, size_t size) { + ArrayHdr *hdr = malloc(sizeof(*hdr) + size*capacity); + + if (hdr == NULL) + return NULL; + + hdr->len = 0; + hdr->cap = capacity; + + return hdr + 1; +} + +void arrayRelease(void* self) { + free(((ArrayHdr*) self) - 1); +} + +void* (arrayPush)(void* self, size_t size) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + + if (hdr->len == hdr->cap) { + hdr->cap *= 2; + hdr = realloc(hdr, sizeof(*hdr) + size*hdr->cap); + + if (hdr == NULL) { + fputs("program ran out of heap memory\n", stderr); + exit(-1); + } + } + + ++hdr->len; + return hdr + 1; +} + +void (arrayPop)(void* self) { + ArrayHdr *hdr = ((ArrayHdr*) self) - 1; + assert(hdr->len > 0); + --hdr->len; +} + +/* Symbol für die Inline-Funktionen erzeugen und aus diesem Modul exportieren */ +extern void arrayClear(void* self); +extern int arrayIsEmpty(const void* self); +extern size_t arrayLen(const void* self); diff --git a/02_exercise/array.h b/02_exercise/array.h new file mode 100644 index 0000000..78d21c9 --- /dev/null +++ b/02_exercise/array.h @@ -0,0 +1,142 @@ +/***************************************************************************//** + * @file array.h + * @author Dorian Weber + * @brief Generischer Array-Typ. + * + * @details + * Diese Datei enthält die Schnittstelle eines generischen Array-Typs. Um den + * Element-Typ des Arrays festzulegen, deklariert man eine Variable als Zeiger + * auf den Element-Typ. Diese kann danach initialisiert und so benutzt werden, + * als wäre sie ein Zeiger auf ein Array variabler Länge. + * + * Hier ist ein Nutzungsbeispiel: + * @code + * int *array; + * + * arrayInit(array); + * arrayPush(array) = 1; + * arrayPush(array) = 2; + * arrayPush(array) = 3; + * + * while (!arrayIsEmpty(array)) + * printf("%i\n", arrayPop(array)); + * + * arrayRelease(array); + * @endcode + * + * Viele der genutzten Funktionen sind in Form von Makros implementiert, die die + * Variable, die den Zeiger auf das Array hält, aktualisieren, obwohl es so + * aussieht, als würde sich dieser Wert niemals ändern. Das macht es riskant + * mehr als einen Zeiger auf das Array zu halten, da jede Vergrößerung des + * Arrays alle Zeiger auf und in das Array potentiell invalidiert. Obwohl diese + * Fehlerquelle subtil und äußerst schwer diagnostizierbar ist - es ist eine + * extrem einfache Abstraktion - führt diese Datenstruktur meiner Meinung nach + * trotzdem zu einem massiven Produktivitätsgewinn; man sollte sich nur in etwa + * im Klaren über die unterliegende Implementation sein, um genau die Untermenge + * von Programmiertechniken auszuwählen, die korrekt funktioniert. + ******************************************************************************/ + +#ifndef ARRAY_H_INCLUDED +#define ARRAY_H_INCLUDED + +/* *** includes ************************************************************* */ + +#include + +/* *** structures *********************************************************** */ + +/**@brief Arrayheader. + * + * Diese Struktur wird jedem Array im Speicher vorangestellt und beinhaltet + * Informationen über Kapazität und aktuelle Auslastung des Arrays. Die + * Arrayelemente schließen sich dieser Struktur unmittelbar an, so dass der + * Nutzer von dieser versteckten Information nichts bemerkt. + */ +typedef struct ArrayHdr +{ + size_t len; /**<@brief Anzahl der Array-Elemente. */ + size_t cap; /**<@brief Kapazität des reservierten Speichers. */ +} ArrayHdr; + +/* *** interface ************************************************************ */ + +/**@internal + * @brief Initialisiert und gibt einen Zeiger auf den Start des Arrays zurück. + * @param capacity initiale Kapazität + * @param size Größe der Arrayelemente + * @return ein Zeiger auf den Start des Arrays, falls erfolgreich,\n + * \c NULL im Falle eines Speicherfehlers + */ +extern void* arrayInit(size_t capacity, size_t size); + +/**@brief Initialisiert ein neues Array. + * @param self das Array + * @return 0, falls keine Fehler bei der Initialisierung aufgetreten sind,\n + * -1 ansonsten + */ +#define arrayInit(self) \ + ((self = arrayInit(8, sizeof((self)[0]))) == NULL ? -1 : 0) + +/**@brief Gibt das Array und alle assoziierten Strukturen frei. + * @param self das Array + */ +extern void arrayRelease(void* self); + +/**@internal + * @brief Reserviert Platz für einen neuen Wert im Array. + * @param self das Array + * @param size Größe der Arrayelemente + * @return der neue Zeiger auf den Start des Arrays + */ +extern void* arrayPush(void* self, size_t size); + +/**@brief Legt einen Wert auf das Array. + * @param self das Array + */ +#define arrayPush(self) \ + (self = arrayPush(self, sizeof((self)[0])), (self)+arrayLen(self)-1)[0] + +/**@brief Entfernt das oberste Element des Arrays. + * @param self das Array + */ +extern void arrayPop(void* self); + +/**@brief Entfernt und liefert das oberste Element des Arrays. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayPop(self) \ + (arrayPop(self), (self)+arrayLen(self))[0] + +/**@brief Gibt das oberste Element des Arrays zurück. + * @param self das Array + * @return das oberste Element von \p self + */ +#define arrayTop(self) \ + (self)[arrayLen(self) - 1] + +/**@brief Setzt die Länge des Arrays auf 0 zurück. +* @param self das Array +*/ +inline void arrayClear(void* self) { + ((ArrayHdr*) self)[-1].len = 0; +} + +/**@brief Gibt zurück, ob das Array leer ist. + * @param self das Array + * @return 0, falls nicht leer\n + 1, falls leer + */ +inline int arrayIsEmpty(const void* self) { + return ((ArrayHdr*) self)[-1].len == 0; +} + +/**@brief Gibt die Anzahl der Array-Elemente zurück. + * @param self das Array + * @return Anzahl der Elemente des Arrays + */ +inline size_t arrayLen(const void* self) { + return ((ArrayHdr*) self)[-1].len; +} + +#endif /* ARRAY_H_INCLUDED */ diff --git a/02_exercise/beispiele/dup/Makefile b/02_exercise/beispiele/dup/Makefile new file mode 100644 index 0000000..0f73ac4 --- /dev/null +++ b/02_exercise/beispiele/dup/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: dup + +run: all + ./dup + +clean: + $(RM) $(RMFILES) dup diff --git a/02_exercise/beispiele/dup/dup.c b/02_exercise/beispiele/dup/dup.c new file mode 100644 index 0000000..ea3c5a6 --- /dev/null +++ b/02_exercise/beispiele/dup/dup.c @@ -0,0 +1,51 @@ +#include +#include +#include + +/* https://www.youtube.com/watch?v=gaXigSu72A4 */ +static inline void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) { + int pdes[2]; + pid_t child; + + if (pipe(pdes) < 0) + die("pipe()"); + + if ((child = fork()) < 0) + die("fork()"); + + if (child == 0) { + /* child process */ + close(pdes[0]); + close(1); /* close stdout */ + + if (dup(pdes[1]) < 0) + die("dup()"); + + /* now stdout and pdes[1] are equivalent (dup returns lowest free descriptor) */ + if (execlp("cat", "cat", "/etc/passwd", NULL) < 0) + die("execlp()"); + + exit(EXIT_SUCCESS); + } + else { + /* parent process */ + close(0); /* close stdin */ + close(pdes[1]); + + if (dup(pdes[0]) < 0) + die("dup()"); + + /* now stdin and pdes[0] are equivalent (dup returns lowest free descriptor) */ + if (execlp("wc", "wc", "-l", NULL) < 0) + die("execlp()"); + + exit(EXIT_SUCCESS); + } + + return 0; +} diff --git a/02_exercise/beispiele/fork_example/Makefile b/02_exercise/beispiele/fork_example/Makefile new file mode 100644 index 0000000..8f69ed9 --- /dev/null +++ b/02_exercise/beispiele/fork_example/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: fork + +run: all + ./fork + +clean: + $(RM) $(RMFILES) fork diff --git a/02_exercise/beispiele/fork_example/fork.c b/02_exercise/beispiele/fork_example/fork.c new file mode 100644 index 0000000..7377b0f --- /dev/null +++ b/02_exercise/beispiele/fork_example/fork.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +int main(void) { + pid_t proc_id; + int status = 0; + + proc_id = fork(); + + if (proc_id < 0) { + fprintf(stderr, "fork error\n"); + fflush(stderr); + return EXIT_FAILURE; + } + + if (proc_id == 0) { + /* child process */ + printf("[child] process id: %d\n", (int) getpid()); + + char* args[] = {"sleep", "1", NULL}; + execvp(args[0], args); + exit(-1); + } + else { + /* parent */ + printf("[parent] process id: %d\n", (int) getpid()); + pid_t child_id = wait(&status); + + printf("[parent] child %d returned: %d\n", + child_id, WEXITSTATUS(status)); + } + + return EXIT_SUCCESS; +} diff --git a/02_exercise/beispiele/fork_signal/Makefile b/02_exercise/beispiele/fork_signal/Makefile new file mode 100644 index 0000000..499b8cb --- /dev/null +++ b/02_exercise/beispiele/fork_signal/Makefile @@ -0,0 +1,19 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -O0 + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: fork signal + +run: all + ./fork 10 + ./signal + +clean: + $(RM) $(RMFILES) fork signal diff --git a/02_exercise/beispiele/fork_signal/fork.c b/02_exercise/beispiele/fork_signal/fork.c new file mode 100644 index 0000000..b144577 --- /dev/null +++ b/02_exercise/beispiele/fork_signal/fork.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +int main(int argc, const char* argv[]) { + pid_t pid; + int h; + int count; + + if (argc < 2) { + printf("Usage: ./fork process#\n"); + return 0; + } + + count = atoi(argv[1]) - 1; + printf("Process#: %d\n", count + 1); + printf("Parent started\n"); + + pid = 1; + for (h = 0; h < count; ++h) { + if ((pid = fork()) < 0) + printf("fork error\n"); + else if (pid == 0) { + printf("Child %d started\n", h); + sleep(1); + printf("Child %d finished\n", h); + exit(0); + } + } + + printf("Parent waiting\n"); + for (h = 0; h < count; ++h) { + wait(NULL); + } + printf("Parent finished\n"); + return EXIT_SUCCESS; +} diff --git a/02_exercise/beispiele/fork_signal/signal.c b/02_exercise/beispiele/fork_signal/signal.c new file mode 100644 index 0000000..500d2e1 --- /dev/null +++ b/02_exercise/beispiele/fork_signal/signal.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +void sig_handler(int signo) { + const char msg[] = "Speicherzugriffsfehler!\n"; + write(2, msg, sizeof(msg) - 1); + exit(-1); +} + +int main(void) { + if (signal(SIGSEGV, sig_handler) == SIG_ERR) { + perror("signal"); + exit(-1); + } + + int array[1] = {0}; + array[1000000] = 3; + return 0; +} diff --git a/02_exercise/beispiele/pipe_bidirect/Makefile b/02_exercise/beispiele/pipe_bidirect/Makefile new file mode 100644 index 0000000..361ac97 --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/Makefile @@ -0,0 +1,19 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: pingpong + +run: all + @echo "writing into log.txt; stop execution with CTRL+C" + ./pingpong + +clean: + $(RM) $(RMFILES) pingpong diff --git a/02_exercise/beispiele/pipe_bidirect/log.txt b/02_exercise/beispiele/pipe_bidirect/log.txt new file mode 100644 index 0000000..c59b0bb --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/log.txt @@ -0,0 +1,19 @@ +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +[3225581]: ping +[3225580]: pong +Programmabbruch durch Nutzer diff --git a/02_exercise/beispiele/pipe_bidirect/pingpong.c b/02_exercise/beispiele/pipe_bidirect/pingpong.c new file mode 100644 index 0000000..f38394f --- /dev/null +++ b/02_exercise/beispiele/pipe_bidirect/pingpong.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include + +static int split(int fd[2]); +static int redir(int fd, const char *file); +static void sig_handler(int sig); + +static int parent; + +int main(void) { + ssize_t bytes; + char buf[256]; + int fd[2]; + + // disable buffering for stdout + setvbuf(stdout, NULL, _IONBF, 0); + + // redirect stdout to write into a file + if (redir(1, "log.txt") < 0) { + perror("redir()"); + exit(-1); + } + + // install the signal handler for Ctrl+C + if (signal(SIGINT, sig_handler) == SIG_ERR) { + perror("signal()"); + exit(-1); + } + + // split processes and assign file descriptors for communication + switch (split(fd)) { + case -1: // error + exit(-1); + + case 0: // child + while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { + printf("[%u]: %s\n", getpid(), buf); + sleep(1); + write(fd[1], "pong", 5); + } + break; + + case 1: // parent + parent = 1; + write(fd[1], "ping", 5); + while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { + printf("[%u]: %s\n", getpid(), buf); + sleep(1); + write(fd[1], "ping", 5); + } + break; + } + + // close the file descriptors + close(fd[0]); + close(fd[1]); + + return 0; +} + +int split(int fd[2]) { + int pfd[2][2]; + + if (pipe(pfd[0]) < 0) { + perror("pipe()"); + goto err_pipe1; + } + + if (pipe(pfd[1]) < 0) { + perror("pipe()"); + goto err_pipe2; + } + + switch (fork()) { + case -1: + perror("fork()"); + goto err_fork; + + case 0: // child process + close(pfd[0][1]); // close write for child + close(pfd[1][0]); // close read for parent + fd[0] = pfd[0][0]; // keep read for child + fd[1] = pfd[1][1]; // keep write for parent + return 0; + + default: // parent process + close(pfd[1][1]); // close write for parent + close(pfd[0][0]); // close read for child + fd[0] = pfd[1][0]; // keep read for parent + fd[1] = pfd[0][1]; // keep write for child + return 1; + } + +err_fork: + close(pfd[1][0]); + close(pfd[1][1]); +err_pipe2: + close(pfd[0][0]); + close(pfd[0][1]); +err_pipe1: + return -1; +} + +int redir(int fd, const char *file) { + int fd2 = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + + if (fd2 < 0) + return -1; + + dup2(fd2, fd); + close(fd2); + + return 0; +} + +void sig_handler(int sig) { + if (parent) { + const char msg[] = "Programmabbruch durch Nutzer\n"; + write(1, msg, sizeof(msg) - 1); + } + exit(0); +} diff --git a/02_exercise/beispiele/pipe_example/Makefile b/02_exercise/beispiele/pipe_example/Makefile new file mode 100644 index 0000000..d69473e --- /dev/null +++ b/02_exercise/beispiele/pipe_example/Makefile @@ -0,0 +1,18 @@ +#!/usr/bin/make +.SUFFIXES: + +CFLAGS = -c -Os -Wall -Werror + +%.o: %.c + $(CC) $(CFLAGS) $^ -o $@ + +%: %.o + $(CC) -o $@ $^ + +all: pipe + +run: all + ./pipe + +clean: + $(RM) $(RMFILES) pipe diff --git a/02_exercise/beispiele/pipe_example/pipe.c b/02_exercise/beispiele/pipe_example/pipe.c new file mode 100644 index 0000000..422e4d5 --- /dev/null +++ b/02_exercise/beispiele/pipe_example/pipe.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#define BUF_SIZE 32 + +int pipe_fds[2]; + +void term(const char *msg) { + if (msg) { + fprintf(stderr, "Error: %s\n", msg); + fflush(stderr); + } + + int close_fail = 0; + int ret = close(pipe_fds[0]); + + if (ret < 0) { + fprintf(stderr, "Error: Couldn't close reading end of pipe\n"); + fflush(stderr); + close_fail = 1; + } + + ret = close(pipe_fds[1]); + + if (ret < 0) { + fprintf(stderr, "Error: Couldn't close writing end of pipe\n"); + fflush(stderr); + close_fail = 1; + } + + exit((msg || close_fail) ? EXIT_FAILURE : EXIT_SUCCESS); +} + +int main(void) { + int ret = pipe(pipe_fds); + + if (ret < 0) + term("Couldn't create pipe"); + + char out_buf[] = "hello"; + ret = write(pipe_fds[1], out_buf, strlen(out_buf) + 1); + + if (ret < 0) + term("Couldn't write to pipe"); + + printf("send msg: %s\n", out_buf); + + char in_buf[BUF_SIZE]; + memset(in_buf, 0, BUF_SIZE); + + ret = read(pipe_fds[0], in_buf, BUF_SIZE - 1); + + if (ret < 0) + term("Couldn't read from pipe"); + + printf("recv msg: %s\n", in_buf); + return 0; +} 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 +#include +#include +#include +#include + +#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 +#include + +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..c14aa59 --- /dev/null +++ b/02_exercise/process_test.c @@ -0,0 +1,63 @@ +#include +#include + +#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 new file mode 100644 index 0000000..efd2527 --- /dev/null +++ b/02_exercise/prog.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +static unsigned int hash(const void* key, size_t size) { + const char* ptr = key; + unsigned int hval; + + for (hval = 0x811c9dc5u; size --> 0; ++ptr) { + hval ^= *ptr; + hval *= 0x1000193u; + } + + return hval; +} + +static inline int roll(int sides) { + return rand() / (RAND_MAX + 1.0) * sides; +} + +int main(int argc, const char* argv[]) { + int rc, delay; + time_t now = time(0); + + /* initialize the random number generator */ + srand(hash(&now, sizeof(now))); + + /* determine exit code and time delay */ + switch (argc) { + default: + delay = atoi(argv[1]); + rc = atoi(argv[2]); + break; + + case 2: + delay = atoi(argv[1]); + rc = roll(2) - 1; + break; + + case 1: + delay = roll(10); + rc = roll(2) - 1; + break; + } + + printf("Delay: %ds, Exit Code: %d\n", delay, rc); + fflush(stdout); + sleep(delay); + + return rc; +} diff --git a/02_exercise/prompt_utils.c b/02_exercise/prompt_utils.c new file mode 100644 index 0000000..168c088 --- /dev/null +++ b/02_exercise/prompt_utils.c @@ -0,0 +1,90 @@ +#include +#include + +#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) { + 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_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); + + // 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) + 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_separator_indices(char const *const text, char seperator) { + size_t *indices; + arrayInit(indices); + char const *current = text; + if (strchr(current, seperator) == NULL) { + arrayRelease(indices); + return NULL; + } + while ((current = strchr(current, seperator)) != NULL) { + arrayPush(indices) = (size_t) (current - text); + ++current; + } + arrayPush(indices) = strlen(text); + return indices; +} diff --git a/02_exercise/prompt_utils.h b/02_exercise/prompt_utils.h new file mode 100644 index 0000000..36d065d --- /dev/null +++ b/02_exercise/prompt_utils.h @@ -0,0 +1,10 @@ +#ifndef PROMPT_UTILS_H_INCLUDED +#define PROMPT_UTILS_H_INCLUDED + +// 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 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 new file mode 100644 index 0000000..1be6162 --- /dev/null +++ b/02_exercise/prompt_utils_test.c @@ -0,0 +1,45 @@ +#include "array.h" +#include "prompt_utils.h" +#include +#include +#include + +void test_get_separator_indices() { + char const *const test_str = "/This/is/a/test"; + 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_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_separator_indices(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", "/", "../.."); +} + + +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 new file mode 100644 index 0000000..59717d0 --- /dev/null +++ b/02_exercise/prompt_utils_test.h @@ -0,0 +1,7 @@ +#ifndef PROMPT_UTILS_TEST_H_INCLUDED +#define PROMPT_UTILS_TEST_H_INCLUDED + +void test_get_separator_indices(); +void test_relative_path(); + +#endif diff --git a/02_exercise/shell.c b/02_exercise/shell.c new file mode 100644 index 0000000..dd0085d --- /dev/null +++ b/02_exercise/shell.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include "array.h" +#include "process.h" +#include "prompt_utils.h" + + +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; + __ssize_t length = 0; + + printf("%s > ", prompt); + if ((length = getline(&line, &cap, stdin)) < 0) { + fprintf(stderr, "Failed to read from STDIN"); + exit(-1); + } + + if (strspn(line, " \n\t") == strlen(line)) { + continue; + } + line[length - 1] = '\0'; // cut the line feed + + Process * processes = NULL; + parse_line(line, &processes); + + if (strcmp(processes[0].argv[0], "cd") == 0) { + + if(arrayLen(processes) != 1) { + perror("Can't chain cd with other processes"); + } + if (arrayLen(processes[0].argv) != 3) { + fprintf(stderr, "usage: cd "); + goto clean; + } + + int ret = chdir(processes[0].argv[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(processes[0].argv[0], "exit") == 0) { + done = true; + } else { + 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); + line = NULL; + cap = 0; + free_processes(&processes); + } + + free((void *)original_wd); + free((void *)prompt); +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4db1f5d..41de2c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) -add_subdirectory(shell) +add_subdirectory(02_exercise) set(CLANG_WARNINGS -Wall diff --git a/shell/.gitignore b/shell/.gitignore deleted file mode 100644 index 819a501..0000000 --- a/shell/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -prog -shell -build \ No newline at end of file diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt deleted file mode 100644 index 9cae089..0000000 --- a/shell/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(shell C) - -set(CMAKE_C_COMPILER gcc) -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED True) - -find_package(Sanitizers) - -add_executable(prog prog.c) -add_executable(shell shell.c) -target_link_libraries(shell PRIVATE array prompt_utils process) - -add_sanitizers(shell) -add_compile_definitions(_GNU_SOURCE) - -add_library(array array.c) - -add_library(prompt_utils prompt_utils.c) -target_link_libraries(prompt_utils PRIVATE array) - -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) - -target_compile_options(shell INTERFACE ${PROJECT_WARNINGS}) -target_compile_options(prompt_utils INTERFACE ${PROJECT_WARNINGS}) -target_compile_options(process INTERFACE ${PROJECT_WARNINGS}) diff --git a/shell/Makefile b/shell/Makefile deleted file mode 100644 index 5d1b360..0000000 --- a/shell/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: -.PHONY: all run pack clean -TAR = shell prog -SRC = $(wildcard *.c) -OBJ = $(SRC:%.c=%.o) -PCK = lab-2.zip - -CFLAGS = -std=gnu11 -c -g -Os -Wall -Werror -MMD -MP -D=_GNU_SOURCE - -DEP = $(OBJ:%.o=%.d) --include $(DEP) - -%.o: %.c - $(CC) $(CFLAGS) $< -o $@ - -all: $(TAR) - -prog: prog.o - $(CC) -o $@ $^ - -shell: $(filter-out prog.o,$(OBJ)) - $(CC) -o $@ $^ - -run: all - ./shell - -pack: - zip $(PCK) $(SRC) $(wildcard *.h) Makefile - -clean: - $(RM) $(RMFILES) $(TAR) $(OBJ) $(DEP) $(PCK) diff --git a/shell/array.c b/shell/array.c deleted file mode 100644 index 4f08366..0000000 --- a/shell/array.c +++ /dev/null @@ -1,59 +0,0 @@ -/***************************************************************************//** - * @file array.c - * @author Dorian Weber - * @brief Implementation des generalisierten Arrays. - ******************************************************************************/ - -#include "array.h" -#include -#include -#include - -/* ********************************************************* public functions */ - -/* (die runden Klammern um einige Funktionsnamen sind notwendig, da Makros - * gleichen Namens existieren und der Präprozessor diese expandieren würde) */ - -void* (arrayInit)(size_t capacity, size_t size) { - ArrayHdr *hdr = malloc(sizeof(*hdr) + size*capacity); - - if (hdr == NULL) - return NULL; - - hdr->len = 0; - hdr->cap = capacity; - - return hdr + 1; -} - -void arrayRelease(void* self) { - free(((ArrayHdr*) self) - 1); -} - -void* (arrayPush)(void* self, size_t size) { - ArrayHdr *hdr = ((ArrayHdr*) self) - 1; - - if (hdr->len == hdr->cap) { - hdr->cap *= 2; - hdr = realloc(hdr, sizeof(*hdr) + size*hdr->cap); - - if (hdr == NULL) { - fputs("program ran out of heap memory\n", stderr); - exit(-1); - } - } - - ++hdr->len; - return hdr + 1; -} - -void (arrayPop)(void* self) { - ArrayHdr *hdr = ((ArrayHdr*) self) - 1; - assert(hdr->len > 0); - --hdr->len; -} - -/* Symbol für die Inline-Funktionen erzeugen und aus diesem Modul exportieren */ -extern void arrayClear(void* self); -extern int arrayIsEmpty(const void* self); -extern size_t arrayLen(const void* self); diff --git a/shell/array.h b/shell/array.h deleted file mode 100644 index 78d21c9..0000000 --- a/shell/array.h +++ /dev/null @@ -1,142 +0,0 @@ -/***************************************************************************//** - * @file array.h - * @author Dorian Weber - * @brief Generischer Array-Typ. - * - * @details - * Diese Datei enthält die Schnittstelle eines generischen Array-Typs. Um den - * Element-Typ des Arrays festzulegen, deklariert man eine Variable als Zeiger - * auf den Element-Typ. Diese kann danach initialisiert und so benutzt werden, - * als wäre sie ein Zeiger auf ein Array variabler Länge. - * - * Hier ist ein Nutzungsbeispiel: - * @code - * int *array; - * - * arrayInit(array); - * arrayPush(array) = 1; - * arrayPush(array) = 2; - * arrayPush(array) = 3; - * - * while (!arrayIsEmpty(array)) - * printf("%i\n", arrayPop(array)); - * - * arrayRelease(array); - * @endcode - * - * Viele der genutzten Funktionen sind in Form von Makros implementiert, die die - * Variable, die den Zeiger auf das Array hält, aktualisieren, obwohl es so - * aussieht, als würde sich dieser Wert niemals ändern. Das macht es riskant - * mehr als einen Zeiger auf das Array zu halten, da jede Vergrößerung des - * Arrays alle Zeiger auf und in das Array potentiell invalidiert. Obwohl diese - * Fehlerquelle subtil und äußerst schwer diagnostizierbar ist - es ist eine - * extrem einfache Abstraktion - führt diese Datenstruktur meiner Meinung nach - * trotzdem zu einem massiven Produktivitätsgewinn; man sollte sich nur in etwa - * im Klaren über die unterliegende Implementation sein, um genau die Untermenge - * von Programmiertechniken auszuwählen, die korrekt funktioniert. - ******************************************************************************/ - -#ifndef ARRAY_H_INCLUDED -#define ARRAY_H_INCLUDED - -/* *** includes ************************************************************* */ - -#include - -/* *** structures *********************************************************** */ - -/**@brief Arrayheader. - * - * Diese Struktur wird jedem Array im Speicher vorangestellt und beinhaltet - * Informationen über Kapazität und aktuelle Auslastung des Arrays. Die - * Arrayelemente schließen sich dieser Struktur unmittelbar an, so dass der - * Nutzer von dieser versteckten Information nichts bemerkt. - */ -typedef struct ArrayHdr -{ - size_t len; /**<@brief Anzahl der Array-Elemente. */ - size_t cap; /**<@brief Kapazität des reservierten Speichers. */ -} ArrayHdr; - -/* *** interface ************************************************************ */ - -/**@internal - * @brief Initialisiert und gibt einen Zeiger auf den Start des Arrays zurück. - * @param capacity initiale Kapazität - * @param size Größe der Arrayelemente - * @return ein Zeiger auf den Start des Arrays, falls erfolgreich,\n - * \c NULL im Falle eines Speicherfehlers - */ -extern void* arrayInit(size_t capacity, size_t size); - -/**@brief Initialisiert ein neues Array. - * @param self das Array - * @return 0, falls keine Fehler bei der Initialisierung aufgetreten sind,\n - * -1 ansonsten - */ -#define arrayInit(self) \ - ((self = arrayInit(8, sizeof((self)[0]))) == NULL ? -1 : 0) - -/**@brief Gibt das Array und alle assoziierten Strukturen frei. - * @param self das Array - */ -extern void arrayRelease(void* self); - -/**@internal - * @brief Reserviert Platz für einen neuen Wert im Array. - * @param self das Array - * @param size Größe der Arrayelemente - * @return der neue Zeiger auf den Start des Arrays - */ -extern void* arrayPush(void* self, size_t size); - -/**@brief Legt einen Wert auf das Array. - * @param self das Array - */ -#define arrayPush(self) \ - (self = arrayPush(self, sizeof((self)[0])), (self)+arrayLen(self)-1)[0] - -/**@brief Entfernt das oberste Element des Arrays. - * @param self das Array - */ -extern void arrayPop(void* self); - -/**@brief Entfernt und liefert das oberste Element des Arrays. - * @param self das Array - * @return das oberste Element von \p self - */ -#define arrayPop(self) \ - (arrayPop(self), (self)+arrayLen(self))[0] - -/**@brief Gibt das oberste Element des Arrays zurück. - * @param self das Array - * @return das oberste Element von \p self - */ -#define arrayTop(self) \ - (self)[arrayLen(self) - 1] - -/**@brief Setzt die Länge des Arrays auf 0 zurück. -* @param self das Array -*/ -inline void arrayClear(void* self) { - ((ArrayHdr*) self)[-1].len = 0; -} - -/**@brief Gibt zurück, ob das Array leer ist. - * @param self das Array - * @return 0, falls nicht leer\n - 1, falls leer - */ -inline int arrayIsEmpty(const void* self) { - return ((ArrayHdr*) self)[-1].len == 0; -} - -/**@brief Gibt die Anzahl der Array-Elemente zurück. - * @param self das Array - * @return Anzahl der Elemente des Arrays - */ -inline size_t arrayLen(const void* self) { - return ((ArrayHdr*) self)[-1].len; -} - -#endif /* ARRAY_H_INCLUDED */ diff --git a/shell/beispiele/dup/Makefile b/shell/beispiele/dup/Makefile deleted file mode 100644 index 0f73ac4..0000000 --- a/shell/beispiele/dup/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: - -CFLAGS = -c -Os -Wall -Werror - -%.o: %.c - $(CC) $(CFLAGS) $^ -o $@ - -%: %.o - $(CC) -o $@ $^ - -all: dup - -run: all - ./dup - -clean: - $(RM) $(RMFILES) dup diff --git a/shell/beispiele/dup/dup.c b/shell/beispiele/dup/dup.c deleted file mode 100644 index ea3c5a6..0000000 --- a/shell/beispiele/dup/dup.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -/* https://www.youtube.com/watch?v=gaXigSu72A4 */ -static inline void die(const char* msg) { - perror(msg); - exit(EXIT_FAILURE); -} - -int main(int argc, char **argv) { - int pdes[2]; - pid_t child; - - if (pipe(pdes) < 0) - die("pipe()"); - - if ((child = fork()) < 0) - die("fork()"); - - if (child == 0) { - /* child process */ - close(pdes[0]); - close(1); /* close stdout */ - - if (dup(pdes[1]) < 0) - die("dup()"); - - /* now stdout and pdes[1] are equivalent (dup returns lowest free descriptor) */ - if (execlp("cat", "cat", "/etc/passwd", NULL) < 0) - die("execlp()"); - - exit(EXIT_SUCCESS); - } - else { - /* parent process */ - close(0); /* close stdin */ - close(pdes[1]); - - if (dup(pdes[0]) < 0) - die("dup()"); - - /* now stdin and pdes[0] are equivalent (dup returns lowest free descriptor) */ - if (execlp("wc", "wc", "-l", NULL) < 0) - die("execlp()"); - - exit(EXIT_SUCCESS); - } - - return 0; -} diff --git a/shell/beispiele/fork_example/Makefile b/shell/beispiele/fork_example/Makefile deleted file mode 100644 index 8f69ed9..0000000 --- a/shell/beispiele/fork_example/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: - -CFLAGS = -c -Os -Wall -Werror - -%.o: %.c - $(CC) $(CFLAGS) $^ -o $@ - -%: %.o - $(CC) -o $@ $^ - -all: fork - -run: all - ./fork - -clean: - $(RM) $(RMFILES) fork diff --git a/shell/beispiele/fork_example/fork.c b/shell/beispiele/fork_example/fork.c deleted file mode 100644 index 7377b0f..0000000 --- a/shell/beispiele/fork_example/fork.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include - -int main(void) { - pid_t proc_id; - int status = 0; - - proc_id = fork(); - - if (proc_id < 0) { - fprintf(stderr, "fork error\n"); - fflush(stderr); - return EXIT_FAILURE; - } - - if (proc_id == 0) { - /* child process */ - printf("[child] process id: %d\n", (int) getpid()); - - char* args[] = {"sleep", "1", NULL}; - execvp(args[0], args); - exit(-1); - } - else { - /* parent */ - printf("[parent] process id: %d\n", (int) getpid()); - pid_t child_id = wait(&status); - - printf("[parent] child %d returned: %d\n", - child_id, WEXITSTATUS(status)); - } - - return EXIT_SUCCESS; -} diff --git a/shell/beispiele/fork_signal/Makefile b/shell/beispiele/fork_signal/Makefile deleted file mode 100644 index 499b8cb..0000000 --- a/shell/beispiele/fork_signal/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: - -CFLAGS = -c -O0 - -%.o: %.c - $(CC) $(CFLAGS) $^ -o $@ - -%: %.o - $(CC) -o $@ $^ - -all: fork signal - -run: all - ./fork 10 - ./signal - -clean: - $(RM) $(RMFILES) fork signal diff --git a/shell/beispiele/fork_signal/fork.c b/shell/beispiele/fork_signal/fork.c deleted file mode 100644 index b144577..0000000 --- a/shell/beispiele/fork_signal/fork.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include -#include - -int main(int argc, const char* argv[]) { - pid_t pid; - int h; - int count; - - if (argc < 2) { - printf("Usage: ./fork process#\n"); - return 0; - } - - count = atoi(argv[1]) - 1; - printf("Process#: %d\n", count + 1); - printf("Parent started\n"); - - pid = 1; - for (h = 0; h < count; ++h) { - if ((pid = fork()) < 0) - printf("fork error\n"); - else if (pid == 0) { - printf("Child %d started\n", h); - sleep(1); - printf("Child %d finished\n", h); - exit(0); - } - } - - printf("Parent waiting\n"); - for (h = 0; h < count; ++h) { - wait(NULL); - } - printf("Parent finished\n"); - return EXIT_SUCCESS; -} diff --git a/shell/beispiele/fork_signal/signal.c b/shell/beispiele/fork_signal/signal.c deleted file mode 100644 index 500d2e1..0000000 --- a/shell/beispiele/fork_signal/signal.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include - -void sig_handler(int signo) { - const char msg[] = "Speicherzugriffsfehler!\n"; - write(2, msg, sizeof(msg) - 1); - exit(-1); -} - -int main(void) { - if (signal(SIGSEGV, sig_handler) == SIG_ERR) { - perror("signal"); - exit(-1); - } - - int array[1] = {0}; - array[1000000] = 3; - return 0; -} diff --git a/shell/beispiele/pipe_bidirect/Makefile b/shell/beispiele/pipe_bidirect/Makefile deleted file mode 100644 index 361ac97..0000000 --- a/shell/beispiele/pipe_bidirect/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: - -CFLAGS = -c -Os -Wall -Werror - -%.o: %.c - $(CC) $(CFLAGS) $^ -o $@ - -%: %.o - $(CC) -o $@ $^ - -all: pingpong - -run: all - @echo "writing into log.txt; stop execution with CTRL+C" - ./pingpong - -clean: - $(RM) $(RMFILES) pingpong diff --git a/shell/beispiele/pipe_bidirect/log.txt b/shell/beispiele/pipe_bidirect/log.txt deleted file mode 100644 index c59b0bb..0000000 --- a/shell/beispiele/pipe_bidirect/log.txt +++ /dev/null @@ -1,19 +0,0 @@ -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -[3225581]: ping -[3225580]: pong -Programmabbruch durch Nutzer diff --git a/shell/beispiele/pipe_bidirect/pingpong.c b/shell/beispiele/pipe_bidirect/pingpong.c deleted file mode 100644 index f38394f..0000000 --- a/shell/beispiele/pipe_bidirect/pingpong.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include -#include - -static int split(int fd[2]); -static int redir(int fd, const char *file); -static void sig_handler(int sig); - -static int parent; - -int main(void) { - ssize_t bytes; - char buf[256]; - int fd[2]; - - // disable buffering for stdout - setvbuf(stdout, NULL, _IONBF, 0); - - // redirect stdout to write into a file - if (redir(1, "log.txt") < 0) { - perror("redir()"); - exit(-1); - } - - // install the signal handler for Ctrl+C - if (signal(SIGINT, sig_handler) == SIG_ERR) { - perror("signal()"); - exit(-1); - } - - // split processes and assign file descriptors for communication - switch (split(fd)) { - case -1: // error - exit(-1); - - case 0: // child - while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { - printf("[%u]: %s\n", getpid(), buf); - sleep(1); - write(fd[1], "pong", 5); - } - break; - - case 1: // parent - parent = 1; - write(fd[1], "ping", 5); - while ((bytes = read(fd[0], buf, sizeof(buf))) > 0) { - printf("[%u]: %s\n", getpid(), buf); - sleep(1); - write(fd[1], "ping", 5); - } - break; - } - - // close the file descriptors - close(fd[0]); - close(fd[1]); - - return 0; -} - -int split(int fd[2]) { - int pfd[2][2]; - - if (pipe(pfd[0]) < 0) { - perror("pipe()"); - goto err_pipe1; - } - - if (pipe(pfd[1]) < 0) { - perror("pipe()"); - goto err_pipe2; - } - - switch (fork()) { - case -1: - perror("fork()"); - goto err_fork; - - case 0: // child process - close(pfd[0][1]); // close write for child - close(pfd[1][0]); // close read for parent - fd[0] = pfd[0][0]; // keep read for child - fd[1] = pfd[1][1]; // keep write for parent - return 0; - - default: // parent process - close(pfd[1][1]); // close write for parent - close(pfd[0][0]); // close read for child - fd[0] = pfd[1][0]; // keep read for parent - fd[1] = pfd[0][1]; // keep write for child - return 1; - } - -err_fork: - close(pfd[1][0]); - close(pfd[1][1]); -err_pipe2: - close(pfd[0][0]); - close(pfd[0][1]); -err_pipe1: - return -1; -} - -int redir(int fd, const char *file) { - int fd2 = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); - - if (fd2 < 0) - return -1; - - dup2(fd2, fd); - close(fd2); - - return 0; -} - -void sig_handler(int sig) { - if (parent) { - const char msg[] = "Programmabbruch durch Nutzer\n"; - write(1, msg, sizeof(msg) - 1); - } - exit(0); -} diff --git a/shell/beispiele/pipe_example/Makefile b/shell/beispiele/pipe_example/Makefile deleted file mode 100644 index d69473e..0000000 --- a/shell/beispiele/pipe_example/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/make -.SUFFIXES: - -CFLAGS = -c -Os -Wall -Werror - -%.o: %.c - $(CC) $(CFLAGS) $^ -o $@ - -%: %.o - $(CC) -o $@ $^ - -all: pipe - -run: all - ./pipe - -clean: - $(RM) $(RMFILES) pipe diff --git a/shell/beispiele/pipe_example/pipe.c b/shell/beispiele/pipe_example/pipe.c deleted file mode 100644 index 422e4d5..0000000 --- a/shell/beispiele/pipe_example/pipe.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include - -#define BUF_SIZE 32 - -int pipe_fds[2]; - -void term(const char *msg) { - if (msg) { - fprintf(stderr, "Error: %s\n", msg); - fflush(stderr); - } - - int close_fail = 0; - int ret = close(pipe_fds[0]); - - if (ret < 0) { - fprintf(stderr, "Error: Couldn't close reading end of pipe\n"); - fflush(stderr); - close_fail = 1; - } - - ret = close(pipe_fds[1]); - - if (ret < 0) { - fprintf(stderr, "Error: Couldn't close writing end of pipe\n"); - fflush(stderr); - close_fail = 1; - } - - exit((msg || close_fail) ? EXIT_FAILURE : EXIT_SUCCESS); -} - -int main(void) { - int ret = pipe(pipe_fds); - - if (ret < 0) - term("Couldn't create pipe"); - - char out_buf[] = "hello"; - ret = write(pipe_fds[1], out_buf, strlen(out_buf) + 1); - - if (ret < 0) - term("Couldn't write to pipe"); - - printf("send msg: %s\n", out_buf); - - char in_buf[BUF_SIZE]; - memset(in_buf, 0, BUF_SIZE); - - ret = read(pipe_fds[0], in_buf, BUF_SIZE - 1); - - if (ret < 0) - term("Couldn't read from pipe"); - - printf("recv msg: %s\n", in_buf); - return 0; -} diff --git a/shell/process.c b/shell/process.c deleted file mode 100644 index 093b712..0000000 --- a/shell/process.c +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include -#include -#include -#include - -#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/shell/process.h b/shell/process.h deleted file mode 100644 index 3b7e833..0000000 --- a/shell/process.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SHELL_PROCESS_H -#define SHELL_PROCESS_H -#include -#include - -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/shell/process_test.c b/shell/process_test.c deleted file mode 100644 index c14aa59..0000000 --- a/shell/process_test.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -#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/shell/prog.c b/shell/prog.c deleted file mode 100644 index efd2527..0000000 --- a/shell/prog.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -static unsigned int hash(const void* key, size_t size) { - const char* ptr = key; - unsigned int hval; - - for (hval = 0x811c9dc5u; size --> 0; ++ptr) { - hval ^= *ptr; - hval *= 0x1000193u; - } - - return hval; -} - -static inline int roll(int sides) { - return rand() / (RAND_MAX + 1.0) * sides; -} - -int main(int argc, const char* argv[]) { - int rc, delay; - time_t now = time(0); - - /* initialize the random number generator */ - srand(hash(&now, sizeof(now))); - - /* determine exit code and time delay */ - switch (argc) { - default: - delay = atoi(argv[1]); - rc = atoi(argv[2]); - break; - - case 2: - delay = atoi(argv[1]); - rc = roll(2) - 1; - break; - - case 1: - delay = roll(10); - rc = roll(2) - 1; - break; - } - - printf("Delay: %ds, Exit Code: %d\n", delay, rc); - fflush(stdout); - sleep(delay); - - return rc; -} diff --git a/shell/prompt_utils.c b/shell/prompt_utils.c deleted file mode 100644 index 168c088..0000000 --- a/shell/prompt_utils.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include - -#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) { - 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_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); - - // 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) + 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_separator_indices(char const *const text, char seperator) { - size_t *indices; - arrayInit(indices); - char const *current = text; - if (strchr(current, seperator) == NULL) { - arrayRelease(indices); - return NULL; - } - while ((current = strchr(current, seperator)) != NULL) { - arrayPush(indices) = (size_t) (current - text); - ++current; - } - arrayPush(indices) = strlen(text); - return indices; -} diff --git a/shell/prompt_utils.h b/shell/prompt_utils.h deleted file mode 100644 index 36d065d..0000000 --- a/shell/prompt_utils.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef PROMPT_UTILS_H_INCLUDED -#define PROMPT_UTILS_H_INCLUDED - -// 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 occurrence of the separator -size_t *get_separator_indices(char const *text, char separator); - -#endif // PROMPT_UTILS_H_INCLUDED diff --git a/shell/prompt_utils_test.c b/shell/prompt_utils_test.c deleted file mode 100644 index 1be6162..0000000 --- a/shell/prompt_utils_test.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "array.h" -#include "prompt_utils.h" -#include -#include -#include - -void test_get_separator_indices() { - char const *const test_str = "/This/is/a/test"; - 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_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_separator_indices(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", "/", "../.."); -} - - -int main(void) { - test_relative_path(); - test_get_separator_indices(); - return 0; -} - diff --git a/shell/prompt_utils_test.h b/shell/prompt_utils_test.h deleted file mode 100644 index 59717d0..0000000 --- a/shell/prompt_utils_test.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef PROMPT_UTILS_TEST_H_INCLUDED -#define PROMPT_UTILS_TEST_H_INCLUDED - -void test_get_separator_indices(); -void test_relative_path(); - -#endif diff --git a/shell/shell.c b/shell/shell.c deleted file mode 100644 index dd0085d..0000000 --- a/shell/shell.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include - -#include "array.h" -#include "process.h" -#include "prompt_utils.h" - - -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; - __ssize_t length = 0; - - printf("%s > ", prompt); - if ((length = getline(&line, &cap, stdin)) < 0) { - fprintf(stderr, "Failed to read from STDIN"); - exit(-1); - } - - if (strspn(line, " \n\t") == strlen(line)) { - continue; - } - line[length - 1] = '\0'; // cut the line feed - - Process * processes = NULL; - parse_line(line, &processes); - - if (strcmp(processes[0].argv[0], "cd") == 0) { - - if(arrayLen(processes) != 1) { - perror("Can't chain cd with other processes"); - } - if (arrayLen(processes[0].argv) != 3) { - fprintf(stderr, "usage: cd "); - goto clean; - } - - int ret = chdir(processes[0].argv[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(processes[0].argv[0], "exit") == 0) { - done = true; - } else { - 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); - line = NULL; - cap = 0; - free_processes(&processes); - } - - free((void *)original_wd); - free((void *)prompt); -} -- cgit v1.2.3-54-g00ecf