forked from Mirrors/freeswitch
d8c4d22d40
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10802 d0543943-73ff-0310-b7d9-9358b9ac24b2
714 lines
16 KiB
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);
|
|
}
|