freeswitch/libs/sofia-sip/libsofia-sip-ua/su/su_strlst.c

714 lines
16 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2006 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library 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 library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@defgroup su_strlst String Lists
*
* Lists of strings.
*
* String lists using #su_home_t.
*
*/
/**@ingroup su_strlst
* @CFILE su_strlst.c
* @brief String lists.
*
* The string lists can be used to concatenate a large number of strings, or
* split a string to smaller pieces (e.g., lines).
*
* Example of concatenating a number of strings to @a s:
* @code
* su_strlst_t *l = su_strlist_create(home);
* su_strlst_append(l, "=============");
* su_slprintf(l, "a is: %d", a)
* su_slprintf(l, "b is: %d", b)
* su_slprintf(l, "c is: %d", c)
* su_strlst_append(l, "------------");
* su_slprintf(l, "total: %d", a + b + c));
* su_strlst_append(l, "=============");
* s = su_strlst_join(l, "\n");
* @endcode
*
* Another example, splitting a string into lines and counting the number of
* non-empty ones:
* @code
* usize_t i, n;
* su_strlst_t *l;
*
* l = su_strlst_split(NULL, buf, "\n");
*
* nonempty = 0;
*
* for (i = 0; i < su_strlst_len(l); i++) {
* n = strcspn(su_strlst_item(l, i), " \t");
* if (su_strlst_item(l, i)[n])
* nonempty++;
* }
*
* su_strlst_destroy(l);
* @endcode
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
*
* @date Created: Fri May 3 09:22:59 2002 ppessi
*/
#include "config.h"
#include "sofia-sip/su_config.h"
#include "sofia-sip/su_strlst.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <memory.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
#if defined(va_copy)
/* Xyzzy */
#elif defined(__va_copy)
#define va_copy(dst, src) __va_copy((dst), (src))
#else
#define va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list)))
#endif
enum { N = 8 };
struct su_strlst_s
{
su_home_t sl_home[1];
size_t sl_size;
size_t sl_len;
size_t sl_total;
char const **sl_list;
};
/** Create a list with initial values */
static
su_strlst_t *su_strlst_vcreate_with_by(su_home_t *home,
char const *value,
va_list va,
int deeply)
{
su_strlst_t *self;
size_t i, n, m;
size_t total, size;
m = 0, total = 0;
/* Count arguments and their length */
if (value) {
char const *s;
va_list va0;
va_copy(va0, va);
for (s = value; s; m++, s = va_arg(va0, char *))
total += strlen(s);
va_end(va0);
}
for (n = N; m > n; n *= 2)
;
size = sizeof(*self) + n * sizeof(*self->sl_list);
if (deeply)
size += total + m;
self = su_home_clone(home, size);
if (self) {
self->sl_size = n;
self->sl_list = (char const **)(self + 1);
self->sl_len = m;
self->sl_total = total;
if (deeply) {
char *s = (char *)(self->sl_list + self->sl_size);
char *end = s + total + m;
for (i = 0; i < m; i++) {
assert(s);
self->sl_list[i] = s;
s = memccpy(s, value, '\0', end - s);
value = va_arg(va, char const *);
}
}
else {
for (i = 0; i < m; i++) {
self->sl_list[i] = value;
value = va_arg(va, char const *);
}
}
}
return self;
}
/** Create an empty string list.
*
* The list is initially empty. The memory home for the list is cloned from
* @a home.
*
*/
su_strlst_t *su_strlst_create(su_home_t *home)
{
su_strlst_t *self;
self = su_home_clone(home, sizeof(*self) + N * sizeof(*self->sl_list));
if (self) {
self->sl_size = N;
self->sl_list = (char const **)(self + 1);
}
return self;
}
/** Create a string list with initial values.
*
* The list is initialized with strings in argument. The argument list is
* terminated with NULL. The memory home for the list is cloned from @a
* home.
*/
su_strlst_t *su_strlst_create_with(su_home_t *home, char const *value, ...)
{
va_list va;
su_strlst_t *l;
va_start(va, value);
l = su_strlst_vcreate_with_by(home, value, va, 0);
va_end(va);
return l;
}
/** Create a string list with initial values.
*
* The string list is initialized with strings from @a va_list @a va. The
* argument list is terminated with NULL. The memory home for the list is
* cloned from @a home.
*/
su_strlst_t *su_strlst_vcreate_with(su_home_t *home,
char const *value,
va_list va)
{
return su_strlst_vcreate_with_by(home, value, va, 0);
}
/** Create a string list with duplicatedd initial values.
*
* The list is initialized with copies of strings in argument list. The
* argument list is terminated with NULL. The memory home for the list is
* cloned from @a home.
*/
su_strlst_t *su_strlst_create_with_dup(su_home_t *home, char const *value, ...)
{
va_list va;
su_strlst_t *l;
va_start(va, value);
l = su_strlst_vcreate_with_by(home, value, va, 1);
va_end(va);
return l;
}
/** Create a string list with duplicates of initial values.
*
* The string list is initialized with copies of strings from @a va_list @a
* va. The argument list is terminated with NULL. The memory home for the
* list is cloned from @a home.
*/
su_strlst_t *su_strlst_vcreate_with_dup(su_home_t *home,
char const *value,
va_list va)
{
return su_strlst_vcreate_with_by(home, value, va, 1);
}
/** Copy a string list */
static
su_strlst_t *su_strlst_copy_by(su_home_t *home,
su_strlst_t const *orig,
int deeply)
{
su_strlst_t *self;
size_t N, i, size, deepsize = 0;
if (orig == NULL)
return NULL;
N = orig->sl_size;
if (deeply)
deepsize = orig->sl_len + orig->sl_total;
size = sizeof(*self) + N * sizeof(self->sl_list[0]) + deepsize;
self = su_home_clone(home, size);
if (self) {
self->sl_size = N;
self->sl_list = (char const **)(self + 1);
self->sl_len = N = orig->sl_len;
self->sl_total = orig->sl_total;
if (deeply) {
char *s = (char *)(self->sl_list + self->sl_size);
char *end = s + deepsize;
for (i = 0; i < N; i++) {
self->sl_list[i] = s;
s = memccpy(s, orig->sl_list[i], '\0', end - s);
assert(s);
}
}
else {
for (i = 0; i < N; i++) {
self->sl_list[i] = orig->sl_list[i];
}
}
}
return self;
}
/** Shallow copy a string list. */
su_strlst_t *su_strlst_copy(su_home_t *home, su_strlst_t const *orig)
{
return su_strlst_copy_by(home, orig, 0);
}
/** Deep copy a string list. */
su_strlst_t *su_strlst_dup(su_home_t *home, su_strlst_t const *orig)
{
return su_strlst_copy_by(home, orig, 1);
}
/** Destroy a string list.
*
* The function su_strlst_destroy() destroys a list of strings and frees all
* duplicated strings belonging to it.
*/
void su_strlst_destroy(su_strlst_t *self)
{
su_home_zap(self->sl_home);
}
/** Increase the list size for next item, if necessary. */
static int su_strlst_increase(su_strlst_t *self)
{
if (self->sl_size <= self->sl_len + 1) {
size_t size = 2 * self->sl_size * sizeof(self->sl_list[0]);
char const **list;
if (self->sl_list != (char const **)(self + 1)) {
list = su_realloc(self->sl_home, (void *)self->sl_list, size);
} else {
list = su_alloc(self->sl_home, size);
if (list)
memcpy(list, self->sl_list, self->sl_len * sizeof(*self->sl_list));
}
if (!list)
return 0;
self->sl_list = list;
self->sl_size = 2 * self->sl_size;
}
return 1;
}
/**Duplicate and append a string to list.
*
* @param self pointer to a string list object
* @param str string to be duplicated and appended
*
* @return
* Pointer to duplicated string, if successful, or NULL upon an error.
*/
char *su_strlst_dup_append(su_strlst_t *self, char const *str)
{
size_t len;
if (str == NULL)
str = "";
len = strlen(str);
if (self && su_strlst_increase(self)) {
char *retval = su_alloc(self->sl_home, len + 1);
if (retval) {
memcpy(retval, str, len);
retval[len] = 0;
self->sl_list[self->sl_len++] = retval;
self->sl_total += len;
}
return retval;
}
return NULL;
}
/**Append a string to list.
*
* The string is not copied, and it @b must not be modified while string
* list exists.
*
* @param self pointer to a string list object
* @param str string to be appended
*
* @return
* Pointer to string, if successful, or NULL upon an error.
*/
char const *su_strlst_append(su_strlst_t *self, char const *str)
{
if (str == NULL)
str = "";
if (self && su_strlst_increase(self)) {
self->sl_list[self->sl_len++] = str;
self->sl_total += strlen(str);
return str;
}
return NULL;
}
/**Append a formatted string to the list.
*
* Format a string according to a @a fmt like printf(). The resulting string
* is copied to a memory area freshly allocated from a the memory home of
* the list and appended to the string list.
*
* @param self pointer to a string list object
* @param fmt format string
* @param ... argument list (must match with the @a fmt format string)
*
* @return A pointer to a fresh copy of formatting result, or NULL upon an
* error.
*/
char const *su_slprintf(su_strlst_t *self, char const *fmt, ...)
{
char const *str;
va_list ap;
va_start(ap, fmt);
str = su_slvprintf(self, fmt, ap);
va_end(ap);
return str;
}
/**Append a formatted string to the list.
*
* Format a string according to a @a fmt like vprintf(). The resulting
* string is copied to a memory area freshly allocated from a the memory
* home of the list and appended to the string list.
*
* @param self pointer to a string list object
* @param fmt format string
* @param ap stdarg argument list (must match with the @a fmt format string)
*
* @return A pointer to a fresh copy of formatting result, or NULL upon an
* error.
*/
char const *su_slvprintf(su_strlst_t *self, char const *fmt, va_list ap)
{
char *str = NULL;
if (self && su_strlst_increase(self)) {
str = su_vsprintf(self->sl_home, fmt, ap);
if (str) {
self->sl_list[self->sl_len++] = str;
self->sl_total += strlen(str);
}
}
return str;
}
/**Returns a numbered item from the list of strings. The numbering starts from
* 0.
*
* @param self pointer to a string list object
* @param i string index
*
* @return
* Pointer to string, if item exists, or NULL if index is out of bounds or
* list does not exist.
*/
char const *su_strlst_item(su_strlst_t const *self, usize_t i)
{
if (self && i < self->sl_len)
return self->sl_list[i];
else
return NULL;
}
/**Sets a item to the list of strings.
*
* Note that the item numbering starts from 0.
*
* @param self pointer to a string list object
* @param i string index
* @param s string to be set as item @a i
*
* @return
* Pointer to string, if item exists, or NULL if index is out of bounds or
* list does not exist.
*/
char const *su_strlst_set_item(su_strlst_t *self, usize_t i, char const *s)
{
char const *old = NULL;
if (self == NULL)
return NULL;
else if (i == self->sl_len)
return (void)su_strlst_append(self, s), NULL;
else if (i > self->sl_len)
return NULL;
if (s == NULL)
s = "";
old = self->sl_list[i];
self->sl_list[i] = s;
return old;
}
/**Removes a numbered item from the list of strings. The numbering starts from
* 0. The caller is responsible of reclaiming memory used by the removed string.
*
* @param self pointer to a string list object
* @param i string index
*
* @return
* Pointer to string, if item exists, or NULL if index is out of bounds or
* list does not exist.
*/
SU_DLL char const *su_strlst_remove(su_strlst_t *self, usize_t i)
{
if (self && i < self->sl_len) {
char const *s = self->sl_list[i];
memmove(&self->sl_list[i], &self->sl_list[i + 1],
&self->sl_list[self->sl_len] - &self->sl_list[i]);
self->sl_len--;
return s;
}
else
return NULL;
}
/** Concatenate list of strings to one string.
*
* The function su_strlst_join() concatenates the list of strings. Between
* each string in list it uses @a sep. The separator is not inserted after
* the last string in list, but one can always append an empty string to the
* list.
*
* The string is allocated from the memory @a home. If @a home is NULL, the
* string is allocated using malloc().
*
* @param self pointer to a string list object
* @param home home pointer
* @param sep separator (may be NULL)
*
* @return
*
* The function su_strlst_join() returns a concatenation of the strings in
* list, or NULL upon an error.
*/
char *su_strlst_join(su_strlst_t *self, su_home_t *home, char const *sep)
{
if (!sep)
sep = "";
if (self && self->sl_len > 0) {
size_t seplen = strlen(sep);
size_t total = self->sl_total + seplen * (self->sl_len - 1);
char *retval;
retval = su_alloc(home, total + 1);
if (retval) {
char *s = retval;
size_t i = 0, len;
for (;;) {
len = strlen(self->sl_list[i]);
memcpy(s, self->sl_list[i], len), s += len;
if (++i >= self->sl_len)
break;
memcpy(s, sep, seplen), s += seplen;
}
*s = '\0';
assert(s == retval + total);
}
return retval;
}
return su_strdup(home, "");
}
su_inline
su_strlst_t *
su_strlst_split0(su_strlst_t *l, char *str, char const *sep)
{
size_t n = sep ? strlen(sep) : 0;
char *s;
if (n > 0) {
while ((s = strstr(str, sep))) {
*s = '\0';
if (!su_strlst_append(l, str))
return NULL;
str = s + n;
}
}
if (!su_strlst_append(l, str))
return NULL;
return l;
}
/**Split a string.
*
* Splits a string to substrings. It returns a string list object. The
* string to be split is not copied but instead modified in place. Use
* su_strlst_dup_split() if you do not want to modify @a str.
*
* @param home home pointer
* @param str string to be split
* @param sep separator between substrings
*
* @return
* Pointer to list of strings, if successful, or NULL upon an error.
*/
su_strlst_t *
su_strlst_split(su_home_t *home, char *str, char const *sep)
{
if (str) {
su_strlst_t *l = su_strlst_create(home);
if (!su_strlst_split0(l, str, sep))
su_strlst_destroy(l), l = NULL;
return l;
}
return NULL;
}
/**Duplicate and split a string.
*
* Duplicates a string and splits the result to substrings. It returns a
* string list object. The string to be splitted is not modified.
*
* @param home home pointer
* @param cstr string to be split
* @param sep separator between substrings
*
* @return
* Pointer to list of strings, if successful, or NULL upon an error.
*/
su_strlst_t *su_strlst_dup_split(su_home_t *home,
char const *cstr,
char const *sep)
{
if (cstr) {
su_strlst_t *l = su_strlst_create(home);
if (l) {
char *s = su_strdup(su_strlst_home(l), cstr);
if (s && !su_strlst_split0(l, s, sep))
su_strlst_destroy(l), l = NULL;
}
return l;
}
return NULL;
}
/** Get number of items in list.
*
* The function su_strlst_len() returns the number of items in the
* string list.
*
*/
usize_t su_strlst_len(su_strlst_t const *l)
{
return l ? l->sl_len : 0;
}
/**Get a string array from list.
*
* The function su_strlst_get_array() returns an array of strings. The
* length of the array is always one longer than the length of the string
* list, and the last string in the returned array is always NULL.
*
* @param self pointer to a string list object
*
* @return
* Pointer to array of strings, or NULL if error occurred.
*/
char const **su_strlst_get_array(su_strlst_t *self)
{
if (self) {
char const **retval;
size_t size = sizeof(retval[0]) * (self->sl_len + 1);
retval = su_alloc(self->sl_home, size);
if (retval) {
memcpy(retval, self->sl_list, sizeof(retval[0]) * self->sl_len);
retval[self->sl_len] = NULL;
return retval;
}
}
return NULL;
}
/**Free a string array.
*
* The function su_strlst_free_array() discards a string array allocated
* with su_strlst_get_array().
*
* @param self pointer to a string list object
* @param array string array to be freed
*
*/
void su_strlst_free_array(su_strlst_t *self, char const **array)
{
if (array)
su_free(self->sl_home, (void *)array);
}