From 01922bd4272f99b7b9e56f87da0586f4fbf94c90 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:14:41 +0000 Subject: [PATCH] Add g_readdir_entries() to OS calls --- common/os_calls.c | 59 ++++++++++++++++++++++++++++ common/os_calls.h | 13 +++++++ tests/common/test_os_calls.c | 74 ++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) diff --git a/common/os_calls.c b/common/os_calls.c index afd5a95858..08e22173a6 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -37,6 +37,7 @@ #define ctid_t id_t #endif #include +#include #include #include #include @@ -4243,3 +4244,61 @@ g_qsort(void *base, size_t nitems, size_t size, { qsort(base, nitems, size, compar); } + +/*****************************************************************************/ +struct list * +g_readdir(const char *dir) +{ + DIR *handle; + struct list *result = NULL; + struct dirent *dent; + int saved_errno; + + errno = 0; // See readdir(3) + if ((handle = opendir(dir)) != NULL && + (result = list_create()) != NULL) + { + result->auto_free = 1; + while (1) + { + errno = 0; + dent = readdir(handle); + if (dent == NULL) + { + break; // errno = 0 for end-of-dir, or != 0 for error + } + + // Ignore '.' and '..' + if (dent->d_name[0] == '.' && dent->d_name[1] == '\0') + { + continue; + } + if (dent->d_name[0] == '.' && dent->d_name[1] == '.' && + dent->d_name[2] == '\0') + { + continue; + } + + if (!list_add_strdup(result, dent->d_name)) + { + // Memory allocation failure + errno = ENOMEM; + break; + } + } + } + + saved_errno = errno; + if (errno != 0) + { + list_delete(result); + result = NULL; + } + if (handle != NULL) + { + closedir(handle); + } + errno = saved_errno; + + return result; +} diff --git a/common/os_calls.h b/common/os_calls.h index be06b07b45..74cc9f54da 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -411,6 +411,19 @@ void g_qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *)); +/** + * Returns a list of the filenames contained within a directory + * + * @param dir Name of directory + * @return list of directory entry names + * + * If NULL is returned, further information may be available in errno. No + * other errors are specifically logged. + * The special files '.' and '..' are not returned. + */ +struct list * +g_readdir(const char *dir); + /* glib-style wrappers */ #define g_new(struct_type, n_structs) \ (struct_type *) malloc(sizeof(struct_type) * (n_structs)) diff --git a/tests/common/test_os_calls.c b/tests/common/test_os_calls.c index c2a3e09e8b..b1c6b8f87d 100644 --- a/tests/common/test_os_calls.c +++ b/tests/common/test_os_calls.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "os_calls.h" #include "list.h" @@ -20,6 +21,9 @@ // File for testing ro/rw opens #define RO_RW_FILE "./test_ro_rw" +// Directory for testing files +#define TEST_READDIR "./test_readdir" + /******************************************************************************/ /*** * Gets the number of open file descriptors for the current process */ @@ -487,6 +491,74 @@ START_TEST(test_g_sck_fd_overflow) } END_TEST +/******************************************************************************/ +static int qsort_func_strlist(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +START_TEST(test_g_readdir) +{ + int status; + const char *entries[] = { "one", "two", "three", "four", "five", 0}; + unsigned int count = 0; + const char **name; + + // Don't check results of create dir, as we may be re-running this + // after a fail + (void)g_mkdir(TEST_READDIR); + // This should work though + status = g_set_current_dir(TEST_READDIR); + ck_assert_int_eq(status, 0); + + // Create some files in the directory + for (name = entries; *name != NULL; ++name, ++count) + { + int fd = g_file_open_rw(*name); + ck_assert(fd >= 0); + g_file_close(fd); + } + + // Can we read them, and are there the expected number? + struct list *dentries = g_readdir("."); + ck_assert_ptr_ne(dentries, NULL); + ck_assert_ptr_ne(dentries, NULL); + ck_assert_int_eq(dentries->count, count); + + // Sort both lists according to the current locale + qsort(entries, count, sizeof(entries[0]), qsort_func_strlist); + qsort(dentries->items, count, sizeof(dentries->items[0]), + qsort_func_strlist); + + // Check both lists are identical + int i; + for (i = 0 ; i < count; ++i) + { + ck_assert_str_eq(entries[i], (const char *)dentries->items[i]); + } + + // Clean up + list_delete(dentries); + for (i = 0 ; i < count ; ++i) + { + g_file_delete(entries[i]); + } + g_set_current_dir(".."); + status = g_remove_dir(TEST_READDIR); // Returns a boolean(?) + ck_assert_int_ne(status, 0); + + +} +END_TEST + +START_TEST(test_g_readdir_not_dir) +{ + struct list *dentries = g_readdir("NoSuchDirectory"); + ck_assert_ptr_eq(dentries, 0); + ck_assert_int_eq(errno, ENOENT); +} +END_TEST + /******************************************************************************/ Suite * make_suite_test_os_calls(void) @@ -512,6 +584,8 @@ make_suite_test_os_calls(void) tcase_add_test(tc_os_calls, test_g_file_is_open); tcase_add_test(tc_os_calls, test_g_sck_fd_passing); tcase_add_test(tc_os_calls, test_g_sck_fd_overflow); + tcase_add_test(tc_os_calls, test_g_readdir); + tcase_add_test(tc_os_calls, test_g_readdir_not_dir); // Add other test cases in other files suite_add_tcase(s, make_tcase_test_os_calls_signals());