From 7f89eb3cfff465cf32d03a2ae6919252eae67e9b Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Thu, 21 Sep 2023 15:53:14 +0200 Subject: libutil: add a string list helper to replace some of the adhoc ones Signed-off-by: David Oberhollenzer --- bin/sqfs2tar/src/iterator.c | 20 ++++---- bin/sqfs2tar/src/options.c | 41 ++++++----------- bin/sqfs2tar/src/sqfs2tar.c | 4 +- bin/sqfs2tar/src/sqfs2tar.h | 4 +- bin/tar2sqfs/src/options.c | 43 +++++------------ bin/tar2sqfs/src/tar2sqfs.c | 4 +- bin/tar2sqfs/src/tar2sqfs.h | 4 +- include/util/strlist.h | 38 +++++++++++++++ lib/util/Makemodule.am | 7 ++- lib/util/src/strlist.c | 71 ++++++++++++++++++++++++++++ lib/util/test/strlist.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 266 insertions(+), 80 deletions(-) create mode 100644 include/util/strlist.h create mode 100644 lib/util/src/strlist.c create mode 100644 lib/util/test/strlist.c diff --git a/bin/sqfs2tar/src/iterator.c b/bin/sqfs2tar/src/iterator.c index 42f01e2..7879d61 100644 --- a/bin/sqfs2tar/src/iterator.c +++ b/bin/sqfs2tar/src/iterator.c @@ -56,21 +56,21 @@ static bool keep_entry(const sqfs_dir_entry_t *ent) { size_t nlen; - if (num_subdirs == 0) + if (subdirs.count == 0) return true; nlen = strlen(ent->name); - for (size_t i = 0; i < num_subdirs; ++i) { - size_t plen = strlen(subdirs[i]); + for (size_t i = 0; i < subdirs.count; ++i) { + size_t plen = strlen(subdirs.strings[i]); if (nlen <= plen) { - if ((nlen == plen || subdirs[i][nlen] == '/') && - strncmp(subdirs[i], ent->name, nlen) == 0) { + if ((nlen == plen || subdirs.strings[i][nlen] == '/') && + strncmp(subdirs.strings[i], ent->name, nlen) == 0) { return true; } } else if (ent->name[plen] == '/' && - strncmp(subdirs[i], ent->name, plen) == 0) { + strncmp(subdirs.strings[i], ent->name, plen) == 0) { return true; } } @@ -124,8 +124,8 @@ static int next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) if (keep_entry(ent)) { /* XXX: skip the entry, but we MUST recurse here! */ - if (num_subdirs == 1 && !keep_as_dir && - strlen(ent->name) <= strlen(subdirs[0])) { + if (subdirs.count == 1 && !keep_as_dir && + strlen(ent->name) <= strlen(subdirs.strings[0])) { sqfs_free(ent); continue; } @@ -138,8 +138,8 @@ static int next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) sqfs_free(ent); } - if (num_subdirs == 1 && !keep_as_dir) { - size_t plen = strlen(subdirs[0]) + 1; + if (subdirs.count == 1 && !keep_as_dir) { + size_t plen = strlen(subdirs.strings[0]) + 1; memmove(ent->name, ent->name + plen, strlen(ent->name + plen) + 1); diff --git a/bin/sqfs2tar/src/options.c b/bin/sqfs2tar/src/options.c index ba1588d..82c1d0a 100644 --- a/bin/sqfs2tar/src/options.c +++ b/bin/sqfs2tar/src/options.c @@ -70,19 +70,15 @@ bool no_xattr = false; bool no_links = false; char *root_becomes = NULL; -char **subdirs = NULL; -size_t num_subdirs = 0; -static size_t max_subdirs = 0; +strlist_t subdirs = { 0, 0, 0 }; int compressor = 0; const char *filename = NULL; void process_args(int argc, char **argv) { - size_t idx, new_count; const char *name; int i, ret; - char **new; for (;;) { i = getopt_long(argc, argv, short_opts, long_opts, NULL); @@ -99,27 +95,10 @@ void process_args(int argc, char **argv) } break; case 'd': - if (num_subdirs == max_subdirs) { - new_count = max_subdirs ? max_subdirs * 2 : 16; - new = realloc(subdirs, - new_count * sizeof(subdirs[0])); - if (new == NULL) - goto fail_errno; - - max_subdirs = new_count; - subdirs = new; - } - - subdirs[num_subdirs] = strdup(optarg); - if (subdirs[num_subdirs] == NULL) - goto fail_errno; - - if (canonicalize_name(subdirs[num_subdirs])) { - perror(optarg); + if (strlist_append(&subdirs, optarg)) { + fputs("out-of-memory\n", stderr); goto fail; } - - ++num_subdirs; break; case 'r': free(root_becomes); @@ -175,6 +154,14 @@ void process_args(int argc, char **argv) } } + for (size_t idx = 0; idx < subdirs.count; ++idx) { + if (canonicalize_name(subdirs.strings[idx])) { + fprintf(stderr, "Invalid name `%s`\n", + subdirs.strings[idx]); + goto fail; + } + } + if (optind >= argc) { fputs("Missing argument: squashfs image\n", stderr); goto fail_arg; @@ -187,7 +174,7 @@ void process_args(int argc, char **argv) goto fail_arg; } - if (num_subdirs > 1) + if (subdirs.count > 1) keep_as_dir = true; return; @@ -204,9 +191,7 @@ out_success: ret = EXIT_SUCCESS; goto out_exit; out_exit: - for (idx = 0; idx < num_subdirs; ++idx) - free(subdirs[idx]); + strlist_cleanup(&subdirs); free(root_becomes); - free(subdirs); exit(ret); } diff --git a/bin/sqfs2tar/src/sqfs2tar.c b/bin/sqfs2tar/src/sqfs2tar.c index 0f83abf..7404221 100644 --- a/bin/sqfs2tar/src/sqfs2tar.c +++ b/bin/sqfs2tar/src/sqfs2tar.c @@ -171,9 +171,7 @@ int main(int argc, char **argv) out: sqfs_drop(it); sqfs_drop(out_file); - for (i = 0; i < num_subdirs; ++i) - free(subdirs[i]); - free(subdirs); + strlist_cleanup(&subdirs); free(root_becomes); return status; } diff --git a/bin/sqfs2tar/src/sqfs2tar.h b/bin/sqfs2tar/src/sqfs2tar.h index a1a5927..4c9a8f2 100644 --- a/bin/sqfs2tar/src/sqfs2tar.h +++ b/bin/sqfs2tar/src/sqfs2tar.h @@ -11,6 +11,7 @@ #include "common.h" #include "util/util.h" +#include "util/strlist.h" #include "tar/tar.h" #include "xfrm/compress.h" #include "io/xfrm.h" @@ -30,8 +31,7 @@ extern bool no_xattr; extern bool no_links; extern char *root_becomes; -extern char **subdirs; -extern size_t num_subdirs; +extern strlist_t subdirs; extern int compressor; extern const char *filename; diff --git a/bin/tar2sqfs/src/options.c b/bin/tar2sqfs/src/options.c index edc27ce..3443ce3 100644 --- a/bin/tar2sqfs/src/options.c +++ b/bin/tar2sqfs/src/options.c @@ -93,9 +93,7 @@ bool no_tail_pack = false; bool no_symlink_retarget = false; sqfs_writer_cfg_t cfg; char *root_becomes = NULL; -char **excludedirs = NULL; -size_t num_excludedirs = 0; -static size_t max_excludedirs = 0; +strlist_t excludedirs = { 0, 0, 0 }; static void input_compressor_print_available(void) { @@ -119,10 +117,8 @@ static void input_compressor_print_available(void) void process_args(int argc, char **argv) { - size_t idx, new_count; bool have_compressor; int i, ret; - char **new; sqfs_writer_cfg_init(&cfg); @@ -222,29 +218,11 @@ void process_args(int argc, char **argv) cfg.quiet = true; break; case 'E': - if (num_excludedirs == max_excludedirs) { - new_count = max_excludedirs ? max_excludedirs * 2 : 16; - new = realloc(excludedirs, - new_count * sizeof(excludedirs[0])); - if (new == NULL) - goto fail_errno; - - max_excludedirs = new_count; - excludedirs = new; - } - - excludedirs[num_excludedirs] = strdup(optarg); - if (excludedirs[num_excludedirs] == NULL) - goto fail_errno; - - if (canonicalize_name(excludedirs[num_excludedirs])) { - perror(optarg); + if (strlist_append(&excludedirs, optarg)) { + fputs("out-of-memory\n", stderr); goto fail; } - - ++num_excludedirs; break; - case 'h': printf(usagestr, SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); @@ -259,6 +237,14 @@ void process_args(int argc, char **argv) } } + for (size_t idx = 0; idx < excludedirs.count; ++idx) { + if (canonicalize_name(excludedirs.strings[idx])) { + fprintf(stderr, "Invalid name `%s`\n", + excludedirs.strings[idx]); + goto fail; + } + } + if (cfg.num_jobs < 1) cfg.num_jobs = 1; @@ -282,9 +268,6 @@ void process_args(int argc, char **argv) goto fail_arg; } return; -fail_errno: - perror("parsing options"); - goto fail; fail_arg: fputs("Try `tar2sqfsr --help' for more information.\n", stderr); goto fail; @@ -295,9 +278,7 @@ out_success: ret = EXIT_SUCCESS; goto out_exit; out_exit: - for (idx = 0; idx < num_excludedirs; ++idx) - free(excludedirs[idx]); + strlist_cleanup(&excludedirs); free(root_becomes); - free(excludedirs); exit(ret); } diff --git a/bin/tar2sqfs/src/tar2sqfs.c b/bin/tar2sqfs/src/tar2sqfs.c index acb42e2..99f012c 100644 --- a/bin/tar2sqfs/src/tar2sqfs.c +++ b/bin/tar2sqfs/src/tar2sqfs.c @@ -23,8 +23,8 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - topts.excludedirs = excludedirs; - topts.num_excludedirs = num_excludedirs; + topts.excludedirs = excludedirs.strings; + topts.num_excludedirs = excludedirs.count; tar = tar_open_stream(input_file, &topts); sqfs_drop(input_file); diff --git a/bin/tar2sqfs/src/tar2sqfs.h b/bin/tar2sqfs/src/tar2sqfs.h index d2049f2..a5b1089 100644 --- a/bin/tar2sqfs/src/tar2sqfs.h +++ b/bin/tar2sqfs/src/tar2sqfs.h @@ -12,6 +12,7 @@ #include "compat.h" #include "util/util.h" +#include "util/strlist.h" #include "tar/tar.h" #include "tar/format.h" #include "xfrm/compress.h" @@ -29,8 +30,7 @@ extern bool no_tail_pack; extern bool no_symlink_retarget; extern sqfs_writer_cfg_t cfg; extern char *root_becomes; -extern char **excludedirs; -extern size_t num_excludedirs; +extern strlist_t excludedirs; void process_args(int argc, char **argv); diff --git a/include/util/strlist.h b/include/util/strlist.h new file mode 100644 index 0000000..ace4e20 --- /dev/null +++ b/include/util/strlist.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strlist.h + * + * Copyright (C) 2023 David Oberhollenzer + */ +#ifndef UTIL_STRLIST_H +#define UTIL_STRLIST_H + +#include "sqfs/predef.h" +#include + +typedef struct { + char **strings; + size_t count; + size_t capacity; +} strlist_t; + +static SQFS_INLINE void strlist_init(strlist_t *list) +{ + memset(list, 0, sizeof(*list)); +} + +#ifdef __cplusplus +extern "C" { +#endif + +SQFS_INTERNAL int strlist_init_copy(strlist_t *dst, const strlist_t *src); + +SQFS_INTERNAL void strlist_cleanup(strlist_t *list); + +SQFS_INTERNAL int strlist_append(strlist_t *list, const char *str); + +#ifdef __cplusplus +} +#endif + +#endif /* UTIL_STRLIST_H */ diff --git a/lib/util/Makemodule.am b/lib/util/Makemodule.am index 9b2065a..24fb50b 100644 --- a/lib/util/Makemodule.am +++ b/lib/util/Makemodule.am @@ -10,7 +10,7 @@ libutil_a_SOURCES = include/util/util.h include/util/str_table.h \ lib/util/src/source_date_epoch.c lib/util/src/file_cmp.c \ lib/util/src/hex_decode.c lib/util/src/base64_decode.c \ lib/util/src/get_line.c lib/util/src/split_line.c \ - lib/util/src/parse_int.c + lib/util/src/parse_int.c lib/util/src/strlist.c include/util/strlist.h libutil_a_CFLAGS = $(AM_CFLAGS) libutil_a_CPPFLAGS = $(AM_CPPFLAGS) @@ -87,11 +87,14 @@ test_split_line_LDADD = libutil.a libcompat.a test_parse_int_SOURCES = lib/util/test/parse_int.c test_parse_int_LDADD = libutil.a libcompat.a +test_strlist_SOURCES = lib/util/test/strlist.c +test_strlist_LDADD = libutil.a libcompat.a + LIBUTIL_TESTS = \ test_str_table test_rbtree test_xxhash test_threadpool test_ismemzero \ test_canonicalize_name test_filename_sane test_filename_sane_w32 \ test_sdate_epoch test_hex_decode test_base64_decode test_get_line \ - test_split_line test_parse_int + test_split_line test_parse_int test_strlist check_PROGRAMS += $(LIBUTIL_TESTS) TESTS += $(LIBUTIL_TESTS) diff --git a/lib/util/src/strlist.c b/lib/util/src/strlist.c new file mode 100644 index 0000000..9d1fd78 --- /dev/null +++ b/lib/util/src/strlist.c @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * strlist.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#include "config.h" +#include "util/strlist.h" +#include "util/util.h" +#include "compat.h" + +#include + +void strlist_cleanup(strlist_t *list) +{ + for (size_t i = 0; i < list->count; ++i) + free(list->strings[i]); + + free(list->strings); + memset(list, 0, sizeof(*list)); +} + +int strlist_init_copy(strlist_t *dst, const strlist_t *src) +{ + memset(dst, 0, sizeof(*dst)); + + dst->strings = alloc_array(sizeof(char *), src->capacity); + if (dst->strings == NULL) + return -1; + + dst->count = src->count; + dst->capacity = src->capacity; + + for (size_t i = 0; i < src->count; ++i) { + dst->strings[i] = strdup(src->strings[i]); + + if (dst->strings[i] == NULL) { + dst->count = i; + strlist_cleanup(dst); + return -1; + } + } + + return 0; +} + +int strlist_append(strlist_t *list, const char *str) +{ + if (list->count == list->capacity) { + size_t new_cap = list->capacity ? (list->capacity * 2) : 128; + size_t new_sz; + char **new; + + if (SZ_MUL_OV(new_cap, sizeof(char *), &new_sz)) + return -1; + + new = realloc(list->strings, new_sz); + if (new == NULL) + return -1; + + list->capacity = new_cap; + list->strings = new; + } + + list->strings[list->count] = strdup(str); + if (list->strings[list->count] == NULL) + return -1; + + list->count += 1; + return 0; +} diff --git a/lib/util/test/strlist.c b/lib/util/test/strlist.c new file mode 100644 index 0000000..23587b3 --- /dev/null +++ b/lib/util/test/strlist.c @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * strlist.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#include "config.h" +#include "util/util.h" +#include "util/test.h" +#include "util/strlist.h" + +int main(int argc, char **argv) +{ + const char *str0, *str1, *str2; + strlist_t a, b; + int ret; + (void)argc; (void)argv; + + str0 = "foo"; + str1 = "bar"; + str2 = "baz"; + + /* init */ + strlist_init(&a); + TEST_NULL(a.strings); + TEST_EQUAL_UI(a.count, 0); + TEST_EQUAL_UI(a.capacity, 0); + + /* append a string */ + ret = strlist_append(&a, str0); + TEST_EQUAL_I(ret, 0); + + TEST_EQUAL_UI(a.count, 1); + TEST_ASSERT(a.capacity >= a.count); + + TEST_NOT_NULL(a.strings); + TEST_NOT_NULL(a.strings[0]); + + TEST_STR_EQUAL(a.strings[0], str0); + TEST_ASSERT(a.strings[0] != str0); + + /* append another */ + ret = strlist_append(&a, str1); + TEST_EQUAL_I(ret, 0); + + TEST_EQUAL_UI(a.count, 2); + TEST_ASSERT(a.capacity >= a.count); + + TEST_NOT_NULL(a.strings); + TEST_NOT_NULL(a.strings[0]); + TEST_NOT_NULL(a.strings[1]); + + TEST_ASSERT(a.strings[0] != str0); + TEST_ASSERT(a.strings[1] != str1); + TEST_STR_EQUAL(a.strings[0], str0); + TEST_STR_EQUAL(a.strings[1], str1); + + /* and one more */ + ret = strlist_append(&a, str2); + TEST_EQUAL_I(ret, 0); + + TEST_EQUAL_UI(a.count, 3); + TEST_ASSERT(a.capacity >= a.count); + + TEST_NOT_NULL(a.strings); + TEST_NOT_NULL(a.strings[0]); + TEST_NOT_NULL(a.strings[1]); + TEST_NOT_NULL(a.strings[2]); + + TEST_ASSERT(a.strings[0] != str0); + TEST_ASSERT(a.strings[1] != str1); + TEST_ASSERT(a.strings[2] != str2); + TEST_STR_EQUAL(a.strings[0], str0); + TEST_STR_EQUAL(a.strings[1], str1); + TEST_STR_EQUAL(a.strings[2], str2); + + /* copy */ + strlist_init_copy(&b, &a); + TEST_NOT_NULL(b.strings); + TEST_EQUAL_UI(b.count, a.count); + TEST_EQUAL_UI(b.capacity, a.capacity); + + TEST_ASSERT(b.strings != a.strings); + + TEST_NOT_NULL(b.strings[0]); + TEST_NOT_NULL(b.strings[1]); + TEST_NOT_NULL(b.strings[2]); + + TEST_ASSERT(b.strings[0] != a.strings[0]); + TEST_ASSERT(b.strings[1] != a.strings[1]); + TEST_ASSERT(b.strings[2] != a.strings[2]); + + TEST_STR_EQUAL(b.strings[0], str0); + TEST_STR_EQUAL(b.strings[1], str1); + TEST_STR_EQUAL(b.strings[2], str2); + + /* cleanup */ + strlist_cleanup(&a); + strlist_cleanup(&b); + + TEST_NULL(a.strings); + TEST_NULL(b.strings); + + TEST_EQUAL_UI(a.count, 0); + TEST_EQUAL_UI(b.count, 0); + + TEST_EQUAL_UI(a.capacity, 0); + TEST_EQUAL_UI(b.capacity, 0); + return EXIT_SUCCESS; +} -- cgit v1.2.3