| /* |
| * |
| * Copyright 2010 Lennart Poettering |
| * |
| * This 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. |
| * |
| * |
| * Copyright (C) 2015 Karel Zak <kzak@redhat.com> |
| * Modified the original version from systemd project for util-linux. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <stdbool.h> |
| #include <assert.h> |
| |
| #include "strutils.h" |
| #include "strv.h" |
| |
| void strv_clear(char **l) { |
| char **k; |
| |
| if (!l) |
| return; |
| |
| for (k = l; *k; k++) |
| free(*k); |
| |
| *l = NULL; |
| } |
| |
| char **strv_free(char **l) { |
| strv_clear(l); |
| free(l); |
| return NULL; |
| } |
| |
| char **strv_copy(char * const *l) { |
| char **r, **k; |
| |
| k = r = malloc(sizeof(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 strv_length(char * const *l) { |
| unsigned 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 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 = malloc(sizeof(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; |
| } |
| |
| int strv_extend_strv(char ***a, char **b) { |
| int r; |
| char **s; |
| |
| STRV_FOREACH(s, b) { |
| r = strv_extend(a, *s); |
| if (r < 0) |
| return r; |
| } |
| |
| return 0; |
| } |
| |
| int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { |
| int r; |
| char **s; |
| |
| STRV_FOREACH(s, b) { |
| char *v; |
| |
| v = strappend(*s, suffix); |
| if (!v) |
| return -ENOMEM; |
| |
| r = strv_push(a, v); |
| if (r < 0) { |
| free(v); |
| return r; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| #define _FOREACH_WORD(word, length, s, separator, quoted, state) \ |
| for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) |
| |
| #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ |
| _FOREACH_WORD(word, length, s, separator, false, state) |
| |
| |
| char **strv_split(const char *s, const char *separator) { |
| const char *word, *state; |
| size_t l; |
| unsigned n, i; |
| char **r; |
| |
| assert(s); |
| |
| n = 0; |
| FOREACH_WORD_SEPARATOR(word, l, s, separator, state) |
| n++; |
| |
| r = malloc(sizeof(char *) * (n + 1)); |
| if (!r) |
| return NULL; |
| |
| i = 0; |
| FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { |
| r[i] = strndup(word, l); |
| if (!r[i]) { |
| strv_free(r); |
| return NULL; |
| } |
| |
| i++; |
| } |
| |
| r[i] = NULL; |
| return r; |
| } |
| |
| 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 = malloc(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; |
| } |
| |
| int strv_push(char ***l, char *value) { |
| char **c; |
| unsigned n, m; |
| |
| if (!value) |
| return 0; |
| |
| n = strv_length(*l); |
| |
| /* Increase and check for overflow */ |
| m = n + 2; |
| if (m < n) |
| return -ENOMEM; |
| |
| c = realloc(*l, sizeof(char *) * m); |
| if (!c) |
| return -ENOMEM; |
| |
| c[n] = value; |
| c[n+1] = NULL; |
| |
| *l = c; |
| return 0; |
| } |
| |
| int strv_push_prepend(char ***l, char *value) { |
| char **c; |
| unsigned n, m, i; |
| |
| if (!value) |
| return 0; |
| |
| n = strv_length(*l); |
| |
| /* increase and check for overflow */ |
| m = n + 2; |
| if (m < n) |
| return -ENOMEM; |
| |
| c = malloc(sizeof(char *) * m); |
| if (!c) |
| return -ENOMEM; |
| |
| for (i = 0; i < n; i++) |
| c[i+1] = (*l)[i]; |
| |
| c[0] = value; |
| c[n+1] = NULL; |
| |
| free(*l); |
| *l = c; |
| |
| return 0; |
| } |
| |
| int strv_consume(char ***l, char *value) { |
| int r; |
| |
| r = strv_push(l, value); |
| if (r < 0) |
| free(value); |
| |
| return r; |
| } |
| |
| int strv_consume_prepend(char ***l, char *value) { |
| int r; |
| |
| r = strv_push_prepend(l, value); |
| if (r < 0) |
| free(value); |
| |
| return r; |
| } |
| |
| int strv_extend(char ***l, const char *value) { |
| char *v; |
| |
| if (!value) |
| return 0; |
| |
| v = strdup(value); |
| if (!v) |
| return -ENOMEM; |
| |
| return strv_consume(l, v); |
| } |
| |
| 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 (strcmp(*f, s) == 0) |
| free(*f); |
| else |
| *(t++) = *f; |
| |
| *t = NULL; |
| return l; |
| } |
| |
| int strv_extendf(char ***l, const char *format, ...) { |
| va_list ap; |
| char *x; |
| int r; |
| |
| va_start(ap, format); |
| r = vasprintf(&x, format, ap); |
| va_end(ap); |
| |
| if (r < 0) |
| return -ENOMEM; |
| |
| return strv_consume(l, x); |
| } |
| |
| int strv_extendv(char ***l, const char *format, va_list ap) { |
| char *x; |
| int r; |
| |
| r = vasprintf(&x, format, ap); |
| if (r < 0) |
| return -ENOMEM; |
| |
| return strv_consume(l, x); |
| } |
| |
| char **strv_reverse(char **l) { |
| unsigned n, i; |
| |
| n = strv_length(l); |
| if (n <= 1) |
| return l; |
| |
| for (i = 0; i < n / 2; i++) { |
| char *t; |
| |
| t = l[i]; |
| l[i] = l[n-1-i]; |
| l[n-1-i] = t; |
| } |
| |
| return l; |
| } |