X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=strv.c;h=a749096f9a35c193ea2d16c36091aac5a26ce33f;hp=25ea92c112c26e6170436e7b08dc71857dbc64f7;hb=d4e6a6f66b7b3c797a1446816b8512d82c92278f;hpb=100a76ee327132db201beda0fa432af79a3ff9fd diff --git a/strv.c b/strv.c index 25ea92c11..a749096f9 100644 --- a/strv.c +++ b/strv.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "util.h" #include "strv.h" @@ -84,21 +85,20 @@ unsigned strv_length(char **l) { return n; } -char **strv_new(const char *x, ...) { +char **strv_new_ap(const char *x, va_list ap) { const char *s; char **a; unsigned n = 0, i = 0; - va_list ap; + va_list aq; + if (x) { n = 1; - va_start(ap, x); - - while (va_arg(ap, const char*)) + va_copy(aq, ap); + while (va_arg(aq, const char*)) n++; - - va_end(ap); + va_end(aq); } if (!(a = new(char*, n+1))) @@ -112,19 +112,16 @@ char **strv_new(const char *x, ...) { i++; - va_start(ap, x); - while ((s = va_arg(ap, const char*))) { if (!(a[i] = strdup(s))) goto fail; i++; } - - va_end(ap); } a[i] = NULL; + return a; fail: @@ -134,9 +131,21 @@ fail: free(a[i-1]); 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; @@ -281,6 +290,8 @@ char *strv_join(char **l, const char *separator) { e = stpcpy(e, *s); } + *e = 0; + return r; } @@ -299,6 +310,7 @@ char **strv_append(char **l, const char *s) { for (k = r; *l; k++, l++) if (!(*k = strdup(*l))) goto fail; + if (!(*(k++) = strdup(s))) goto fail; @@ -347,3 +359,145 @@ char **strv_remove(char **l, const char *s) { *t = NULL; return l; } + +static int env_append(char **r, char ***k, char **a) { + assert(r); + assert(k); + assert(a); + + /* Add the entries of a to *k unless they already exist in *r + * in which case they are overriden instead. This assumes + * there is enough space in the r */ + + for (; *a; a++) { + char **j; + size_t n = strcspn(*a, "=") + 1; + + for (j = r; j < *k; j++) + if (strncmp(*j, *a, n) == 0) + break; + + if (j >= *k) + (*k)++; + else + free(*j); + + if (!(*j = strdup(*a))) + return -ENOMEM; + } + + return 0; +} + +char **strv_env_merge(char **x, ...) { + size_t n = 0; + char **l, **k, **r; + va_list ap; + + /* Merges an arbitrary number of environment sets */ + + if (x) { + n += strv_length(x); + + va_start(ap, x); + while ((l = va_arg(ap, char**))) + n += strv_length(l); + va_end(ap); + } + + + if (!(r = new(char*, n+1))) + return NULL; + + k = r; + + if (x) { + if (env_append(r, &k, x) < 0) + goto fail; + + va_start(ap, x); + while ((l = va_arg(ap, char**))) + if (env_append(r, &k, l) < 0) + goto fail; + va_end(ap); + } + + *k = NULL; + + return r; + +fail: + for (k--; k >= r; k--) + free(*k); + + free(r); + + return NULL; +} + +static bool env_match(const char *t, const char *pattern) { + assert(t); + assert(pattern); + + /* pattern a matches string a + * a matches a= + * a matches a=b + * a= matches a= + * a=b matches a=b + * a= does not match a + * a=b does not match a= + * a=b does not match a + * a=b does not match a=c */ + + if (streq(t, pattern)) + return true; + + if (!strchr(pattern, '=')) { + size_t l = strlen(pattern); + + return strncmp(t, pattern, l) == 0 && t[l] == '='; + } + + return false; +} + +char **strv_env_delete(char **x, ...) { + size_t n = 0, i = 0; + char **l, **k, **r, **j; + va_list ap; + + /* Deletes every entry fromx that is mentioned in the other + * string lists */ + + n = strv_length(x); + + if (!(r = new(char*, n+1))) + return NULL; + + STRV_FOREACH(k, x) { + va_start(ap, x); + + while ((l = va_arg(ap, char**))) + STRV_FOREACH(j, l) + if (env_match(*k, *j)) + goto delete; + + va_end(ap); + + if (!(r[i++] = strdup(*k))) { + strv_free(r); + return NULL; + } + + continue; + + delete: + va_end(ap); + } + + r[i] = NULL; + + assert(i <= n); + + return r; +}