From ea845f2552d5cd20fd20996c44e4bb0d2fc04abd Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 17 Jul 2024 14:32:39 +0900 Subject: [PATCH] Impl. setup for de/compression Some setup code in preparation of doing the actual file compression when creating an archive. --- src/archiver.c | 46 +++++++++++++++++++++++++++++++ src/archiver.h | 4 +++ src/helpers.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/helpers.h | 8 ++++++ src/test.c | 27 ++++++++++++++++++ 5 files changed, 159 insertions(+) diff --git a/src/archiver.c b/src/archiver.c index 592d121..67fa5c2 100644 --- a/src/archiver.c +++ b/src/archiver.c @@ -22,6 +22,14 @@ #include #include +#if SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_COSMOPOLITAN || \ + SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_MAC || \ + SIMPLE_ARCHIVER_PLATFORM == SIMPLE_ARCHIVER_PLATFORM_LINUX +#include +#include +#include +#endif + #include "helpers.h" typedef struct SDArchiverInternalToWrite { @@ -465,3 +473,41 @@ int simple_archiver_print_archive_info(FILE *in_f) { return 0; } + +int simple_archiver_de_compress(int pipe_fd_in, int pipe_fd_out, + const char *cmd) { + posix_spawn_file_actions_t file_actions; + memset(&file_actions, 0, sizeof(file_actions)); + if (posix_spawn_file_actions_init(&file_actions) != 0) { + close(pipe_fd_in); + close(pipe_fd_out); + return 1; + } else if (posix_spawn_file_actions_adddup2(&file_actions, pipe_fd_in, 0) != + 0) { + posix_spawn_file_actions_destroy(&file_actions); + close(pipe_fd_in); + close(pipe_fd_out); + return 2; + } else if (posix_spawn_file_actions_adddup2(&file_actions, pipe_fd_out, 1) != + 0) { + posix_spawn_file_actions_destroy(&file_actions); + close(pipe_fd_in); + close(pipe_fd_out); + return 3; + } + + __attribute__((cleanup( + simple_archiver_helper_cmd_string_argv_free_ptr))) char **cmd_argv = + simple_archiver_helper_cmd_string_to_argv(cmd); + + pid_t spawned_pid; + if (posix_spawnp(&spawned_pid, cmd_argv[0], &file_actions, NULL, cmd_argv, + NULL) != 0) { + posix_spawn_file_actions_destroy(&file_actions); + return 4; + } + + posix_spawn_file_actions_destroy(&file_actions); + + return 0; +} diff --git a/src/archiver.h b/src/archiver.h index 91ff4e7..d0e0697 100644 --- a/src/archiver.h +++ b/src/archiver.h @@ -55,4 +55,8 @@ int simple_archiver_write_all(FILE *out_f, SDArchiverState *state, /// Returns zero on success. int simple_archiver_print_archive_info(FILE *in_f); +/// Returns zero on success. +int simple_archiver_de_compress(int pipe_fd_in, int pipe_fd_out, + const char *cmd); + #endif diff --git a/src/helpers.c b/src/helpers.c index 5539448..77a94fb 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -18,6 +18,10 @@ #include "helpers.h" +#include +#include +#include + int simple_archiver_helper_is_big_endian(void) { union { uint32_t i; @@ -54,3 +58,73 @@ void simple_archiver_helper_64_bit_be(uint64_t *value) { } } } + +char **simple_archiver_helper_cmd_string_to_argv(const char *cmd) { + unsigned int capacity = 16; + unsigned int idx = 0; + // Size of every pointer is the same, so using size of (void*) should be ok. + char **args = malloc(sizeof(void *) * capacity); + memset(args, 0, sizeof(void *) * capacity); + + unsigned int word_capacity = 16; + unsigned int word_idx = 0; + char *word = malloc(word_capacity); + memset(word, 0, word_capacity); + for (const char *c = cmd; *c != 0; ++c) { + if (isspace(*c)) { + if (word_idx > 0) { + if (idx >= capacity) { + capacity *= 2; + args = realloc(args, sizeof(void *) * capacity); + } + args[idx] = malloc(word_idx + 1); + memcpy(args[idx], word, word_idx); + args[idx][word_idx] = 0; + ++idx; + word_idx = 0; + } + } else { + if (word_idx >= word_capacity) { + word_capacity *= 2; + word = realloc(word, word_capacity); + } + word[word_idx++] = *c; + } + } + if (word_idx > 0) { + if (idx >= capacity) { + capacity *= 2; + args = realloc(args, sizeof(void *) * capacity); + } + args[idx] = malloc(word_idx + 1); + memcpy(args[idx], word, word_idx); + args[idx][word_idx] = 0; + ++idx; + word_idx = 0; + } + + free(word); + + if (idx >= capacity) { + args = realloc(args, sizeof(void *) * (capacity + 1)); + args[capacity] = NULL; + } + + return args; +} + +void simple_archiver_helper_cmd_string_argv_free(char **argv_strs) { + if (argv_strs) { + for (char **iter = argv_strs; *iter != 0; ++iter) { + free(*iter); + } + free(argv_strs); + } +} + +void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs) { + if (argv_strs) { + simple_archiver_helper_cmd_string_argv_free(*argv_strs); + *argv_strs = NULL; + } +} diff --git a/src/helpers.h b/src/helpers.h index cb4c040..4643a76 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -35,4 +35,12 @@ void simple_archiver_helper_32_bit_be(uint32_t *value); /// Swaps value from/to big-endian. Nop on big-endian systems. void simple_archiver_helper_64_bit_be(uint64_t *value); +/// Returns a array of c-strings on success, NULL on error. +/// The returned array must be free'd with +/// simple_archiver_helper_cmd_string_argv_free(...). +char **simple_archiver_helper_cmd_string_to_argv(const char *cmd); + +void simple_archiver_helper_cmd_string_argv_free(char **argv_strs); +void simple_archiver_helper_cmd_string_argv_free_ptr(char ***argv_strs); + #endif diff --git a/src/test.c b/src/test.c index 6d1dfdb..09a1f9a 100644 --- a/src/test.c +++ b/src/test.c @@ -44,6 +44,15 @@ static int checks_passed = 0; ++checks_passed; \ } \ } while (0); +#define CHECK_STREQ(a, b) \ + do { \ + ++checks_checked; \ + if (strcmp((a), (b)) == 0) { \ + ++checks_passed; \ + } else { \ + printf("CHECK_STREQ at line %u failed: %s != %s\n", __LINE__, #a, #b); \ + } \ + } while (0); int main(void) { // Test parser. @@ -170,6 +179,24 @@ int main(void) { } } + // Test helpers cmd string to argv. + do { + const char *cmd = "zstd --compress --ultra\n -20 derp_file"; + char **result_argv = simple_archiver_helper_cmd_string_to_argv(cmd); + CHECK_TRUE(result_argv); + if (!result_argv) { + break; + } + CHECK_STREQ("zstd", result_argv[0]); + CHECK_STREQ("--compress", result_argv[1]); + CHECK_STREQ("--ultra", result_argv[2]); + CHECK_STREQ("-20", result_argv[3]); + CHECK_STREQ("derp_file", result_argv[4]); + CHECK_TRUE(result_argv[5] == NULL); + + simple_archiver_helper_cmd_string_argv_free(result_argv); + } while (0); + printf("Checks checked: %u\n", checks_checked); printf("Checks passed: %u\n", checks_passed); return checks_passed == checks_checked ? 0 : 1;