This is an automated email from the git hooks/post-receive script. guillem pushed a commit to branch main in repository dpkg.
View the commit online: https://git.dpkg.org/cgit/dpkg/dpkg.git/commit/?id=b234e83ba333a5b86e562e89e095858dcb8ee2dd commit b234e83ba333a5b86e562e89e095858dcb8ee2dd Author: Guillem Jover <[email protected]> AuthorDate: Mon Apr 22 23:07:40 2024 +0200 libdpkg: Add new strvec module This module implements a string vector type. --- lib/dpkg/Makefile.am | 3 + lib/dpkg/strvec.c | 145 ++++++++++++++++++++++ lib/dpkg/{execname.h => strvec.h} | 51 ++++++-- lib/dpkg/t/.gitignore | 1 + lib/dpkg/t/t-strvec.c | 252 ++++++++++++++++++++++++++++++++++++++ po/POTFILES.in | 1 + 6 files changed, 445 insertions(+), 8 deletions(-) diff --git a/lib/dpkg/Makefile.am b/lib/dpkg/Makefile.am index 3ff82a904..fad8e14d4 100644 --- a/lib/dpkg/Makefile.am +++ b/lib/dpkg/Makefile.am @@ -125,6 +125,7 @@ libdpkg_la_SOURCES = \ report.c \ string.c \ strhash.c \ + strvec.c \ strwide.c \ subproc.c \ tarfn.c \ @@ -182,6 +183,7 @@ pkginclude_HEADERS = \ progress.h \ report.h \ string.h \ + strvec.h \ subproc.h \ tarfn.h \ treewalk.h \ @@ -228,6 +230,7 @@ test_programs = \ t/t-ehandle \ t/t-error \ t/t-string \ + t/t-strvec \ t/t-file \ t/t-buffer \ t/t-meminfo \ diff --git a/lib/dpkg/strvec.c b/lib/dpkg/strvec.c new file mode 100644 index 000000000..615ed0a51 --- /dev/null +++ b/lib/dpkg/strvec.c @@ -0,0 +1,145 @@ +/* + * libdpkg - Debian packaging suite library routines + * strvec.c - string vector support + * + * Copyright © 2024 Guillem Jover <[email protected]> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <string.h> +#include <stdlib.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/varbuf.h> +#include <dpkg/strvec.h> + +struct strvec * +strvec_new(size_t size) +{ + struct strvec *sv; + + sv = m_malloc(sizeof(*sv)); + if (size == 0) + sv->size = 16; + else + sv->size = size; + sv->used = 0; + sv->vec = m_calloc(sv->size, sizeof(char *)); + + return sv; +} + +void +strvec_grow(struct strvec *sv, size_t need_size) +{ + size_t new_size; + + if ((sv->size - sv->used) >= need_size) + return; + + new_size = (sv->size + need_size) * 2; + if (new_size < sv->size) + ohshit(_("cannot grow strvec to size %zu; it would overflow"), + need_size); + + sv->size = new_size; + sv->vec = m_realloc(sv->vec, sv->size * sizeof(char *)); +} + +void +strvec_push(struct strvec *sv, char *str) +{ + strvec_grow(sv, 1); + sv->vec[sv->used++] = str; +} + +char * +strvec_pop(struct strvec *sv) +{ + if (sv->used == 0) + return NULL; + + return sv->vec[--sv->used]; +} + +void +strvec_drop(struct strvec *sv) +{ + if (sv->used == 0) + return; + + free(sv->vec[--sv->used]); +} + +const char * +strvec_peek(struct strvec *sv) +{ + if (sv->used == 0) + return NULL; + + return sv->vec[sv->used - 1]; +} + +struct strvec * +strvec_split(const char *str, int sep, enum strvec_split_flags flags) +{ + struct strvec *sv; + size_t ini = 0, end = 0; + + sv = strvec_new(0); + + do { + if (str[end] == '\0' || str[end] == sep) { + strvec_push(sv, m_strndup(&str[ini], end - ini)); + if (flags & STRVEC_SPLIT_SKIP_DUP_SEP) { + while (str[end] == sep && str[end + 1] == sep) + end++; + } + ini = end + 1; + } + } while (str[end++]); + + return sv; +} + +char * +strvec_join(struct strvec *sv, int sep) +{ + struct varbuf vb = VARBUF_INIT; + size_t i; + + for (i = 0; i < sv->used; i++) { + if (i) + varbuf_add_char(&vb, sep); + varbuf_add_str(&vb, sv->vec[i]); + } + + return varbuf_detach(&vb); +} + +void +strvec_free(struct strvec *sv) +{ + size_t i; + + for (i = 0; i < sv->used; i++) + free(sv->vec[i]); + free(sv->vec); + free(sv); +} diff --git a/lib/dpkg/execname.h b/lib/dpkg/strvec.h similarity index 50% copy from lib/dpkg/execname.h copy to lib/dpkg/strvec.h index f82efc340..1fb9301e5 100644 --- a/lib/dpkg/execname.h +++ b/lib/dpkg/strvec.h @@ -1,6 +1,6 @@ /* * libdpkg - Debian packaging suite library routines - * execname.h - executable name handling functions + * strvec.h - string vector support * * Copyright © 2024 Guillem Jover <[email protected]> * @@ -18,26 +18,61 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#ifndef LIBDPKG_EXECNAME_H -#define LIBDPKG_EXECNAME_H +#ifndef LIBDPKG_STRVEC_H +#define LIBDPKG_STRVEC_H -#include <sys/types.h> +#include <stddef.h> #include <dpkg/macros.h> DPKG_BEGIN_DECLS /** - * @defgroup execname Executable name handling - * @ingroup dpkg-public + * @defgroup strvec String vector functions + * @ingroup dpkg-internal * @{ */ +struct strvec { + char **vec; + size_t size; + size_t used; +}; + +struct strvec * +strvec_new(size_t size); + +void +strvec_grow(struct strvec *sv, size_t need_size); + +void +strvec_push(struct strvec *sv, char *str); + char * -dpkg_get_pid_execname(pid_t pid); +strvec_pop(struct strvec *sv); + +void +strvec_drop(struct strvec *sv); + +const char * +strvec_peek(struct strvec *sv); + +enum DPKG_ATTR_ENUM_FLAGS strvec_split_flags { + STRVEC_SPLIT_DEFAULT = 0, + STRVEC_SPLIT_SKIP_DUP_SEP = DPKG_BIT(0), +}; + +struct strvec * +strvec_split(const char *str, int sep, enum strvec_split_flags flags); + +char * +strvec_join(struct strvec *sv, int sep); + +void +strvec_free(struct strvec *sv); /** @} */ DPKG_END_DECLS -#endif +#endif /* LIBDPKG_STRVEC_H */ diff --git a/lib/dpkg/t/.gitignore b/lib/dpkg/t/.gitignore index 6ddf3b261..ae1d35596 100644 --- a/lib/dpkg/t/.gitignore +++ b/lib/dpkg/t/.gitignore @@ -32,6 +32,7 @@ t-pkg-queue t-pkg-show t-progname t-string +t-strvec t-subproc t-tar t-test diff --git a/lib/dpkg/t/t-strvec.c b/lib/dpkg/t/t-strvec.c new file mode 100644 index 000000000..456dec211 --- /dev/null +++ b/lib/dpkg/t/t-strvec.c @@ -0,0 +1,252 @@ +/* + * libdpkg - Debian packaging suite library routines + * t-strvec.c - test string vector handling + * + * Copyright © 2024 Guillem Jover <[email protected]> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <dpkg/test.h> +#include <dpkg/dpkg.h> +#include <dpkg/strvec.h> + +static void +test_strvec_stack(void) +{ + struct strvec *sv; + char *str; + + sv = strvec_new(0); + strvec_push(sv, m_strdup("aa")); + strvec_push(sv, m_strdup("bb")); + strvec_push(sv, m_strdup("cc")); + test_pass(sv->size >= 3); + test_pass(sv->used == 3); + test_str(sv->vec[0], ==, "aa"); + test_str(sv->vec[1], ==, "bb"); + test_str(sv->vec[2], ==, "cc"); + strvec_free(sv); + + sv = strvec_new(0); + strvec_push(sv, m_strdup("ini")); + strvec_push(sv, m_strdup("tmp")); + str = strvec_pop(sv); + strvec_push(sv, m_strdup("end")); + test_pass(sv->size >= 2); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, "ini"); + test_str(sv->vec[1], ==, "end"); + test_str(str, ==, "tmp"); + free(str); + strvec_free(sv); + + sv = strvec_new(0); + strvec_push(sv, m_strdup("aa")); + test_str(strvec_peek(sv), ==, "aa"); + strvec_push(sv, m_strdup("bb")); + test_str(strvec_peek(sv), ==, "bb"); + strvec_push(sv, m_strdup("cc")); + test_str(strvec_peek(sv), ==, "cc"); + str = strvec_pop(sv); + test_str(strvec_peek(sv), ==, "bb"); + test_pass(sv->size >= 2); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, "aa"); + test_str(sv->vec[1], ==, "bb"); + test_str(str, ==, "cc"); + free(str); + strvec_free(sv); +} + +static void +test_strvec_parts(void) +{ + struct strvec *sv; + char *str; + + sv = strvec_split("", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 1); + test_str(sv->vec[0], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, ""); + free(str); + strvec_free(sv); + + sv = strvec_split("one", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 1); + test_str(sv->vec[0], ==, "one"); + str = strvec_join(sv, '/'); + test_str(str, ==, "one"); + free(str); + strvec_free(sv); + + sv = strvec_split(":", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "/"); + free(str); + strvec_free(sv); + + sv = strvec_split(":end", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, "end"); + str = strvec_join(sv, '/'); + test_str(str, ==, "/end"); + free(str); + strvec_free(sv); + + sv = strvec_split("ini:", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, "ini"); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "ini/"); + free(str); + strvec_free(sv); + + sv = strvec_split("foo:bar:quux:foo::end:", ':', STRVEC_SPLIT_DEFAULT); + test_pass(sv != NULL); + test_pass(sv->used == 7); + test_str(sv->vec[0], ==, "foo"); + test_str(sv->vec[1], ==, "bar"); + test_str(sv->vec[2], ==, "quux"); + test_str(sv->vec[3], ==, "foo"); + test_str(sv->vec[4], ==, ""); + test_str(sv->vec[5], ==, "end"); + test_str(sv->vec[6], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "foo/bar/quux/foo//end/"); + free(str); + strvec_free(sv); +} + +static void +test_strvec_parts_skip_dup_sep(void) +{ + struct strvec *sv; + char *str; + + sv = strvec_split("", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 1); + test_str(sv->vec[0], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, ""); + free(str); + strvec_free(sv); + + sv = strvec_split("one", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 1); + test_str(sv->vec[0], ==, "one"); + str = strvec_join(sv, '/'); + test_str(str, ==, "one"); + free(str); + strvec_free(sv); + + sv = strvec_split(":", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "/"); + free(str); + strvec_free(sv); + + sv = strvec_split(":::", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "/"); + free(str); + strvec_free(sv); + + sv = strvec_split(":end", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, "end"); + str = strvec_join(sv, '/'); + test_str(str, ==, "/end"); + free(str); + strvec_free(sv); + + sv = strvec_split(":::end", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, ""); + test_str(sv->vec[1], ==, "end"); + str = strvec_join(sv, '/'); + test_str(str, ==, "/end"); + free(str); + strvec_free(sv); + + sv = strvec_split("ini:", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, "ini"); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "ini/"); + free(str); + strvec_free(sv); + + sv = strvec_split("ini:::", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 2); + test_str(sv->vec[0], ==, "ini"); + test_str(sv->vec[1], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "ini/"); + free(str); + strvec_free(sv); + + sv = strvec_split("foo:bar::quux:foo::::end::::", ':', STRVEC_SPLIT_SKIP_DUP_SEP); + test_pass(sv != NULL); + test_pass(sv->used == 6); + test_str(sv->vec[0], ==, "foo"); + test_str(sv->vec[1], ==, "bar"); + test_str(sv->vec[2], ==, "quux"); + test_str(sv->vec[3], ==, "foo"); + test_str(sv->vec[4], ==, "end"); + test_str(sv->vec[5], ==, ""); + str = strvec_join(sv, '/'); + test_str(str, ==, "foo/bar/quux/foo/end/"); + free(str); + strvec_free(sv); +} + +TEST_ENTRY(test) +{ + test_plan(99); + + test_strvec_stack(); + test_strvec_parts(); + test_strvec_parts_skip_dup_sep(); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 38e83b593..cbb2298b6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -64,6 +64,7 @@ lib/dpkg/progress.c lib/dpkg/report.c lib/dpkg/strhash.c lib/dpkg/string.c +lib/dpkg/strvec.c lib/dpkg/strwide.c lib/dpkg/subproc.c lib/dpkg/tarfn.c -- Dpkg.Org's dpkg

