/* strlist.c - Argument Parser for option handling
* Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc.
* Copyright (C) 2015, 2024, 2025 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This file 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 Lesser General Public License
* along with this program; if not, see .
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This file was originally a part of GnuPG.
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include "gpgrt-int.h"
#define SL_PRIV_FLAG_WIPE 0x01
void
_gpgrt_strlist_free (gpgrt_strlist_t sl)
{
gpgrt_strlist_t sl2;
for (; sl; sl = sl2)
{
sl2 = sl->next;
if ((sl->_private_flags & ~SL_PRIV_FLAG_WIPE))
_gpgrt_log_fatal ("gpgrt_strlist_free: corrupted object %p\n", sl);
if ((sl->_private_flags & SL_PRIV_FLAG_WIPE))
_gpgrt_wipememory (sl, sizeof *sl + strlen (sl->d));
xfree(sl);
}
}
/* Core of gpgrt_strlist_append 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 gpgrt_strlist_t
do_strlist_append (gpgrt_strlist_t *list, const char *string, size_t stringlen,
unsigned int flags)
{
gpgrt_strlist_t r, sl;
sl = xtrymalloc (sizeof *sl + stringlen);
if (!sl)
return NULL;
sl->flags = 0;
sl->_private_flags = (flags & GPGRT_STRLIST_WIPE)? SL_PRIV_FLAG_WIPE : 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. This function returns NULL and sets ERRNO
* on memory shortage. If STRING is NULL an empty string is stored
* instead. FLAGS are these bits:
* GPGRT_STRLIST_APPEND - Append to the list; default is to prepend
* GPGRT_STRLIST_WIPE - Set a marker to wipe the string on free.
*/
gpgrt_strlist_t
_gpgrt_strlist_add (gpgrt_strlist_t *list, const char *string,
unsigned int flags)
{
gpgrt_strlist_t sl;
if (!string)
string = "";
if ((flags & GPGRT_STRLIST_APPEND))
return do_strlist_append (list, string, strlen (string), flags);
/* Default is to prepend. */
sl = xtrymalloc (sizeof *sl + strlen (string));
if (sl)
{
sl->flags = 0;
sl->_private_flags = (flags & GPGRT_STRLIST_WIPE)? SL_PRIV_FLAG_WIPE : 0;
strcpy (sl->d, string);
sl->next = *list;
*list = sl;
}
return sl;
}
/* Tokenize STRING using the delimiters from DELIM and append each
* token to the string list LIST. On success a pointer 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. Only GPGRT_STRLIST_WIPE has an effect here. */
gpgrt_strlist_t
_gpgrt_strlist_tokenize (gpgrt_strlist_t *list, const char *string,
const char *delim, unsigned int flags)
{
const char *s, *se;
size_t n;
gpgrt_strlist_t newlist = NULL;
gpgrt_strlist_t tail;
if (!string)
string = "";
s = string;
do
{
se = strpbrk (s, delim);
if (se)
n = se - s;
else
n = strlen (s);
if (!n)
continue; /* Skip empty string. */
tail = do_strlist_append (&newlist, s, n, flags);
if (!tail)
{
_gpgrt_strlist_free (newlist);
return NULL;
}
_gpgrt_trim_spaces (tail->d);
if (!*tail->d) /* Remove new but empty item from the list. */
{
tail = _gpgrt_strlist_prev (newlist, tail);
if (tail)
{
_gpgrt_strlist_free (tail->next);
tail->next = NULL;
}
else if (newlist)
{
_gpgrt_strlist_free (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. On error set ERRNO and return NULL. */
gpgrt_strlist_t
_gpgrt_strlist_copy (gpgrt_strlist_t list)
{
gpgrt_strlist_t newlist = NULL;
gpgrt_strlist_t sl, *last;
last = &newlist;
for (; list; list = list->next)
{
sl = xtrymalloc (sizeof *sl + strlen (list->d));
if (!sl)
{
_gpgrt_strlist_free (newlist);
return NULL;
}
sl->flags = list->flags;
sl->_private_flags = list->_private_flags;
strcpy (sl->d, list->d);
sl->next = NULL;
*last = sl;
last = &sl;
}
return newlist;
}
/* Reverse the list *LIST in place. */
gpgrt_strlist_t
_gpgrt_strlist_rev (gpgrt_strlist_t *list)
{
gpgrt_strlist_t l = *list;
gpgrt_strlist_t lrev = NULL;
while (l)
{
gpgrt_strlist_t tail = l->next;
l->next = lrev;
lrev = l;
l = tail;
}
*list = lrev;
return lrev;
}
gpgrt_strlist_t
_gpgrt_strlist_prev (gpgrt_strlist_t head, gpgrt_strlist_t node)
{
gpgrt_strlist_t n = NULL;
for (; head && head != node; head = head->next )
n = head;
return n;
}
gpgrt_strlist_t
_gpgrt_strlist_last (gpgrt_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 returns NULl and sets ERRNO on
* error. */
char *
_gpgrt_strlist_pop (gpgrt_strlist_t *list)
{
char *str = NULL;
gpgrt_strlist_t sl = *list;
if (sl)
{
str = xtrystrdup (sl->d);
if (!str)
return NULL;
*list = sl->next;
sl->next = NULL;
xfree (sl);
}
return str;
}
/* Return the first element of the string list HAYSTACK whose string
matches NEEDLE. If no elements match, return NULL. */
gpgrt_strlist_t
_gpgrt_strlist_find (gpgrt_strlist_t haystack, const char *needle)
{
for (; haystack; haystack = haystack->next)
if (!strcmp (haystack->d, needle))
return haystack;
return NULL;
}