diff --git a/common/strlist.c b/common/strlist.c
index 6feb3a45a..4ad0b0816 100644
--- a/common/strlist.c
+++ b/common/strlist.c
@@ -1,281 +1,363 @@
 /* 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.
  *
  * GnuPG is free software; you can redistribute and/or modify this
  * part of GnuPG under the terms of either
  *
  *   - the GNU Lesser General Public License as published by the Free
  *     Software Foundation; either version 3 of the License, or (at
  *     your option) any later version.
  *
  * or
  *
  *   - 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.
  *
  * or both in parallel, as here.
  *
  * GnuPG 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 copies of the GNU General Public License
  * and the GNU Lesser General Public License along with this program;
  * if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
 
 #include "util.h"
 #include "common-defs.h"
 #include "strlist.h"
 #include "utf8conv.h"
 #include "mischelp.h"
 
 void
 free_strlist( strlist_t sl )
 {
     strlist_t sl2;
 
     for(; sl; sl = sl2 ) {
 	sl2 = sl->next;
 	xfree(sl);
     }
 }
 
 
 void
 free_strlist_wipe (strlist_t sl)
 {
     strlist_t sl2;
 
     for(; sl; sl = sl2 ) {
 	sl2 = sl->next;
         wipememory (sl, sizeof *sl + strlen (sl->d));
 	xfree(sl);
     }
 }
 
 
 /* Add STRING to the LIST at the front.  This function terminates the
    process on memory shortage.  */
 strlist_t
 add_to_strlist( strlist_t *list, const char *string )
 {
     strlist_t sl;
 
     sl = xmalloc( sizeof *sl + strlen(string));
     sl->flags = 0;
     strcpy(sl->d, string);
     sl->next = *list;
     *list = sl;
     return sl;
 }
 
 
 /* Add STRING to the LIST at the front.  This function returns NULL
    and sets ERRNO on memory shortage.  */
 strlist_t
 add_to_strlist_try (strlist_t *list, const char *string)
 {
   strlist_t sl;
 
   sl = xtrymalloc (sizeof *sl + strlen (string));
   if (sl)
     {
       sl->flags = 0;
       strcpy (sl->d, string);
       sl->next = *list;
       *list = sl;
     }
   return sl;
 }
 
 
 /* Same as add_to_strlist() but if IS_UTF8 is *not* set, a conversion
    to UTF-8 is done.  This function terminates the process on memory
    shortage.  */
 strlist_t
 add_to_strlist2( strlist_t *list, const char *string, int is_utf8 )
 {
   strlist_t sl;
 
   if (is_utf8)
     sl = add_to_strlist( list, string );
   else
     {
       char *p = native_to_utf8( string );
       sl = add_to_strlist( list, p );
       xfree ( p );
     }
   return sl;
 }
 
 
 /* Add STRING to the LIST at the end.  This function terminates the
    process on memory shortage.  */
 strlist_t
 append_to_strlist( strlist_t *list, const char *string )
 {
   strlist_t sl;
   sl = append_to_strlist_try (list, string);
   if (!sl)
     xoutofcore ();
   return sl;
 }
 
 
-/* Add STRING to the LIST at the end.  */
-strlist_t
-append_to_strlist_try (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;
+  strlist_t r, sl;
 
-    sl = xtrymalloc( sizeof *sl + strlen(string));
-    if (sl == NULL)
-      return NULL;
+  sl = xtrymalloc (sizeof *sl + stringlen);
+  if (!sl)
+    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;
+  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;
+  return sl;
+}
+
+
+/* Add STRING to the LIST at the end.  */
+strlist_t
+append_to_strlist_try (strlist_t *list, const char *string)
+{
+  return do_append_to_strlist (list, string, strlen (string));
 }
 
 
 strlist_t
 append_to_strlist2( strlist_t *list, const char *string, int is_utf8 )
 {
   strlist_t sl;
 
   if( is_utf8 )
     sl = append_to_strlist( list, string );
   else
     {
       char *p = native_to_utf8 (string);
       sl = append_to_strlist( list, p );
       xfree( p );
     }
   return sl;
 }
 
 
+/* 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
 strlist_copy (strlist_t list)
 {
   strlist_t newlist = NULL, sl, *last;
 
   last = &newlist;
   for (; list; list = list->next)
     {
       sl = xmalloc (sizeof *sl + strlen (list->d));
       sl->flags = list->flags;
       strcpy(sl->d, list->d);
       sl->next = NULL;
       *last = sl;
       last = &sl;
     }
   return newlist;
 }
 
 
 
 strlist_t
 strlist_prev( strlist_t head, strlist_t node )
 {
     strlist_t n;
 
     for(n=NULL; head && head != node; head = head->next )
 	n = head;
     return n;
 }
 
 strlist_t
 strlist_last( strlist_t node )
 {
     if( node )
 	for( ; node->next ; node = node->next )
 	    ;
     return node;
 }
 
 
 /* Remove the first item from LIST and return its content in an
    allocated buffer.  This function terminates the process on memory
    shortage.  */
 char *
 strlist_pop (strlist_t *list)
 {
   char *str=NULL;
   strlist_t sl=*list;
 
   if(sl)
     {
       str = xmalloc(strlen(sl->d)+1);
       strcpy(str,sl->d);
 
       *list=sl->next;
       xfree(sl);
     }
 
   return str;
 }
 
 /* Return the first element of the string list HAYSTACK whose string
    matches NEEDLE.  If no elements match, return NULL.  */
 strlist_t
 strlist_find (strlist_t haystack, const char *needle)
 {
   for (;
        haystack;
        haystack = haystack->next)
     if (strcmp (haystack->d, needle) == 0)
       return haystack;
   return NULL;
 }
 
 int
 strlist_length (strlist_t list)
 {
   int i;
   for (i = 0; list; list = list->next)
     i ++;
 
   return i;
 }
 
 /* Reverse the list *LIST in place.  */
 strlist_t
 strlist_rev (strlist_t *list)
 {
   strlist_t l = *list;
   strlist_t lrev = NULL;
 
   while (l)
     {
       strlist_t tail = l->next;
       l->next = lrev;
       lrev = l;
       l = tail;
     }
 
   *list = lrev;
   return lrev;
 }
diff --git a/common/strlist.h b/common/strlist.h
index 641ea06cb..bf1ffa903 100644
--- a/common/strlist.h
+++ b/common/strlist.h
@@ -1,69 +1,72 @@
 /* strlist.h
  *	Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute and/or modify this
  * part of GnuPG under the terms of either
  *
  *   - the GNU Lesser General Public License as published by the Free
  *     Software Foundation; either version 3 of the License, or (at
  *     your option) any later version.
  *
  * or
  *
  *   - 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.
  *
  * or both in parallel, as here.
  *
  * GnuPG 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 copies of the GNU General Public License
  * and the GNU Lesser General Public License along with this program;
  * if not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifndef GNUPG_COMMON_STRLIST_H
 #define GNUPG_COMMON_STRLIST_H
 
 struct string_list
 {
   struct string_list *next;
   unsigned int flags;
   char d[1];
 };
 typedef struct string_list *strlist_t;
 
 void    free_strlist (strlist_t sl);
 void	free_strlist_wipe (strlist_t sl);
 
 strlist_t add_to_strlist (strlist_t *list, const char *string);
 strlist_t add_to_strlist_try (strlist_t *list, const char *string);
 
 strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8);
 
 strlist_t append_to_strlist (strlist_t *list, const char *string);
 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);
 strlist_t strlist_last (strlist_t node);
 char * strlist_pop (strlist_t *list);
 
 strlist_t strlist_find (strlist_t haystack, const char *needle);
 int strlist_length (strlist_t list);
 
 strlist_t strlist_rev (strlist_t *haystack);
 
 #define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0)
 
 
 #endif /*GNUPG_COMMON_STRLIST_H*/
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
@@ -1,84 +1,273 @@
 /* t-strlist.c - Regression tests for strist.c
  * Copyright (C) 2015  g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute and/or modify this
  * part of GnuPG under the terms of either
  *
  *   - the GNU Lesser General Public License as published by the Free
  *     Software Foundation; either version 3 of the License, or (at
  *     your option) any later version.
  *
  * or
  *
  *   - 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.
  *
  * or both in parallel, as here.
  *
  * GnuPG 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 copies of the GNU General Public License
  * and the GNU Lesser General Public License along with this program;
  * if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <string.h>
 
 #include "strlist.h"
 
 #include "t-support.h"
 
 static void
 test_strlist_rev (void)
 {
   strlist_t s = NULL;
 
   /* Reversing an empty list should yield the empty list.  */
   if (! (strlist_rev (&s) == NULL))
     fail (1);
 
   add_to_strlist (&s, "1");
   add_to_strlist (&s, "2");
   add_to_strlist (&s, "3");
 
   if (strcmp (s->d, "3") != 0)
     fail (2);
   if (strcmp (s->next->d, "2") != 0)
     fail (2);
   if (strcmp (s->next->next->d, "1") != 0)
     fail (2);
   if (s->next->next->next)
     fail (2);
 
   strlist_rev (&s);
 
   if (strcmp (s->d, "1") != 0)
     fail (2);
   if (strcmp (s->next->d, "2") != 0)
     fail (2);
   if (strcmp (s->next->next->d, "3") != 0)
     fail (2);
   if (s->next->next->next)
     fail (2);
 
   free_strlist (s);
 }
 
 
+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)
 {
   (void)argc;
   (void)argv;
 
   test_strlist_rev ();
+  test_tokenize_to_strlist ();
 
   return 0;
 }