diff --git a/common/strlist.c b/common/strlist.c
index 6feb3a45a..4ad0b0816 100644
--- a/common/strlist.c
+++ b/common/strlist.c
@@ -1,6 +1,6 @@
/* strlist.c - string helpers
* Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc.
- * Copyright (C) 2015 g10 Code GmbH
+ * Copyright (C) 2015, 2024 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -27,6 +27,7 @@
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
*/
#include
@@ -134,27 +135,39 @@ append_to_strlist( strlist_t *list, const char *string )
}
+/* Core of append_to_strlist_try which take the length of the string.
+ * Return the item added to the end of the list. Or NULL in case of
+ * an error. */
+static strlist_t
+do_append_to_strlist (strlist_t *list, const char *string, size_t stringlen)
+{
+ strlist_t r, sl;
+
+ sl = xtrymalloc (sizeof *sl + stringlen);
+ if (!sl)
+ return NULL;
+
+ sl->flags = 0;
+ memcpy (sl->d, string, stringlen);
+ sl->d[stringlen] = 0;
+ sl->next = NULL;
+ if (!*list)
+ *list = sl;
+ else
+ {
+ for (r = *list; r->next; r = r->next)
+ ;
+ r->next = sl;
+ }
+ return sl;
+}
+
+
/* Add STRING to the LIST at the end. */
strlist_t
append_to_strlist_try (strlist_t *list, const char *string)
{
- strlist_t r, sl;
-
- sl = xtrymalloc( sizeof *sl + strlen(string));
- if (sl == NULL)
- return NULL;
-
- sl->flags = 0;
- strcpy(sl->d, string);
- sl->next = NULL;
- if( !*list )
- *list = sl;
- else {
- for( r = *list; r->next; r = r->next )
- ;
- r->next = sl;
- }
- return sl;
+ return do_append_to_strlist (list, string, strlen (string));
}
@@ -175,6 +188,75 @@ append_to_strlist2( strlist_t *list, const char *string, int is_utf8 )
}
+/* Tokenize STRING using the delimiters from DELIM and append each
+ * token to the string list LIST. On success a pinter into LIST with
+ * the first new token is returned. Returns NULL on error and sets
+ * ERRNO. Take care, an error with ENOENT set mean that no tokens
+ * were found in STRING. */
+strlist_t
+tokenize_to_strlist (strlist_t *list, const char *string, const char *delim)
+{
+ const char *s, *se;
+ size_t n;
+ strlist_t newlist = NULL;
+ strlist_t tail;
+
+ s = string;
+ do
+ {
+ se = strpbrk (s, delim);
+ if (se)
+ n = se - s;
+ else
+ n = strlen (s);
+ if (!n)
+ continue; /* Skip empty string. */
+ tail = do_append_to_strlist (&newlist, s, n);
+ if (!tail)
+ {
+ free_strlist (newlist);
+ return NULL;
+ }
+ trim_spaces (tail->d);
+ if (!*tail->d) /* Remove new but empty item from the list. */
+ {
+ tail = strlist_prev (newlist, tail);
+ if (tail)
+ {
+ free_strlist (tail->next);
+ tail->next = NULL;
+ }
+ else if (newlist)
+ {
+ free_strlist (newlist);
+ newlist = NULL;
+ }
+ continue;
+ }
+ }
+ while (se && (s = se + 1));
+
+ if (!newlist)
+ {
+ /* Not items found. Indicate this by returnning NULL with errno
+ * set to ENOENT. */
+ gpg_err_set_errno (ENOENT);
+ return NULL;
+ }
+
+ /* Append NEWLIST to LIST. */
+ if (!*list)
+ *list = newlist;
+ else
+ {
+ for (tail = *list; tail->next; tail = tail->next)
+ ;
+ tail->next = newlist;
+ }
+ return newlist;
+}
+
+
/* Return a copy of LIST. This function terminates the process on
memory shortage.*/
strlist_t
diff --git a/common/strlist.h b/common/strlist.h
index 641ea06cb..bf1ffa903 100644
--- a/common/strlist.h
+++ b/common/strlist.h
@@ -52,6 +52,9 @@ strlist_t append_to_strlist_try (strlist_t *list, const char *string);
strlist_t append_to_strlist2 (strlist_t *list, const char *string,
int is_utf8);
+strlist_t tokenize_to_strlist (strlist_t *list,
+ const char *string, const char *delim);
+
strlist_t strlist_copy (strlist_t list);
strlist_t strlist_prev (strlist_t head, strlist_t node);
diff --git a/common/t-strlist.c b/common/t-strlist.c
index fdbeb9b99..65fc52420 100644
--- a/common/t-strlist.c
+++ b/common/t-strlist.c
@@ -72,6 +72,194 @@ test_strlist_rev (void)
}
+static void
+test_tokenize_to_strlist (void)
+{
+ struct {
+ const char *s;
+ const char *delim;
+ int error_expected;
+ const char *items_expected[10];
+ } tv[] = {
+ {
+ "", ":",
+ 1, { NULL }
+ },
+ {
+ "a", ":",
+ 0, { "a", NULL }
+ },
+ {
+ ":", ":",
+ 1, { NULL }
+ },
+ {
+ "::", ":",
+ 1, { NULL }
+ },
+ {
+ "a:b:c", ":",
+ 0, { "a", "b", "c", NULL }
+ },
+ {
+ "a:b:", ":",
+ 0, { "a", "b", NULL }
+ },
+ {
+ "a:b", ":",
+ 0, { "a", "b", NULL }
+ },
+ {
+ "aa:b:cd", ":",
+ 0, { "aa", "b", "cd", NULL }
+ },
+ {
+ "aa::b:cd", ":",
+ 0, { "aa", "b", "cd", NULL }
+ },
+ {
+ "::b:cd", ":",
+ 0, { "b", "cd", NULL }
+ },
+ {
+ "aa: : b:cd ", ":",
+ 0, { "aa", "b", "cd", NULL }
+ },
+ {
+ " aa: : b: cd ", ":",
+ 0, { "aa", "b", "cd", NULL }
+ },
+ {
+ " :", ":",
+ 1, { NULL }
+ },
+ {
+ " : ", ":",
+ 1, { NULL }
+ },
+ {
+ ": ", ":",
+ 1, { NULL }
+ },
+ {
+ ": x ", ":",
+ 0, { "x", NULL }
+ },
+ {
+ "a:bc:cde:fghi:jklmn::foo:", ":",
+ 0, { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL }
+ },
+ {
+ ",a,bc,,def,", ",",
+ 0, { "a", "bc", "def", NULL }
+ },
+ {
+ " a ", " ",
+ 0, { "a", NULL }
+ },
+ {
+ " ", " ",
+ 1, { NULL }
+ },
+ {
+ "a:bc:c de:fg hi:jklmn::foo :", ":",
+ 0, { "a", "bc", "c de", "fg hi", "jklmn", "foo", NULL }
+ },
+ {
+ "", " ",
+ 1, { NULL }
+ }
+ };
+ const char *prefixes[3] = { "abc", "bcd", "efg" };
+ int tidx;
+ int nprefixes; /* Number of items in already in the list. */
+ strlist_t list = NULL;
+
+ for (nprefixes = 0; nprefixes < DIM (prefixes); nprefixes++)
+ for (tidx = 0; tidx < DIM(tv); tidx++)
+ {
+ int item_count_expected;
+ int i;
+ strlist_t sl, newitems;
+
+ for (item_count_expected = 0;
+ tv[tidx].items_expected[item_count_expected];
+ item_count_expected++)
+ ;
+
+ /* printf ("np=%d testing %d \"%s\" delim=\"%s\"\n", */
+ /* nprefixes, tidx, tv[tidx].s, tv[tidx].delim); */
+ for (i=0; i < nprefixes; i++)
+ append_to_strlist (&list, prefixes[i]);
+
+ newitems = tokenize_to_strlist (&list, tv[tidx].s, tv[tidx].delim);
+ if (!newitems)
+ {
+ if (gpg_err_code_from_syserror () == GPG_ERR_ENOENT
+ && tv[tidx].error_expected)
+ {
+ /* Good. But need to check the prefixes. */
+ for (sl=list, i=0; i < nprefixes; i++, sl=sl->next)
+ {
+ if (!sl || strcmp (prefixes[i], sl->d))
+ {
+ printf ("For item %d prefix item %d, expected '%s'\n",
+ tidx, i, prefixes[i]);
+ fail (tidx * 1000 + 40 + i + 1);
+ }
+ }
+ }
+ else
+ fail (tidx * 1000);
+ }
+ else if (tv[tidx].error_expected)
+ {
+ printf ("got items");
+ for (sl = list; sl; sl = sl->next)
+ printf (" \"%s\"", sl->d);
+ printf ("\n");
+ fail (tidx * 1000);
+ }
+ else
+ {
+ if (strlist_length (list) != nprefixes + item_count_expected)
+ fail (tidx * 1000);
+ else
+ {
+ for (sl=list, i=0; i < nprefixes; i++, sl=sl->next)
+ {
+ if (!sl || strcmp (prefixes[i], sl->d))
+ {
+ printf ("For item %d prefix item %d, expected '%s'\n",
+ tidx, i, prefixes[i]);
+ fail (tidx * 1000 + 50 + i + 1);
+ }
+ }
+ for (i=0; i < item_count_expected; i++, sl=sl->next)
+ {
+ if (!sl)
+ {
+ printf ("No item at item index %d\n", i);
+ fail (tidx * 1000 + i + 0);
+ break;
+ }
+ if (strcmp (tv[tidx].items_expected[i], sl->d))
+ {
+ printf ("For item %d, expected '%s', but got '%s'\n",
+ i, tv[tidx].items_expected[i], sl->d);
+ fail (tidx * 1000 + 10 + i + 1);
+ }
+ }
+ }
+ }
+
+ free_strlist (list);
+ list = NULL;
+ }
+}
+
+
+
int
main (int argc, char **argv)
{
@@ -79,6 +267,7 @@ main (int argc, char **argv)
(void)argv;
test_strlist_rev ();
+ test_tokenize_to_strlist ();
return 0;
}