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; }