/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi * * 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 * * @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 #include #include #include #include #include #include #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); }