diff options
Diffstat (limited to 'lib/util/util_strlist.c')
-rw-r--r-- | lib/util/util_strlist.c | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/lib/util/util_strlist.c b/lib/util/util_strlist.c new file mode 100644 index 0000000..b56e173 --- /dev/null +++ b/lib/util/util_strlist.c @@ -0,0 +1,560 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "debug.h" +#include "tsort.h" + +#include "util_strlist.h" + +#undef strcasecmp + +/** + * @file + * @brief String list manipulation + */ + +/** + build an empty (only NULL terminated) list of strings (for expansion with str_list_add() etc) +*/ +_PUBLIC_ char **str_list_make_empty(TALLOC_CTX *mem_ctx) +{ + char **ret = talloc_zero_array(mem_ctx, char *, 1); + return ret; +} + +/** + place the only element 'entry' into a new, NULL terminated string list +*/ +_PUBLIC_ char **str_list_make_single(TALLOC_CTX *mem_ctx, const char *entry) +{ + char **ret = NULL; + + ret = talloc_array(mem_ctx, char *, 2); + if (ret == NULL) { + return NULL; + } + + ret[0] = talloc_strdup(ret, entry); + if (!ret[0]) { + talloc_free(ret); + return NULL; + } + ret[1] = NULL; + + return ret; +} + +/** + build a null terminated list of strings from a input string and a + separator list. The separator list must contain characters less than + or equal to 0x2f for this to work correctly on multi-byte strings +*/ +_PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const char *sep) +{ + int num_elements = 0; + char **ret = NULL; + + if (sep == NULL) { + sep = LIST_SEP; + } + + ret = talloc_array(mem_ctx, char *, 1); + if (ret == NULL) { + return NULL; + } + + while (string && *string) { + size_t len = strcspn(string, sep); + char **ret2; + + if (len == 0) { + string += strspn(string, sep); + continue; + } + + ret2 = talloc_realloc(mem_ctx, ret, char *, + num_elements+2); + if (ret2 == NULL) { + talloc_free(ret); + return NULL; + } + ret = ret2; + + ret[num_elements] = talloc_strndup(ret, string, len); + if (ret[num_elements] == NULL) { + talloc_free(ret); + return NULL; + } + + num_elements++; + string += len; + } + + ret[num_elements] = NULL; + + return ret; +} + +/** + * build a null terminated list of strings from an argv-like input string + * Entries are separated by spaces and can be enclosed by quotes. + * Does NOT support escaping + */ +_PUBLIC_ char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep) +{ + int num_elements = 0; + char **ret = NULL; + + ret = talloc_array(mem_ctx, char *, 1); + if (ret == NULL) { + return NULL; + } + + if (sep == NULL) + sep = " \t\n\r"; + + while (string && *string) { + size_t len = strcspn(string, sep); + char *element; + char **ret2; + + if (len == 0) { + string += strspn(string, sep); + continue; + } + + if (*string == '\"') { + string++; + len = strcspn(string, "\""); + element = talloc_strndup(ret, string, len); + string += len + 1; + } else { + element = talloc_strndup(ret, string, len); + string += len; + } + + if (element == NULL) { + talloc_free(ret); + return NULL; + } + + ret2 = talloc_realloc(mem_ctx, ret, char *, num_elements+2); + if (ret2 == NULL) { + talloc_free(ret); + return NULL; + } + ret = ret2; + + ret[num_elements] = element; + + num_elements++; + } + + ret[num_elements] = NULL; + + return ret; + +} + +/** + * join a list back to one string + */ +_PUBLIC_ char *str_list_join(TALLOC_CTX *mem_ctx, const char **list, char separator) +{ + char *ret = NULL; + int i; + + if (list[0] == NULL) + return talloc_strdup(mem_ctx, ""); + + ret = talloc_strdup(mem_ctx, list[0]); + + for (i = 1; list[i]; i++) { + ret = talloc_asprintf_append_buffer(ret, "%c%s", separator, list[i]); + } + + return ret; +} + +/** join a list back to one (shell-like) string; entries + * separated by spaces, using quotes where necessary */ +_PUBLIC_ char *str_list_join_shell(TALLOC_CTX *mem_ctx, const char **list, char sep) +{ + char *ret = NULL; + int i; + + if (list[0] == NULL) + return talloc_strdup(mem_ctx, ""); + + if (strchr(list[0], ' ') || strlen(list[0]) == 0) + ret = talloc_asprintf(mem_ctx, "\"%s\"", list[0]); + else + ret = talloc_strdup(mem_ctx, list[0]); + + for (i = 1; list[i]; i++) { + if (strchr(list[i], ' ') || strlen(list[i]) == 0) + ret = talloc_asprintf_append_buffer(ret, "%c\"%s\"", sep, list[i]); + else + ret = talloc_asprintf_append_buffer(ret, "%c%s", sep, list[i]); + } + + return ret; +} + +/** + return the number of elements in a string list +*/ +_PUBLIC_ size_t str_list_length(const char * const *list) +{ + size_t ret; + for (ret=0;list && list[ret];ret++) /* noop */ ; + return ret; +} + + +/** + copy a string list +*/ +_PUBLIC_ char **str_list_copy(TALLOC_CTX *mem_ctx, const char **list) +{ + int i; + char **ret; + + if (list == NULL) + return NULL; + + ret = talloc_array(mem_ctx, char *, str_list_length(list)+1); + if (ret == NULL) + return NULL; + + for (i=0;list && list[i];i++) { + ret[i] = talloc_strdup(ret, list[i]); + if (ret[i] == NULL) { + talloc_free(ret); + return NULL; + } + } + ret[i] = NULL; + return ret; +} + +/** + Return true if all the elements of the list match exactly. + */ +_PUBLIC_ bool str_list_equal(const char * const *list1, + const char * const *list2) +{ + int i; + + if (list1 == NULL || list2 == NULL) { + return (list1 == list2); + } + + for (i=0;list1[i] && list2[i];i++) { + if (strcmp(list1[i], list2[i]) != 0) { + return false; + } + } + if (list1[i] || list2[i]) { + return false; + } + return true; +} + + +/** + add an entry to a string list +*/ +_PUBLIC_ const char **str_list_add(const char **list, const char *s) +{ + size_t len = str_list_length(list); + const char **ret; + + ret = talloc_realloc(NULL, list, const char *, len+2); + if (ret == NULL) return NULL; + + ret[len] = talloc_strdup(ret, s); + if (ret[len] == NULL) return NULL; + + ret[len+1] = NULL; + + return ret; +} + +/** + * @brief Extend a talloc'ed string list with a printf'ed string + * + * str_list_add_printf() does nothing if *plist is NULL and it sets + * *plist to NULL on failure. It is designed to avoid intermediate + * NULL checks: + * + * argv = str_list_make_empty(ctx); + * str_list_add_printf(&argv, "smbstatus"); + * str_list_add_printf(&argv, "--configfile=%s", config); + * if (argv == NULL) { + * goto nomem; + * } + * + * @param[in,out] plist The talloc'ed list to extend + * @param[in] fmt The format string + */ +void str_list_add_printf(char ***plist, const char *fmt, ...) +{ + char **list = *plist; + size_t len; + char **tmp = NULL; + va_list ap; + + if (list == NULL) { + return; + } + len = str_list_length((const char * const *)list); + + tmp = talloc_realloc(NULL, list, char *, len+2); + if (tmp == NULL) { + goto fail; + } + list = tmp; + list[len+1] = NULL; + + va_start(ap, fmt); + list[len] = talloc_vasprintf(list, fmt, ap); + va_end(ap); + + if (list[len] == NULL) { + goto fail; + } + *plist = list; + + return; +fail: + TALLOC_FREE(list); + *plist = NULL; +} + +/** + remove an entry from a string list +*/ +_PUBLIC_ void str_list_remove(const char **list, const char *s) +{ + int i; + + for (i=0;list[i];i++) { + if (strcmp(list[i], s) == 0) break; + } + if (!list[i]) return; + + for (;list[i];i++) { + list[i] = list[i+1]; + } +} + + +/** + return true if a string is in a list +*/ +_PUBLIC_ bool str_list_check(const char **list, const char *s) +{ + int i; + + for (i=0; list != NULL && list[i] != NULL; i++) { + if (strcmp(list[i], s) == 0) return true; + } + return false; +} + +/** + return true if a string is in a list, case insensitively +*/ +_PUBLIC_ bool str_list_check_ci(const char **list, const char *s) +{ + int i; + + for (i=0; list != NULL && list[i] != NULL; i++) { + if (strcasecmp(list[i], s) == 0) return true; + } + return false; +} + + +/** + append one list to another - expanding list1 +*/ +_PUBLIC_ const char **str_list_append(const char **list1, + const char * const *list2) +{ + size_t len1 = str_list_length(list1); + size_t len2 = str_list_length(list2); + const char **ret; + size_t i; + + ret = talloc_realloc(NULL, list1, const char *, len1+len2+1); + if (ret == NULL) return NULL; + + for (i=len1;i<len1+len2;i++) { + ret[i] = talloc_strdup(ret, list2[i-len1]); + if (ret[i] == NULL) { + return NULL; + } + } + ret[i] = NULL; + + return ret; +} + +static int list_cmp(const char **el1, const char **el2) +{ + return strcmp(*el1, *el2); +} + +/* + return a list that only contains the unique elements of a list, + removing any duplicates + */ +_PUBLIC_ const char **str_list_unique(const char **list) +{ + size_t len = str_list_length(list); + const char **list2; + size_t i, j; + if (len < 2) { + return list; + } + list2 = (const char **)talloc_memdup(list, list, + sizeof(list[0])*(len+1)); + TYPESAFE_QSORT(list2, len, list_cmp); + list[0] = list2[0]; + for (i=j=1;i<len;i++) { + if (strcmp(list2[i], list[j-1]) != 0) { + list[j] = list2[i]; + j++; + } + } + list[j] = NULL; + list = talloc_realloc(NULL, list, const char *, j + 1); + talloc_free(list2); + return list; +} + +/* + very useful when debugging complex list related code + */ +_PUBLIC_ void str_list_show(const char **list) +{ + int i; + DEBUG(0,("{ ")); + for (i=0;list && list[i];i++) { + DEBUG(0,("\"%s\", ", list[i])); + } + DEBUG(0,("}\n")); +} + + + +/** + append one list to another - expanding list1 + this assumes the elements of list2 are const pointers, so we can re-use them +*/ +_PUBLIC_ const char **str_list_append_const(const char **list1, + const char **list2) +{ + size_t len1 = str_list_length(list1); + size_t len2 = str_list_length(list2); + const char **ret; + size_t i; + + ret = talloc_realloc(NULL, list1, const char *, len1+len2+1); + if (ret == NULL) return NULL; + + for (i=len1;i<len1+len2;i++) { + ret[i] = list2[i-len1]; + } + ret[i] = NULL; + + return ret; +} + +/** + * Add a string to an array of strings. + * + * num should be a pointer to an integer that holds the current + * number of elements in strings. It will be updated by this function. + */ +_PUBLIC_ bool add_string_to_array(TALLOC_CTX *mem_ctx, + const char *str, const char ***strings, size_t *num) +{ + char *dup_str = talloc_strdup(mem_ctx, str); + + *strings = talloc_realloc(mem_ctx, + *strings, + const char *, ((*num)+1)); + + if ((*strings == NULL) || (dup_str == NULL)) { + *num = 0; + return false; + } + + (*strings)[*num] = dup_str; + *num += 1; + + return true; +} + +/** + add an entry to a string list + this assumes s will not change +*/ +_PUBLIC_ const char **str_list_add_const(const char **list, const char *s) +{ + size_t len = str_list_length(list); + const char **ret; + + ret = talloc_realloc(NULL, list, const char *, len+2); + if (ret == NULL) return NULL; + + ret[len] = s; + ret[len+1] = NULL; + + return ret; +} + +/** + copy a string list + this assumes list will not change +*/ +_PUBLIC_ const char **str_list_copy_const(TALLOC_CTX *mem_ctx, + const char **list) +{ + int i; + const char **ret; + + if (list == NULL) + return NULL; + + ret = talloc_array(mem_ctx, const char *, str_list_length(list)+1); + if (ret == NULL) + return NULL; + + for (i=0;list && list[i];i++) { + ret[i] = list[i]; + } + ret[i] = NULL; + return ret; +} |