+char **strv_split(const char *s, const char *separator) {
+ char *state;
+ char *w;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+ n++;
+
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+ if (!(r[i++] = strndup(w, l))) {
+ strv_free(r);
+ return NULL;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char **strv_split_quoted(const char *s) {
+ char *state;
+ char *w;
+ size_t l;
+ unsigned n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state)
+ n++;
+
+ if (!(r = new(char*, n+1)))
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state)
+ if (!(r[i++] = strndup(w, l))) {
+ strv_free(r);
+ return NULL;
+ }
+
+ 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);
+ }
+
+ if (!(r = new(char, n+1)))
+ 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);
+
+ if (!(r = new(char*, strv_length(l)+2)))
+ return NULL;
+
+ for (k = r; *l; k++, l++)
+ if (!(*k = strdup(*l)))
+ goto fail;
+
+ if (!(*(k++) = strdup(s)))
+ goto fail;
+
+ *k = NULL;
+ return r;
+
+fail:
+ for (k--; k >= r; k--)
+ free(*k);
+
+ free(r);
+
+ return NULL;
+}
+
+char **strv_uniq(char **l) {