diff options
Diffstat (limited to 'src/install/strv.c')
-rw-r--r-- | src/install/strv.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/src/install/strv.c b/src/install/strv.c new file mode 100644 index 0000000..02b0507 --- /dev/null +++ b/src/install/strv.c @@ -0,0 +1,616 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd 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. + + systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include "util.h" +#include "strv.h" + +char *strv_find(char **l, const char *name) +{ + char **i; + + assert(name); + + STRV_FOREACH(i, l) { + if (streq(*i, name)) + return *i; + } + + return NULL; +} + +char *strv_find_prefix(char **l, const char *name) +{ + char **i; + + assert(name); + + STRV_FOREACH(i, l) { + if (startswith(*i, name)) + return *i; + } + + return NULL; +} + +void strv_free(char **l) +{ + char **k; + + if (!l) + return; + + for (k = l; *k; k++) + free(*k); + + free(l); +} + +char **strv_copy(char *const *l) +{ + char **r, **k; + + k = r = new(char *, strv_length(l) + 1); + if (!r) + return NULL; + + if (l) + for (; *l; k++, l++) { + *k = strdup(*l); + if (!*k) { + strv_free(r); + return NULL; + } + } + + *k = NULL; + return r; +} + +unsigned int strv_length(char *const *l) +{ + unsigned int n = 0; + + if (!l) + return 0; + + for (; *l; l++) + n++; + + return n; +} + +char **strv_new_ap(const char *x, va_list ap) +{ + const char *s; + char **a; + unsigned int n = 0, i = 0; + va_list aq; + + /* As a special trick we ignore all listed strings that equal + * (const char*) -1. This is supposed to be used with the + * STRV_IFNOTNULL() macro to include possibly NULL strings in + * the string list. */ + + if (x) { + n = x == (const char *)-1 ? 0 : 1; + + va_copy(aq, ap); + while ((s = va_arg(aq, const char *))) { + if (s == (const char *)-1) + continue; + + n++; + } + + va_end(aq); + } + + a = new(char *, n + 1); + if (!a) + return NULL; + + if (x) { + if (x != (const char *)-1) { + a[i] = strdup(x); + if (!a[i]) + goto fail; + i++; + } + + while ((s = va_arg(ap, const char *))) { + + if (s == (const char *)-1) + continue; + + a[i] = strdup(s); + if (!a[i]) + goto fail; + + i++; + } + } + + a[i] = NULL; + + return a; + +fail: + strv_free(a); + return NULL; +} + +char **strv_new(const char *x, ...) +{ + char **r; + va_list ap; + + va_start(ap, x); + r = strv_new_ap(x, ap); + va_end(ap); + + return r; +} + +char **strv_merge(char **a, char **b) +{ + char **r, **k; + + if (!a) + return strv_copy(b); + + if (!b) + return strv_copy(a); + + r = new(char *, strv_length(a) + strv_length(b) + 1); + if (!r) + return NULL; + + for (k = r; *a; k++, a++) { + *k = strdup(*a); + if (!*k) + goto fail; + } + + for (; *b; k++, b++) { + *k = strdup(*b); + if (!*k) + goto fail; + } + + *k = NULL; + return r; + +fail: + strv_free(r); + return NULL; +} + +char **strv_merge_concat(char **a, char **b, const char *suffix) +{ + char **r, **k; + + /* Like strv_merge(), but appends suffix to all strings in b, before adding */ + + if (!b) + return strv_copy(a); + + r = new(char *, strv_length(a) + strv_length(b) + 1); + if (!r) + return NULL; + + k = r; + if (a) + for (; *a; k++, a++) { + *k = strdup(*a); + if (!*k) + goto fail; + } + + for (; *b; k++, b++) { + *k = strappend(*b, suffix); + if (!*k) + goto fail; + } + + *k = NULL; + return r; + +fail: + strv_free(r); + return NULL; + +} + +char **strv_split(const char *s, const char *separator) +{ + char *state; + char *w; + size_t l; + unsigned int n, i; + char **r; + + assert(s); + + n = 0; + FOREACH_WORD_SEPARATOR(w, l, s, separator, state) { + n++; + } + + r = new(char *, n + 1); + if (!r) + return NULL; + + i = 0; + FOREACH_WORD_SEPARATOR(w, l, s, separator, state) { + r[i] = strndup(w, l); + if (!r[i]) { + strv_free(r); + return NULL; + } + + i++; + } + + r[i] = NULL; + return r; +} + +char **strv_split_quoted(const char *s) +{ + char *state; + char *w; + size_t l; + unsigned int n, i; + char **r; + + assert(s); + + n = 0; + FOREACH_WORD_QUOTED(w, l, s, state) { + n++; + } + + r = new(char *, n + 1); + if (!r) + return NULL; + + i = 0; + FOREACH_WORD_QUOTED(w, l, s, state) { + r[i] = cunescape_length(w, l); + if (!r[i]) { + strv_free(r); + return NULL; + } + i++; + } + + r[i] = NULL; + return r; +} + +char **strv_split_newlines(const char *s) +{ + char **l; + unsigned int n; + + assert(s); + + /* Special version of strv_split() that splits on newlines and + * suppresses an empty string at the end */ + + l = strv_split(s, NEWLINE); + if (!l) + return NULL; + + n = strv_length(l); + if (n == 0) + return l; + + if (isempty(l[n - 1])) { + free(l[n - 1]); + l[n - 1] = NULL; + } + + return l; +} + +char *strv_join(char **l, const char *separator) +{ + char *r, *e; + char **s; + size_t n, k; + + if (!separator) + separator = " "; + + k = strlen(separator); + + n = 0; + STRV_FOREACH(s, l) { + if (n != 0) + n += k; + n += strlen(*s); + } + + r = new(char, n + 1); + if (!r) + return NULL; + + e = r; + STRV_FOREACH(s, l) { + if (e != r) + e = stpcpy(e, separator); + + e = stpcpy(e, *s); + } + + *e = 0; + + return r; +} + +char **strv_append(char **l, const char *s) +{ + char **r, **k; + + if (!l) + return strv_new(s, NULL); + + if (!s) + return strv_copy(l); + + r = new(char *, strv_length(l) + 2); + if (!r) + return NULL; + + for (k = r; *l; k++, l++) { + *k = strdup(*l); + if (!*k) + goto fail; + } + + k[0] = strdup(s); + if (!k[0]) + goto fail; + + k[1] = NULL; + return r; + +fail: + strv_free(r); + return NULL; +} + +int strv_push(char ***l, char *value) +{ + char **c; + unsigned int n; + + if (!value) + return 0; + + n = strv_length(*l); + c = realloc(*l, sizeof(char *) * (n + 2)); + if (!c) + return -ENOMEM; + + c[n] = value; + c[n + 1] = NULL; + + *l = c; + return 0; +} + +int strv_extend(char ***l, const char *value) +{ + char *v; + int r; + + if (!value) + return 0; + + v = strdup(value); + if (!v) + return -ENOMEM; + + r = strv_push(l, v); + if (r < 0) + free(v); + + return r; +} + +char **strv_uniq(char **l) +{ + char **i; + + /* Drops duplicate entries. The first identical string will be + * kept, the others dropped */ + + STRV_FOREACH(i, l) { + strv_remove(i + 1, *i); + } + return l; +} + +char **strv_remove(char **l, const char *s) +{ + char **f, **t; + + if (!l) + return NULL; + + assert(s); + + /* Drops every occurrence of s in the string list, edits + * in-place. */ + + for (f = t = l; *f; f++) { + + if (streq(*f, s)) { + free(*f); + continue; + } + + *(t++) = *f; + } + + *t = NULL; + return l; +} + +char **strv_remove_prefix(char **l, const char *s) +{ + char **f, **t; + + if (!l) + return NULL; + + assert(s); + + /* Drops every occurrence of a string prefixed with s in the + * string list, edits in-place. */ + + for (f = t = l; *f; f++) { + + if (startswith(*f, s)) { + free(*f); + continue; + } + + *(t++) = *f; + } + + *t = NULL; + return l; +} + +char **strv_parse_nulstr(const char *s, size_t l) +{ + const char *p; + unsigned int c = 0, i = 0; + char **v; + + assert(s || l == 0); + + if (l == 0) + return new0(char *, 1); + + for (p = s; p < s + l; p++) + if (*p == 0) + c++; + + if (s[l - 1] != 0) + c++; + + v = new0(char *, c + 1); + if (!v) + return NULL; + + p = s; + while (p < s + l) { + const char *e; + + e = memchr(p, 0, s + l - p); + + v[i] = strndup(p, e ? e - p : s + l - p); + if (!v[i]) { + strv_free(v); + return NULL; + } + + i++; + + if (!e) + break; + + p = e + 1; + } + + assert(i == c); + + return v; +} + +char **strv_split_nulstr(const char *s) +{ + const char *i; + char **r = NULL; + + NULSTR_FOREACH(i, s) { + if (strv_extend(&r, i) < 0) { + strv_free(r); + return NULL; + } + } + + if (!r) + return strv_new(NULL, NULL); + + return r; +} + +bool strv_overlap(char **a, char **b) +{ + char **i, **j; + + STRV_FOREACH(i, a) { + STRV_FOREACH(j, b) { + if (streq(*i, *j)) + return true; + } + } + + return false; +} + +static int str_compare(const void *_a, const void *_b) +{ + const char **a = (const char **)_a, **b = (const char **)_b; + + return strcmp(*a, *b); +} + +char **strv_sort(char **l) +{ + + if (strv_isempty(l)) + return l; + + qsort(l, strv_length(l), sizeof(char *), str_compare); + return l; +} + +void strv_print(char **l) +{ + char **s; + + if (!l) + return; + + STRV_FOREACH(s, l) { + puts(*s); + } +} |