2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2010-02-03 13:03:47 +01:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2010 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
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
|
2010-02-03 13:03:47 +01:00
|
|
|
(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
|
2012-04-12 00:20:58 +02:00
|
|
|
Lesser General Public License for more details.
|
2010-02-03 13:03:47 +01:00
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2010-02-03 13:03:47 +01:00
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <errno.h>
|
2015-11-30 21:43:37 +01:00
|
|
|
#include <fnmatch.h>
|
2009-11-18 00:42:52 +01:00
|
|
|
#include <stdarg.h>
|
2015-11-30 21:43:37 +01:00
|
|
|
#include <stdio.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <stdlib.h>
|
2009-11-18 00:42:52 +01:00
|
|
|
#include <string.h>
|
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
#include "escape.h"
|
2015-11-30 21:43:37 +01:00
|
|
|
#include "extract-word.h"
|
2016-01-25 22:42:36 +01:00
|
|
|
#include "fileio.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2009-11-18 00:42:52 +01:00
|
|
|
#include "strv.h"
|
2015-11-16 22:09:36 +01:00
|
|
|
#include "util.h"
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
|
|
char *strv_find(char **l, const char *name) {
|
2010-02-13 01:04:44 +01:00
|
|
|
char **i;
|
|
|
|
|
2009-11-18 00:42:52 +01:00
|
|
|
assert(name);
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
if (streq(*i, name))
|
|
|
|
return *i;
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-06-16 05:06:02 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-08-21 16:22:34 +02:00
|
|
|
char *strv_find_startswith(char **l, const char *name) {
|
|
|
|
char **i, *e;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
/* Like strv_find_prefix, but actually returns only the
|
|
|
|
* suffix, not the whole item */
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l) {
|
|
|
|
e = startswith(*i, name);
|
|
|
|
if (e)
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-17 01:08:56 +01:00
|
|
|
void strv_clear(char **l) {
|
2009-11-18 00:42:52 +01:00
|
|
|
char **k;
|
|
|
|
|
|
|
|
if (!l)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (k = l; *k; k++)
|
|
|
|
free(*k);
|
|
|
|
|
2014-12-17 01:08:56 +01:00
|
|
|
*l = NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-17 12:20:31 +01:00
|
|
|
char **strv_free(char **l) {
|
2014-12-17 01:08:56 +01:00
|
|
|
strv_clear(l);
|
2016-10-17 00:28:30 +02:00
|
|
|
return mfree(l);
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
|
|
|
|
2015-10-15 16:02:35 +02:00
|
|
|
char **strv_free_erase(char **l) {
|
|
|
|
char **i;
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
string_erase(*i);
|
|
|
|
|
|
|
|
return strv_free(l);
|
|
|
|
}
|
|
|
|
|
2013-03-25 02:30:32 +01:00
|
|
|
char **strv_copy(char * const *l) {
|
2009-11-18 00:42:52 +01:00
|
|
|
char **r, **k;
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
k = r = new(char*, strv_length(l) + 1);
|
|
|
|
if (!r)
|
2009-11-18 00:42:52 +01:00
|
|
|
return NULL;
|
|
|
|
|
2011-04-16 01:49:20 +02:00
|
|
|
if (l)
|
2012-10-30 18:29:45 +01:00
|
|
|
for (; *l; k++, l++) {
|
|
|
|
*k = strdup(*l);
|
|
|
|
if (!*k) {
|
|
|
|
strv_free(r);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
|
|
*k = NULL;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-25 02:30:32 +01:00
|
|
|
unsigned strv_length(char * const *l) {
|
2009-11-18 00:42:52 +01:00
|
|
|
unsigned n = 0;
|
|
|
|
|
|
|
|
if (!l)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (; *l; l++)
|
|
|
|
n++;
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2010-04-10 17:43:12 +02:00
|
|
|
char **strv_new_ap(const char *x, va_list ap) {
|
2009-11-18 00:42:52 +01:00
|
|
|
const char *s;
|
|
|
|
char **a;
|
|
|
|
unsigned n = 0, i = 0;
|
2010-04-10 17:43:12 +02:00
|
|
|
va_list aq;
|
|
|
|
|
2012-05-23 03:43:29 +02:00
|
|
|
/* As a special trick we ignore all listed strings that equal
|
2016-06-27 23:26:07 +02:00
|
|
|
* STRV_IGNORE. This is supposed to be used with the
|
2012-05-23 03:43:29 +02:00
|
|
|
* STRV_IFNOTNULL() macro to include possibly NULL strings in
|
|
|
|
* the string list. */
|
|
|
|
|
2009-11-18 00:42:52 +01:00
|
|
|
if (x) {
|
2016-06-27 23:26:07 +02:00
|
|
|
n = x == STRV_IGNORE ? 0 : 1;
|
2009-11-18 00:42:52 +01:00
|
|
|
|
2010-04-10 17:43:12 +02:00
|
|
|
va_copy(aq, ap);
|
2012-05-23 03:43:29 +02:00
|
|
|
while ((s = va_arg(aq, const char*))) {
|
2016-06-27 23:26:07 +02:00
|
|
|
if (s == STRV_IGNORE)
|
2012-05-23 03:43:29 +02:00
|
|
|
continue;
|
|
|
|
|
2009-11-18 00:42:52 +01:00
|
|
|
n++;
|
2012-05-23 03:43:29 +02:00
|
|
|
}
|
|
|
|
|
2010-04-10 17:43:12 +02:00
|
|
|
va_end(aq);
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
|
|
|
|
2012-05-23 03:43:29 +02:00
|
|
|
a = new(char*, n+1);
|
|
|
|
if (!a)
|
2009-11-18 00:42:52 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (x) {
|
2016-06-27 23:26:07 +02:00
|
|
|
if (x != STRV_IGNORE) {
|
2012-05-23 03:43:29 +02:00
|
|
|
a[i] = strdup(x);
|
|
|
|
if (!a[i])
|
|
|
|
goto fail;
|
|
|
|
i++;
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
while ((s = va_arg(ap, const char*))) {
|
2012-05-23 03:43:29 +02:00
|
|
|
|
2016-06-27 23:26:07 +02:00
|
|
|
if (s == STRV_IGNORE)
|
2012-05-23 03:43:29 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
a[i] = strdup(s);
|
|
|
|
if (!a[i])
|
2009-11-18 00:42:52 +01:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a[i] = NULL;
|
2010-04-10 17:43:12 +02:00
|
|
|
|
2009-11-18 00:42:52 +01:00
|
|
|
return a;
|
|
|
|
|
|
|
|
fail:
|
2012-10-30 18:29:45 +01:00
|
|
|
strv_free(a);
|
2009-11-18 00:42:52 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-01-26 04:18:44 +01:00
|
|
|
|
2010-04-10 17:43:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
int strv_extend_strv(char ***a, char **b, bool filter_duplicates) {
|
|
|
|
char **s, **t;
|
|
|
|
size_t p, q, i = 0, j;
|
|
|
|
|
|
|
|
assert(a);
|
|
|
|
|
|
|
|
if (strv_isempty(b))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p = strv_length(*a);
|
|
|
|
q = strv_length(b);
|
|
|
|
|
2018-02-27 19:09:22 +01:00
|
|
|
t = reallocarray(*a, p + q + 1, sizeof(char *));
|
2015-10-07 11:26:10 +02:00
|
|
|
if (!t)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
t[p] = NULL;
|
|
|
|
*a = t;
|
2012-05-23 03:43:29 +02:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
STRV_FOREACH(s, b) {
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
if (filter_duplicates && strv_contains(t, *s))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
t[p+i] = strdup(*s);
|
|
|
|
if (!t[p+i])
|
|
|
|
goto rollback;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
t[p+i] = NULL;
|
2012-05-23 03:43:29 +02:00
|
|
|
}
|
2010-01-26 04:18:44 +01:00
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
assert(i <= q);
|
|
|
|
|
|
|
|
return (int) i;
|
|
|
|
|
|
|
|
rollback:
|
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
free(t[p + j]);
|
|
|
|
|
|
|
|
t[p] = NULL;
|
|
|
|
return -ENOMEM;
|
2010-02-13 01:04:44 +01:00
|
|
|
}
|
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
|
|
|
|
int r;
|
|
|
|
char **s;
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
STRV_FOREACH(s, b) {
|
|
|
|
char *v;
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
v = strappend(*s, suffix);
|
|
|
|
if (!v)
|
|
|
|
return -ENOMEM;
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
r = strv_push(a, v);
|
|
|
|
if (r < 0) {
|
|
|
|
free(v);
|
|
|
|
return r;
|
2011-09-23 01:43:28 +02:00
|
|
|
}
|
|
|
|
}
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
return 0;
|
2010-02-13 01:04:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
char **strv_split(const char *s, const char *separator) {
|
2014-07-30 04:01:36 +02:00
|
|
|
const char *word, *state;
|
2010-02-13 01:04:44 +01:00
|
|
|
size_t l;
|
|
|
|
unsigned n, i;
|
|
|
|
char **r;
|
|
|
|
|
|
|
|
assert(s);
|
|
|
|
|
|
|
|
n = 0;
|
2014-07-30 04:01:36 +02:00
|
|
|
FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
|
2010-02-13 01:04:44 +01:00
|
|
|
n++;
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
r = new(char*, n+1);
|
|
|
|
if (!r)
|
2010-02-13 01:04:44 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
i = 0;
|
2014-07-30 04:01:36 +02:00
|
|
|
FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
|
|
|
|
r[i] = strndup(word, l);
|
2012-10-30 18:29:45 +01:00
|
|
|
if (!r[i]) {
|
2010-02-13 01:04:44 +01:00
|
|
|
strv_free(r);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
r[i] = NULL;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-02-27 18:50:41 +01:00
|
|
|
char **strv_split_newlines(const char *s) {
|
|
|
|
char **l;
|
|
|
|
unsigned 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;
|
|
|
|
|
2015-09-08 23:03:38 +02:00
|
|
|
if (isempty(l[n - 1]))
|
2015-09-08 18:43:11 +02:00
|
|
|
l[n - 1] = mfree(l[n - 1]);
|
2013-02-27 18:50:41 +01:00
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2015-06-19 17:24:34 +02:00
|
|
|
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
|
2014-11-10 23:44:34 +01:00
|
|
|
_cleanup_strv_free_ char **l = NULL;
|
2015-10-04 17:36:19 +02:00
|
|
|
size_t n = 0, allocated = 0;
|
2014-11-10 23:44:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(t);
|
|
|
|
assert(s);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *word = NULL;
|
|
|
|
|
2015-06-19 17:24:34 +02:00
|
|
|
r = extract_first_word(&s, &word, separators, flags);
|
2014-11-10 23:44:34 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2015-09-08 23:03:38 +02:00
|
|
|
if (r == 0)
|
2014-11-10 23:44:34 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(l, allocated, n + 2))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-03-22 16:53:26 +01:00
|
|
|
l[n++] = TAKE_PTR(word);
|
2014-11-10 23:44:34 +01:00
|
|
|
|
|
|
|
l[n] = NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-04 17:36:19 +02:00
|
|
|
if (!l) {
|
2014-11-10 23:44:34 +01:00
|
|
|
l = new0(char*, 1);
|
2015-10-04 17:36:19 +02:00
|
|
|
if (!l)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2014-11-10 23:44:34 +01:00
|
|
|
|
2018-03-22 16:53:26 +01:00
|
|
|
*t = TAKE_PTR(l);
|
2014-11-10 23:44:34 +01:00
|
|
|
|
2015-10-04 17:36:19 +02:00
|
|
|
return (int) n;
|
2014-11-10 23:44:34 +01:00
|
|
|
}
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
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) {
|
basic/strv: fix strv_join for first empty argument
Empty strings were ignored in strv_join, but only if they were at the beginning
of the string. Empty strings after at least one non-empty item were treated
normally.
Previously:
{"x"} → "x"
{"x", ""} → "x"
{"x", "", ""} → "x::"
{""} → ""
{"", ""} → ""
{"", "", ""} → ""
{"", "x"} → "x"
{"", "x", ""} → "x:"
Now:
{"x"} → "x"
{"x", ""} → "x"
{"x", "", ""} → "x::"
{""} → ""
{"", ""} → ":"
{"", "", ""} → "::"
{"", "x"} → ":x"
{"", "x", ""} → ":x:"
2016-02-12 05:24:14 +01:00
|
|
|
if (s != l)
|
2010-02-13 01:04:44 +01:00
|
|
|
n += k;
|
|
|
|
n += strlen(*s);
|
|
|
|
}
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
r = new(char, n+1);
|
|
|
|
if (!r)
|
2010-02-13 01:04:44 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
e = r;
|
|
|
|
STRV_FOREACH(s, l) {
|
basic/strv: fix strv_join for first empty argument
Empty strings were ignored in strv_join, but only if they were at the beginning
of the string. Empty strings after at least one non-empty item were treated
normally.
Previously:
{"x"} → "x"
{"x", ""} → "x"
{"x", "", ""} → "x::"
{""} → ""
{"", ""} → ""
{"", "", ""} → ""
{"", "x"} → "x"
{"", "x", ""} → "x:"
Now:
{"x"} → "x"
{"x", ""} → "x"
{"x", "", ""} → "x::"
{""} → ""
{"", ""} → ":"
{"", "", ""} → "::"
{"", "x"} → ":x"
{"", "x", ""} → ":x:"
2016-02-12 05:24:14 +01:00
|
|
|
if (s != l)
|
2010-02-13 01:04:44 +01:00
|
|
|
e = stpcpy(e, separator);
|
|
|
|
|
|
|
|
e = stpcpy(e, *s);
|
|
|
|
}
|
|
|
|
|
2010-02-14 22:38:07 +01:00
|
|
|
*e = 0;
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-22 17:44:15 +01:00
|
|
|
int strv_push(char ***l, char *value) {
|
2013-01-11 01:04:11 +01:00
|
|
|
char **c;
|
2014-10-21 14:01:28 +02:00
|
|
|
unsigned n, m;
|
2013-01-11 01:04:11 +01:00
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
|
2013-01-18 00:47:19 +01:00
|
|
|
n = strv_length(*l);
|
2014-10-21 14:01:28 +02:00
|
|
|
|
2014-12-03 18:31:51 +01:00
|
|
|
/* Increase and check for overflow */
|
2014-10-21 14:01:28 +02:00
|
|
|
m = n + 2;
|
|
|
|
if (m < n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-02-26 21:20:00 +01:00
|
|
|
c = reallocarray(*l, m, sizeof(char*));
|
2013-03-22 17:44:15 +01:00
|
|
|
if (!c)
|
2013-01-18 00:47:19 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-03-22 17:44:15 +01:00
|
|
|
c[n] = value;
|
2013-01-18 00:47:19 +01:00
|
|
|
c[n+1] = NULL;
|
|
|
|
|
2013-01-11 01:04:11 +01:00
|
|
|
*l = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-03 18:31:51 +01:00
|
|
|
int strv_push_pair(char ***l, char *a, char *b) {
|
|
|
|
char **c;
|
|
|
|
unsigned n, m;
|
|
|
|
|
|
|
|
if (!a && !b)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = strv_length(*l);
|
|
|
|
|
|
|
|
/* increase and check for overflow */
|
|
|
|
m = n + !!a + !!b + 1;
|
|
|
|
if (m < n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-02-26 21:20:00 +01:00
|
|
|
c = reallocarray(*l, m, sizeof(char*));
|
2014-12-03 18:31:51 +01:00
|
|
|
if (!c)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (a)
|
|
|
|
c[n++] = a;
|
|
|
|
if (b)
|
|
|
|
c[n++] = b;
|
|
|
|
c[n] = NULL;
|
|
|
|
|
|
|
|
*l = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:50:18 +01:00
|
|
|
int strv_insert(char ***l, unsigned position, char *value) {
|
2014-07-02 12:23:36 +02:00
|
|
|
char **c;
|
2014-10-21 14:01:28 +02:00
|
|
|
unsigned n, m, i;
|
2014-07-02 12:23:36 +02:00
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = strv_length(*l);
|
2018-02-01 12:50:18 +01:00
|
|
|
position = MIN(position, n);
|
2014-10-21 14:01:28 +02:00
|
|
|
|
|
|
|
/* increase and check for overflow */
|
|
|
|
m = n + 2;
|
|
|
|
if (m < n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
c = new(char*, m);
|
2014-07-02 12:23:36 +02:00
|
|
|
if (!c)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-02-01 12:50:18 +01:00
|
|
|
for (i = 0; i < position; i++)
|
|
|
|
c[i] = (*l)[i];
|
|
|
|
c[position] = value;
|
|
|
|
for (i = position; i < n; i++)
|
2014-07-02 12:23:36 +02:00
|
|
|
c[i+1] = (*l)[i];
|
|
|
|
|
|
|
|
c[n+1] = NULL;
|
|
|
|
|
|
|
|
free(*l);
|
|
|
|
*l = c;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-04 15:20:51 +01:00
|
|
|
int strv_consume(char ***l, char *value) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = strv_push(l, value);
|
|
|
|
if (r < 0)
|
|
|
|
free(value);
|
|
|
|
|
2014-07-02 12:23:36 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-12-03 18:31:51 +01:00
|
|
|
int strv_consume_pair(char ***l, char *a, char *b) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = strv_push_pair(l, a, b);
|
|
|
|
if (r < 0) {
|
|
|
|
free(a);
|
|
|
|
free(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-07-02 12:23:36 +02:00
|
|
|
int strv_consume_prepend(char ***l, char *value) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = strv_push_prepend(l, value);
|
|
|
|
if (r < 0)
|
|
|
|
free(value);
|
|
|
|
|
2014-03-04 15:20:51 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-22 17:44:15 +01:00
|
|
|
int strv_extend(char ***l, const char *value) {
|
|
|
|
char *v;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
v = strdup(value);
|
|
|
|
if (!v)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-03-04 15:20:51 +01:00
|
|
|
return strv_consume(l, v);
|
2013-03-22 17:44:15 +01:00
|
|
|
}
|
|
|
|
|
2016-04-07 15:43:59 +02:00
|
|
|
int strv_extend_front(char ***l, const char *value) {
|
|
|
|
size_t n, m;
|
|
|
|
char *v, **c;
|
|
|
|
|
|
|
|
assert(l);
|
|
|
|
|
|
|
|
/* Like strv_extend(), but prepends rather than appends the new entry */
|
|
|
|
|
2017-02-21 18:11:12 +01:00
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
|
2016-04-07 15:43:59 +02:00
|
|
|
n = strv_length(*l);
|
|
|
|
|
|
|
|
/* Increase and overflow check. */
|
|
|
|
m = n + 2;
|
|
|
|
if (m < n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-02-21 18:11:12 +01:00
|
|
|
v = strdup(value);
|
|
|
|
if (!v)
|
|
|
|
return -ENOMEM;
|
2016-04-07 15:43:59 +02:00
|
|
|
|
2018-02-26 21:20:00 +01:00
|
|
|
c = reallocarray(*l, m, sizeof(char*));
|
2016-04-07 15:43:59 +02:00
|
|
|
if (!c) {
|
|
|
|
free(v);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
memmove(c+1, c, n * sizeof(char*));
|
|
|
|
c[0] = v;
|
|
|
|
c[n+1] = NULL;
|
|
|
|
|
|
|
|
*l = c;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
char **strv_uniq(char **l) {
|
2010-01-27 22:38:21 +01:00
|
|
|
char **i;
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
/* Drops duplicate entries. The first identical string will be
|
|
|
|
* kept, the others dropped */
|
|
|
|
|
2010-01-27 22:38:21 +01:00
|
|
|
STRV_FOREACH(i, l)
|
2010-02-13 01:04:44 +01:00
|
|
|
strv_remove(i+1, *i);
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2014-12-19 01:31:59 +01:00
|
|
|
bool strv_is_uniq(char **l) {
|
|
|
|
char **i;
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
if (strv_find(i+1, *i))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
char **strv_remove(char **l, const char *s) {
|
|
|
|
char **f, **t;
|
|
|
|
|
|
|
|
if (!l)
|
|
|
|
return NULL;
|
|
|
|
|
2011-04-16 01:50:10 +02:00
|
|
|
assert(s);
|
|
|
|
|
|
|
|
/* Drops every occurrence of s in the string list, edits
|
|
|
|
* in-place. */
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
for (f = t = l; *f; f++)
|
|
|
|
if (streq(*f, s))
|
2012-04-11 12:56:51 +02:00
|
|
|
free(*f);
|
2014-01-04 02:35:27 +01:00
|
|
|
else
|
|
|
|
*(t++) = *f;
|
2012-04-11 12:56:51 +02:00
|
|
|
|
|
|
|
*t = NULL;
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2011-02-23 01:12:07 +01:00
|
|
|
char **strv_parse_nulstr(const char *s, size_t l) {
|
2016-07-17 21:25:01 +02:00
|
|
|
/* l is the length of the input data, which will be split at NULs into
|
|
|
|
* elements of the resulting strv. Hence, the number of items in the resulting strv
|
|
|
|
* will be equal to one plus the number of NUL bytes in the l bytes starting at s,
|
|
|
|
* unless s[l-1] is NUL, in which case the final empty string is not stored in
|
|
|
|
* the resulting strv, and length is equal to the number of NUL bytes.
|
|
|
|
*
|
|
|
|
* Note that contrary to a normal nulstr which cannot contain empty strings, because
|
|
|
|
* the input data is terminated by any two consequent NUL bytes, this parser accepts
|
|
|
|
* empty strings in s.
|
|
|
|
*/
|
|
|
|
|
2011-02-23 01:12:07 +01:00
|
|
|
const char *p;
|
|
|
|
unsigned c = 0, i = 0;
|
|
|
|
char **v;
|
|
|
|
|
|
|
|
assert(s || l <= 0);
|
|
|
|
|
|
|
|
if (l <= 0)
|
2013-11-30 04:14:10 +01:00
|
|
|
return new0(char*, 1);
|
2011-02-23 01:12:07 +01:00
|
|
|
|
|
|
|
for (p = s; p < s + l; p++)
|
|
|
|
if (*p == 0)
|
|
|
|
c++;
|
|
|
|
|
|
|
|
if (s[l-1] != 0)
|
|
|
|
c++;
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
v = new0(char*, c+1);
|
|
|
|
if (!v)
|
2011-02-23 01:12:07 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = s;
|
|
|
|
while (p < s + l) {
|
|
|
|
const char *e;
|
|
|
|
|
|
|
|
e = memchr(p, 0, s + l - p);
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
v[i] = strndup(p, e ? e - p : s + l - p);
|
|
|
|
if (!v[i]) {
|
2011-02-23 01:12:07 +01:00
|
|
|
strv_free(v);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-30 18:29:45 +01:00
|
|
|
i++;
|
|
|
|
|
2011-02-23 01:12:07 +01:00
|
|
|
if (!e)
|
|
|
|
break;
|
|
|
|
|
|
|
|
p = e + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(i == c);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
2011-08-23 00:37:35 +02:00
|
|
|
|
2013-02-11 23:48:36 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
int strv_make_nulstr(char **l, char **p, size_t *q) {
|
2016-07-17 21:25:01 +02:00
|
|
|
/* A valid nulstr with two NULs at the end will be created, but
|
|
|
|
* q will be the length without the two trailing NULs. Thus the output
|
|
|
|
* string is a valid nulstr and can be iterated over using NULSTR_FOREACH,
|
|
|
|
* and can also be parsed by strv_parse_nulstr as long as the length
|
|
|
|
* is provided separately.
|
|
|
|
*/
|
|
|
|
|
2015-10-07 11:26:10 +02:00
|
|
|
size_t n_allocated = 0, n = 0;
|
|
|
|
_cleanup_free_ char *m = NULL;
|
|
|
|
char **i;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
assert(q);
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l) {
|
|
|
|
size_t z;
|
|
|
|
|
|
|
|
z = strlen(*i);
|
|
|
|
|
2016-07-17 21:25:01 +02:00
|
|
|
if (!GREEDY_REALLOC(m, n_allocated, n + z + 2))
|
2015-10-07 11:26:10 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memcpy(m + n, *i, z + 1);
|
|
|
|
n += z + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m) {
|
|
|
|
m = new0(char, 1);
|
|
|
|
if (!m)
|
|
|
|
return -ENOMEM;
|
2016-07-17 21:25:01 +02:00
|
|
|
n = 1;
|
|
|
|
} else
|
|
|
|
/* make sure there is a second extra NUL at the end of resulting nulstr */
|
|
|
|
m[n] = '\0';
|
2015-10-07 11:26:10 +02:00
|
|
|
|
2016-07-17 21:25:01 +02:00
|
|
|
assert(n > 0);
|
2015-10-07 11:26:10 +02:00
|
|
|
*p = m;
|
2016-07-17 21:25:01 +02:00
|
|
|
*q = n - 1;
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-23 00:37:35 +02:00
|
|
|
bool strv_overlap(char **a, char **b) {
|
2014-01-04 02:35:27 +01:00
|
|
|
char **i;
|
2011-08-23 00:37:35 +02:00
|
|
|
|
2014-01-04 02:35:27 +01:00
|
|
|
STRV_FOREACH(i, a)
|
|
|
|
if (strv_contains(b, *i))
|
|
|
|
return true;
|
2011-08-23 00:37:35 +02:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2012-10-19 04:52:25 +02:00
|
|
|
|
|
|
|
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) {
|
2017-07-10 20:55:14 +02:00
|
|
|
qsort_safe(l, strv_length(l), sizeof(char*), str_compare);
|
2012-10-19 04:52:25 +02:00
|
|
|
return l;
|
|
|
|
}
|
2013-02-07 00:15:27 +01:00
|
|
|
|
2014-11-27 16:08:46 +01:00
|
|
|
bool strv_equal(char **a, char **b) {
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
if (strv_isempty(a))
|
|
|
|
return strv_isempty(b);
|
|
|
|
|
|
|
|
if (strv_isempty(b))
|
|
|
|
return false;
|
2014-11-27 16:08:46 +01:00
|
|
|
|
|
|
|
for ( ; *a || *b; ++a, ++b)
|
|
|
|
if (!streq_ptr(*a, *b))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-07 00:15:27 +01:00
|
|
|
void strv_print(char **l) {
|
|
|
|
char **s;
|
|
|
|
|
|
|
|
STRV_FOREACH(s, l)
|
|
|
|
puts(*s);
|
|
|
|
}
|
2014-03-05 18:57:21 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-12-19 01:31:59 +01:00
|
|
|
|
|
|
|
char **strv_reverse(char **l) {
|
|
|
|
unsigned n, i;
|
|
|
|
|
|
|
|
n = strv_length(l);
|
|
|
|
if (n <= 1)
|
|
|
|
return l;
|
|
|
|
|
2016-06-28 21:12:01 +02:00
|
|
|
for (i = 0; i < n / 2; i++)
|
2016-06-26 12:37:00 +02:00
|
|
|
SWAP_TWO(l[i], l[n-1-i]);
|
2014-12-19 01:31:59 +01:00
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
2015-02-14 00:37:43 +01:00
|
|
|
|
2015-06-23 12:57:41 +02:00
|
|
|
char **strv_shell_escape(char **l, const char *bad) {
|
|
|
|
char **s;
|
|
|
|
|
|
|
|
/* Escapes every character in every string in l that is in bad,
|
|
|
|
* edits in-place, does not roll-back on error. */
|
|
|
|
|
|
|
|
STRV_FOREACH(s, l) {
|
|
|
|
char *v;
|
|
|
|
|
|
|
|
v = shell_escape(*s, bad);
|
|
|
|
if (!v)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
free(*s);
|
|
|
|
*s = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2015-02-16 20:04:36 +01:00
|
|
|
bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
|
2015-02-14 00:37:43 +01:00
|
|
|
char* const* p;
|
|
|
|
|
|
|
|
STRV_FOREACH(p, patterns)
|
2016-06-27 14:47:37 +02:00
|
|
|
if (fnmatch(*p, s, flags) == 0)
|
2015-02-14 00:37:43 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-23 01:01:26 +02:00
|
|
|
|
|
|
|
char ***strv_free_free(char ***l) {
|
|
|
|
char ***i;
|
|
|
|
|
|
|
|
if (!l)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = l; *i; i++)
|
|
|
|
strv_free(*i);
|
|
|
|
|
2016-10-17 00:28:30 +02:00
|
|
|
return mfree(l);
|
2015-09-23 01:01:26 +02:00
|
|
|
}
|
2015-09-24 12:23:21 +02:00
|
|
|
|
|
|
|
char **strv_skip(char **l, size_t n) {
|
|
|
|
|
|
|
|
while (n > 0) {
|
|
|
|
if (strv_isempty(l))
|
|
|
|
return l;
|
|
|
|
|
|
|
|
l++, n--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return l;
|
|
|
|
}
|
2015-10-04 17:36:19 +02:00
|
|
|
|
|
|
|
int strv_extend_n(char ***l, const char *value, size_t n) {
|
|
|
|
size_t i, j, k;
|
|
|
|
char **nl;
|
|
|
|
|
|
|
|
assert(l);
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
if (n == 0)
|
|
|
|
return 0;
|
|
|
|
|
2016-07-10 14:48:23 +02:00
|
|
|
/* Adds the value n times to l */
|
2015-10-04 17:36:19 +02:00
|
|
|
|
|
|
|
k = strv_length(*l);
|
|
|
|
|
2018-02-27 19:09:22 +01:00
|
|
|
nl = reallocarray(*l, k + n + 1, sizeof(char *));
|
2015-10-04 17:36:19 +02:00
|
|
|
if (!nl)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*l = nl;
|
|
|
|
|
|
|
|
for (i = k; i < k + n; i++) {
|
|
|
|
nl[i] = strdup(value);
|
|
|
|
if (!nl[i])
|
|
|
|
goto rollback;
|
|
|
|
}
|
|
|
|
|
|
|
|
nl[i] = NULL;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rollback:
|
2015-10-07 10:52:10 +02:00
|
|
|
for (j = k; j < i; j++)
|
2015-10-04 17:36:19 +02:00
|
|
|
free(nl[j]);
|
|
|
|
|
|
|
|
nl[k] = NULL;
|
2015-10-06 12:32:50 +02:00
|
|
|
return -ENOMEM;
|
2015-10-04 17:36:19 +02:00
|
|
|
}
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
|
|
|
|
int fputstrv(FILE *f, char **l, const char *separator, bool *space) {
|
|
|
|
bool b = false;
|
|
|
|
char **s;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* Like fputs(), but for strv, and with a less stupid argument order */
|
|
|
|
|
|
|
|
if (!space)
|
|
|
|
space = &b;
|
|
|
|
|
|
|
|
STRV_FOREACH(s, l) {
|
2016-01-25 22:42:36 +01:00
|
|
|
r = fputs_with_space(f, *s, separator, space);
|
networkd: rework Domains= setting
Previously, .network files only knew a vaguely defined "Domains=" concept, for which the documentation declared it was
the "DNS domain" for the network connection, without specifying what that means.
With this the Domains setting is reworked, so that there are now "routing" domains and "search" domains. The former are
to be used by resolved to route DNS request to specific network interfaces, the latter is to be used for searching
single-label hostnames with (in addition to being used for routing). Both settings are configured in the "Domains="
setting. Normal domain names listed in it are now considered search domains (for compatibility with existing setups),
while those prefixed with "~" are considered routing domains only. To route all lookups to a specific interface the
routing domain "." may be used, referring to the root domain. An alternative syntax for this is the "*", as was already
implemented before using the "wildcard" domain concept.
This commit adds proper parsers for this new logic, and exposes this via the sd-network API. This information is not
used by resolved yet, this will be added in a later commit.
2016-01-25 19:46:00 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|