diff --git a/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/Makefile b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/Makefile new file mode 100644 index 0000000..916f2b4 --- /dev/null +++ b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/Makefile @@ -0,0 +1,11 @@ +SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk") + +TARGET := bin/c-get-exec-list +MODULES := +SOURCES := src/main.c $(MODULES) +TSTSOURCES := tests/tests.c $(MODULES) + + +include $(SNP_SHARED_MAKEFILE) + +# CFLAGS += -Werror diff --git a/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/mainpage.dox b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/mainpage.dox new file mode 100644 index 0000000..f7e791a --- /dev/null +++ b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P08: C implementation of P01-Bash get_exec_list_arg.sh + * + * @section Purpose + * + * This is a lab on POSIX file and directory attribute access. + * + */ diff --git a/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/src/main.c b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/src/main.c new file mode 100644 index 0000000..6ff1d9b --- /dev/null +++ b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/src/main.c @@ -0,0 +1,123 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ + +#include // getenv, malloc, free, EXIT_SUCCESS, NULL +#include // strchr +#include // printf, sprintf +#include // struct stat, S_ISDIR, S_ISREG +#include // stat, access +#include // opendir, closedir, readdir, DIR, struct dirent + +/** + * @brief Creates a copy of the string on the heap. + * @param s [IN] The original string. + * @return Returns the allocated copy or NULL if s is NULL or if malloc failed. + * @remark Suggestion: Use the functions malloc(), strcpy(). + */ +static char *malloc_copy(const char *s) +{ + char *p = NULL; + // BEGIN-STUDENTS-TO-ADD-CODE + + + + + + // END-STUDENTS-TO-ADD-CODE + return p; +} + +/** + * @brief Replaces inplace in the buffer all occurances of sep by '\0'. + * @param buffer [INOUT] The string which gets searched and modified. + * @param sep [IN] The separator for which all instances in buffer get replaced by '\0'. + * @return Returns the number of fields which are separated by sep, or 0 if buffer is NULL. + * @remark Empty fields are allowed and to be supported, i.e. given by ":..." or by "...::..." or by "...:". + * @remark Suggestion: Use the function strchr() to search for the separator. + */ +static size_t split_buffer_inplace(char buffer[], char sep) +{ + size_t n = 0; + // BEGIN-STUDENTS-TO-ADD-CODE + + + + + + + + + + + // END-STUDENTS-TO-ADD-CODE + return n; +} + +/** + * @brief If path is an executable directory, list all executable files located directly in that directory - each such file as i:path:file. + * @param i [IN] The number of the path to report. + * @param path [IN] The directory path to search for executables. + * @remark Suggestion: Use the function is_accessible_dir(), is_executable_file(), opendir(), readdir(), closedir(). + */ +static void list_executables(size_t i, const char *path) +{ + // bash: + // [ -n "$p" ] || p="." + // if [ -d "$p" ] && [ -x "$p" ] + // then + // find -L "$p" -maxdepth 1 -type f -executable -printf "$i:%h:%f\n" + // fi + const char *p = path && strlen(path) ? path : "."; // replace an empty path by "." as current directory to allow for appending /name + // BEGIN-STUDENTS-TO-ADD-CODE + + + + + + + + + + // END-STUDENTS-TO-ADD-CODE +} + +// # from lab P01_Bash +// paths="$1" +// [ -n "$paths" ] || paths="$PATH" +// +// # input-field-separator: tells the shell to split in the 'for' loop the $var by ":" +// IFS=":" +// +// for p in $paths +// do +// i=$((i+1)) +// [ -n "$p" ] || p="." +// if [ -d "$p" ] && [ -x "$p" ] +// then +// find -L "$p" -maxdepth 1 -type f -executable -printf "$i:%h:%f\n" 2>/dev/null +// fi +// done + +int main(int argc, const char *argv[]) +{ + char *paths = malloc_copy(argc > 1 && argv[1][0] ? argv[1] : getenv("PATH")); + size_t n = split_buffer_inplace(paths, ':'); // replaces all ':' by '\0' and returns the number of the resulting fields + char *p = paths; + for(size_t i = 1; i <= n; i++) { // 1...n + list_executables(i, p); + p += strlen(p) + 1; // readover the field and the trailing '\0' to get to the next field + } + free(paths); + return EXIT_SUCCESS; +} diff --git a/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/tests/tests.c b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/tests/tests.c new file mode 100644 index 0000000..6a22285 --- /dev/null +++ b/P08_Auflisten_aller_PATH_Executables/c_get_exec_list/tests/tests.c @@ -0,0 +1,88 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Test suite for the given package. + */ +#include +#include +#include +#include +#include +#include +#include "test_utils.h" + +#ifndef TARGET // must be given by the make file --> see test target +#error missing TARGET define +#endif + +/// @brief alias for EXIT_SUCCESS +#define OK EXIT_SUCCESS +/// @brief alias for EXIT_FAILURE +#define FAIL EXIT_FAILURE + +/// @brief The name of the STDOUT text file. +#define OUTFILE "stdout.txt" +/// @brief The name of the STDERR text file. +#define ERRFILE "stderr.txt" + +#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error + +#define PATH_SIZE 256 + +// setup & cleanup +static int setup(void) +{ + remove_file_if_exists(OUTFILE); + remove_file_if_exists(ERRFILE); + return 0; // success +} + +static int teardown(void) +{ + // Do nothing. + // Especially: do not remove result files - they are removed in int setup(void) *before* running a test. + return 0; // success +} + +// tests +static void test_paths(void) +{ + // arrange + const char *name = strrchr(XSTR(TARGET), '/'); + name = name ? name+1 : XSTR(TARGET); + + char buf[PATH_SIZE] = { 0 }; + snprintf(buf, PATH_SIZE, "4:../bin:%s\n", name); + + const char *out_txt[] = + { "1:.:runtest\n" + , buf + }; + const char *err_txt[] = { NULL }; + // act + int exit_code = system(XSTR(TARGET) " .::..:../bin 1>" OUTFILE " 2>" ERRFILE); + // assert + CU_ASSERT_EQUAL(WEXITSTATUS(exit_code), EXIT_SUCCESS); + assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt)); + assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt)); +} + +/** + * @brief Registers and runs the tests. + * @returns success (0) or one of the CU_ErrorCode (>0) + */ +int main(void) +{ + // setup, run, teardown + TestMainBasic("lab test", setup, teardown + , test_paths + ); +}