From 6ea37119a3c4e9364af10a2617be589f2412ac51 Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Sat, 4 Nov 2023 12:22:54 +0100 Subject: [PATCH] easy: add a function to list directory paths This change adds `sqsh_easy_directory_list_path()`, a function to list the contents of a directory as a list of file paths. This enables the user to get a list of paths that can be directly passed to other easy functions like `sqsh_easy_file_content()`. This change also adds a test for the new function. --- include/sqsh_easy.h | 16 ++++++- libsqsh/src/easy/directory.c | 83 ++++++++++++++++++++++++++++++++++- test/libsqsh/easy/directory.c | 42 ++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/include/sqsh_easy.h b/include/sqsh_easy.h index 2d0772f5a..c4bf33d1e 100644 --- a/include/sqsh_easy.h +++ b/include/sqsh_easy.h @@ -119,7 +119,7 @@ sqsh_easy_file_mtime(struct SqshArchive *archive, const char *path, int *err); */ /** - * @brief retrieves the contents of a directory. + * @brief retrieves the contents of a directory as a list of file names * * The returned list needs to be released with `free()`. * @@ -132,6 +132,20 @@ sqsh_easy_file_mtime(struct SqshArchive *archive, const char *path, int *err); char **sqsh_easy_directory_list( struct SqshArchive *archive, const char *path, int *err); +/** + * @brief retrieves the contents of a directory as a list of file paths + * + * The returned list needs to be released with `free()`. + * + * @param[in] archive The sqsh archive context. + * @param[in] path The path the file or directory. + * @param[out] err Pointer to an int where the error code will be stored. + * + * @return A list of files and directories on success, NULL on error. + */ +char **sqsh_easy_directory_list_path( + struct SqshArchive *archive, const char *path, int *err); + /*************************************** * easy/xattr.c */ diff --git a/libsqsh/src/easy/directory.c b/libsqsh/src/easy/directory.c index e673cb68c..2a9cc4619 100644 --- a/libsqsh/src/easy/directory.c +++ b/libsqsh/src/easy/directory.c @@ -31,7 +31,6 @@ * @file file.c */ -#include #define _DEFAULT_SOURCE #include @@ -46,6 +45,88 @@ #include #include +struct DirectoryIterator { + struct SqshDirectoryIterator dir; + const char *path; + size_t path_size; + struct CxBuffer value; +}; + +static int +directory_path_collector_next( + void *iterator, const char **value, size_t *size) { + struct DirectoryIterator *it = iterator; + int rv = 0; + if (sqsh_directory_iterator_next(&it->dir, &rv)) { + cx_buffer_drain(&it->value); + rv = cx_buffer_append( + &it->value, (const uint8_t *)it->path, it->path_size); + if (rv < 0) { + goto out; + } + rv = cx_buffer_append(&it->value, (const uint8_t *)"/", 1); + if (rv < 0) { + goto out; + } + rv = cx_buffer_append( + &it->value, + (const uint8_t *)sqsh_directory_iterator_name(&it->dir), + (size_t)sqsh_directory_iterator_name_size(&it->dir)); + if (rv < 0) { + goto out; + } + *value = (const char *)cx_buffer_data(&it->value); + *size = cx_buffer_size(&it->value); + } +out: + return rv; +} + +char ** +sqsh_easy_directory_list_path( + struct SqshArchive *archive, const char *path, int *err) { + int rv = 0; + struct SqshFile *file = NULL; + struct DirectoryIterator iterator = {0}; + char **list = NULL; + + file = sqsh_open(archive, path, &rv); + if (rv < 0) { + goto out; + } + + rv = sqsh__directory_iterator_init(&iterator.dir, file); + if (rv < 0) { + goto out; + } + + rv = cx_buffer_init(&iterator.value); + if (rv < 0) { + goto out; + } + + iterator.path = path; + iterator.path_size = strlen(path); + while (iterator.path_size > 0 && + iterator.path[iterator.path_size - 1] == '/') { + iterator.path_size--; + } + + rv = cx_collect(&list, directory_path_collector_next, &iterator.dir); + if (rv < 0) { + goto out; + } + +out: + cx_buffer_cleanup(&iterator.value); + sqsh__directory_iterator_cleanup(&iterator.dir); + sqsh_close(file); + if (err) { + *err = rv; + } + return list; +} + static int directory_collector_next(void *iterator, const char **value, size_t *size) { int rv = 0; diff --git a/test/libsqsh/easy/directory.c b/test/libsqsh/easy/directory.c index b362d68dd..42d4bbc20 100644 --- a/test/libsqsh/easy/directory.c +++ b/test/libsqsh/easy/directory.c @@ -80,6 +80,48 @@ list_two_files(void) { sqsh__archive_cleanup(&archive); } +static void +list_two_paths(void) { + int rv = 0; + struct SqshArchive archive = {0}; + uint8_t payload[] = { + /* clang-format off */ + SQSH_HEADER, + /* inode */ + [INODE_TABLE_OFFSET] = METABLOCK_HEADER(0, 1024), + INODE_HEADER(1, 0, 0, 0, 0, 1), + INODE_BASIC_DIR(0, 33, 0, 0), + [INODE_TABLE_OFFSET+2+128] = + INODE_HEADER(3, 0, 0, 0, 0, 2), + INODE_BASIC_SYMLINK(3), + 't', 'g', 't', + [INODE_TABLE_OFFSET+2+256] = + INODE_HEADER(2, 0, 0, 0, 0, 3), + INODE_BASIC_FILE(0, 0xFFFFFFFF, 0, 0), + [DIRECTORY_TABLE_OFFSET] = METABLOCK_HEADER(0, 128), + DIRECTORY_HEADER(2, 0, 0), + DIRECTORY_ENTRY(128, 2, 3, 1), + '1', + DIRECTORY_ENTRY(256, 3, 2, 1), + '2', + [FRAGMENT_TABLE_OFFSET] = 0, + /* clang-format on */ + }; + mk_stub(&archive, payload, sizeof(payload)); + + char **dir_list = sqsh_easy_directory_list_path(&archive, "/", &rv); + assert(rv == 0); + assert(dir_list != NULL); + + assert(strcmp(dir_list[0], "/1") == 0); + assert(strcmp(dir_list[1], "/2") == 0); + assert(dir_list[2] == NULL); + + free(dir_list); + sqsh__archive_cleanup(&archive); +} + DECLARE_TESTS TEST(list_two_files) +TEST(list_two_paths) END_TESTS