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

Reply via email to