2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2010-02-03 13:03:47 +01:00
|
|
|
|
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
|
|
|
|
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"
|
2019-07-10 13:54:50 +02:00
|
|
|
#include "memory-util.h"
|
2019-03-14 13:14:33 +01:00
|
|
|
#include "nulstr-util.h"
|
2019-03-13 12:14:47 +01:00
|
|
|
#include "sort-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2009-11-18 00:42:52 +01:00
|
|
|
#include "strv.h"
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
char *strv_find(char * const *l, const char *name) {
|
|
|
|
char * const *i;
|
2010-02-13 01:04:44 +01:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-30 15:07:45 +02:00
|
|
|
char *strv_find_case(char * const *l, const char *name) {
|
|
|
|
char * const *i;
|
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
if (strcaseeq(*i, name))
|
|
|
|
return *i;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
char *strv_find_prefix(char * const *l, const char *name) {
|
|
|
|
char * const *i;
|
2010-06-16 05:06:02 +02:00
|
|
|
|
|
|
|
assert(name);
|
|
|
|
|
|
|
|
STRV_FOREACH(i, l)
|
|
|
|
if (startswith(*i, name))
|
|
|
|
return *i;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
char *strv_find_startswith(char * const *l, const char *name) {
|
|
|
|
char * const *i, *e;
|
2014-08-21 16:22:34 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-21 10:07:34 +01:00
|
|
|
char **strv_free(char **l) {
|
2009-11-18 00:42:52 +01:00
|
|
|
char **k;
|
|
|
|
|
|
|
|
if (!l)
|
2020-01-21 10:07:34 +01:00
|
|
|
return NULL;
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
|
|
for (k = l; *k; k++)
|
|
|
|
free(*k);
|
|
|
|
|
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)
|
2019-07-10 13:54:50 +02:00
|
|
|
erase_and_freep(i);
|
2015-10-15 16:02:35 +02:00
|
|
|
|
2019-07-10 13:54:50 +02:00
|
|
|
return mfree(l);
|
2015-10-15 16:02:35 +02:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t strv_length(char * const *l) {
|
|
|
|
size_t n = 0;
|
2009-11-18 00:42:52 +01:00
|
|
|
|
|
|
|
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) {
|
2018-05-10 14:10:53 +02:00
|
|
|
_cleanup_strv_free_ char **a = NULL;
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t 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. */
|
|
|
|
|
2020-10-30 10:54:15 +01:00
|
|
|
va_copy(aq, ap);
|
|
|
|
for (const char *s = x; s; s = va_arg(aq, const char*)) {
|
|
|
|
if (s == STRV_IGNORE)
|
|
|
|
continue;
|
2012-05-23 03:43:29 +02:00
|
|
|
|
2020-10-30 10:54:15 +01:00
|
|
|
n++;
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
2020-10-30 10:54:15 +01: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;
|
|
|
|
|
2020-10-30 10:54:15 +01:00
|
|
|
for (const char *s = x; s; s = va_arg(ap, const char*)) {
|
|
|
|
if (s == STRV_IGNORE)
|
|
|
|
continue;
|
2012-05-23 03:43:29 +02:00
|
|
|
|
2020-10-30 10:54:15 +01:00
|
|
|
a[i] = strdup(s);
|
|
|
|
if (!a[i])
|
|
|
|
return NULL;
|
2009-11-18 00:42:52 +01:00
|
|
|
|
2020-10-30 10:54:15 +01:00
|
|
|
i++;
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
a[i] = NULL;
|
2010-04-10 17:43:12 +02:00
|
|
|
|
2018-05-10 14:10:53 +02:00
|
|
|
return TAKE_PTR(a);
|
2009-11-18 00:42:52 +01:00
|
|
|
}
|
2010-01-26 04:18:44 +01:00
|
|
|
|
2018-10-31 17:03:50 +01:00
|
|
|
char **strv_new_internal(const char *x, ...) {
|
2010-04-10 17:43:12 +02:00
|
|
|
char **r;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, x);
|
|
|
|
r = strv_new_ap(x, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
|
|
|
|
char * const *s, **t;
|
2015-10-07 11:26:10 +02:00
|
|
|
size_t p, q, i = 0, j;
|
|
|
|
|
|
|
|
assert(a);
|
|
|
|
|
|
|
|
if (strv_isempty(b))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
p = strv_length(*a);
|
|
|
|
q = strv_length(b);
|
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
if (p >= SIZE_MAX - q)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
t = reallocarray(*a, GREEDY_ALLOC_ROUND_UP(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
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
|
|
|
|
char * const *s;
|
2014-01-04 02:35:27 +01:00
|
|
|
int r;
|
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
|
|
|
|
2019-07-11 19:14:16 +02:00
|
|
|
v = strjoin(*s, suffix);
|
2014-01-04 02:35:27 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-02-27 18:50:41 +01:00
|
|
|
char **strv_split_newlines(const char *s) {
|
|
|
|
char **l;
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t n;
|
2013-02-27 18:50:41 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-08-03 17:52:01 +02:00
|
|
|
int strv_split_full(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
|
|
|
}
|
|
|
|
|
2020-07-07 18:12:48 +02:00
|
|
|
int strv_split_colon_pairs(char ***t, const char *s) {
|
|
|
|
_cleanup_strv_free_ char **l = NULL;
|
|
|
|
size_t n = 0, allocated = 0;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(t);
|
|
|
|
assert(s);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL, *second_or_empty = NULL;
|
|
|
|
|
|
|
|
r = extract_first_word(&s, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
const char *p = tuple;
|
|
|
|
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
|
|
|
|
&first, &second, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
/* Enforce that at most 2 colon-separated words are contained in each group */
|
|
|
|
if (!isempty(p))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
second_or_empty = strdup(strempty(second));
|
|
|
|
if (!second_or_empty)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(l, allocated, n + 3))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
l[n++] = TAKE_PTR(first);
|
|
|
|
l[n++] = TAKE_PTR(second_or_empty);
|
|
|
|
|
|
|
|
l[n] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!l) {
|
|
|
|
l = new0(char*, 1);
|
|
|
|
if (!l)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
*t = TAKE_PTR(l);
|
|
|
|
|
|
|
|
return (int) n;
|
|
|
|
}
|
|
|
|
|
2020-09-24 13:06:52 +02:00
|
|
|
char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
|
2019-12-16 07:51:04 +01:00
|
|
|
char * const *s;
|
2010-02-13 01:04:44 +01:00
|
|
|
char *r, *e;
|
2018-09-11 06:22:31 +02:00
|
|
|
size_t n, k, m;
|
2010-02-13 01:04:44 +01:00
|
|
|
|
|
|
|
if (!separator)
|
|
|
|
separator = " ";
|
|
|
|
|
|
|
|
k = strlen(separator);
|
2018-09-11 06:22:31 +02:00
|
|
|
m = strlen_ptr(prefix);
|
2010-02-13 01:04:44 +01:00
|
|
|
|
2020-09-24 13:06:52 +02:00
|
|
|
if (unescape_separators) /* If there separator is multi-char, we won't know how to escape it. */
|
|
|
|
assert(k == 1);
|
|
|
|
|
2010-02-13 01:04:44 +01:00
|
|
|
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;
|
2020-09-24 13:06:52 +02:00
|
|
|
|
|
|
|
bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
|
|
|
|
|
|
|
|
n += m + strlen(*s) * (1 + needs_escaping);
|
2010-02-13 01:04:44 +01:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2018-09-11 06:22:31 +02:00
|
|
|
if (prefix)
|
|
|
|
e = stpcpy(e, prefix);
|
|
|
|
|
2020-09-24 13:06:52 +02:00
|
|
|
bool needs_escaping = unescape_separators && strchr(*s, separator[0]);
|
|
|
|
|
|
|
|
if (needs_escaping)
|
|
|
|
for (size_t i = 0; (*s)[i]; i++) {
|
|
|
|
if ((*s)[i] == separator[0])
|
|
|
|
*(e++) = '\\';
|
|
|
|
*(e++) = (*s)[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
e = stpcpy(e, *s);
|
2010-02-13 01:04:44 +01:00
|
|
|
}
|
|
|
|
|
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;
|
2019-12-09 18:30:00 +01:00
|
|
|
size_t n;
|
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
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
/* Check for overflow */
|
|
|
|
if (n > SIZE_MAX-2)
|
2014-10-21 14:01:28 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + 2), 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;
|
2019-12-09 18:30:00 +01:00
|
|
|
size_t n;
|
2014-12-03 18:31:51 +01:00
|
|
|
|
|
|
|
if (!a && !b)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
n = strv_length(*l);
|
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
/* Check for overflow */
|
|
|
|
if (n > SIZE_MAX-3)
|
2014-12-03 18:31:51 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
/* increase and check for overflow */
|
|
|
|
c = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(n + !!a + !!b + 1), 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;
|
|
|
|
}
|
|
|
|
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
int strv_insert(char ***l, size_t position, char *value) {
|
2014-07-02 12:23:36 +02:00
|
|
|
char **c;
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t 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;
|
|
|
|
}
|
|
|
|
|
2020-10-21 18:14:37 +02:00
|
|
|
int strv_prepend(char ***l, const char *value) {
|
|
|
|
char *v;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
v = strdup(value);
|
|
|
|
if (!v)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return strv_consume_prepend(l, v);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
bool strv_is_uniq(char * const *l) {
|
|
|
|
char * const *i;
|
2014-12-19 01:31:59 +01:00
|
|
|
|
|
|
|
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;
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t c = 0, i = 0;
|
2011-02-23 01:12:07 +01:00
|
|
|
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)
|
2018-10-31 17:03:50 +01:00
|
|
|
return strv_new(NULL);
|
2013-02-11 23:48:36 +01:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
|
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;
|
2019-12-16 07:51:04 +01:00
|
|
|
char * const *i;
|
2015-10-07 11:26:10 +02:00
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
assert(ret);
|
|
|
|
assert(ret_size);
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
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);
|
2019-12-16 07:51:04 +01:00
|
|
|
*ret = m;
|
|
|
|
*ret_size = n - 1;
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
m = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
bool strv_overlap(char * const *a, char * const *b) {
|
|
|
|
char * const *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
|
|
|
|
2018-09-18 01:39:24 +02:00
|
|
|
static int str_compare(char * const *a, char * const *b) {
|
2012-10-19 04:52:25 +02:00
|
|
|
return strcmp(*a, *b);
|
|
|
|
}
|
|
|
|
|
|
|
|
char **strv_sort(char **l) {
|
2018-09-18 01:39:24 +02:00
|
|
|
typesafe_qsort(l, strv_length(l), str_compare);
|
2012-10-19 04:52:25 +02:00
|
|
|
return l;
|
|
|
|
}
|
2013-02-07 00:15:27 +01:00
|
|
|
|
2020-01-14 10:29:50 +01:00
|
|
|
int strv_compare(char * const *a, char * const *b) {
|
|
|
|
int r;
|
2015-10-07 11:26:10 +02:00
|
|
|
|
2020-01-14 10:29:50 +01:00
|
|
|
if (strv_isempty(a)) {
|
|
|
|
if (strv_isempty(b))
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2015-10-07 11:26:10 +02:00
|
|
|
|
|
|
|
if (strv_isempty(b))
|
2020-01-14 10:29:50 +01:00
|
|
|
return 1;
|
2014-11-27 16:08:46 +01:00
|
|
|
|
2020-01-14 10:29:50 +01:00
|
|
|
for ( ; *a || *b; ++a, ++b) {
|
|
|
|
r = strcmp_ptr(*a, *b);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
2014-11-27 16:08:46 +01:00
|
|
|
|
2020-01-14 10:29:50 +01:00
|
|
|
return 0;
|
2014-11-27 16:08:46 +01:00
|
|
|
}
|
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
void strv_print(char * const *l) {
|
|
|
|
char * const *s;
|
2013-02-07 00:15:27 +01:00
|
|
|
|
|
|
|
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) {
|
tree-wide: be more careful with the type of array sizes
Previously we were a bit sloppy with the index and size types of arrays,
we'd regularly use unsigned. While I don't think this ever resulted in
real issues I think we should be more careful there and follow a
stricter regime: unless there's a strong reason not to use size_t for
array sizes and indexes, size_t it should be. Any allocations we do
ultimately will use size_t anyway, and converting forth and back between
unsigned and size_t will always be a source of problems.
Note that on 32bit machines "unsigned" and "size_t" are equivalent, and
on 64bit machines our arrays shouldn't grow that large anyway, and if
they do we have a problem, however that kind of overly large allocation
we have protections for usually, but for overflows we do not have that
so much, hence let's add it.
So yeah, it's a story of the current code being already "good enough",
but I think some extra type hygiene is better.
This patch tries to be comprehensive, but it probably isn't and I missed
a few cases. But I guess we can cover that later as we notice it. Among
smaller fixes, this changes:
1. strv_length()' return type becomes size_t
2. the unit file changes array size becomes size_t
3. DNS answer and query array sizes become size_t
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=76745
2018-04-27 14:09:31 +02:00
|
|
|
size_t n, i;
|
2014-12-19 01:31:59 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-01-12 12:14:31 +01:00
|
|
|
bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) {
|
|
|
|
for (size_t i = 0; patterns && patterns[i]; i++)
|
|
|
|
if (fnmatch(patterns[i], s, flags) == 0) {
|
|
|
|
if (matched_pos)
|
|
|
|
*matched_pos = i;
|
2015-02-14 00:37:43 +01:00
|
|
|
return true;
|
2020-01-12 12:14:31 +01:00
|
|
|
}
|
2015-02-14 00:37:43 +01:00
|
|
|
|
|
|
|
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);
|
2019-12-09 18:30:00 +01:00
|
|
|
if (n >= SIZE_MAX - k)
|
|
|
|
return -ENOMEM;
|
2015-10-04 17:36:19 +02:00
|
|
|
|
2019-12-09 18:30:00 +01:00
|
|
|
nl = reallocarray(*l, GREEDY_ALLOC_ROUND_UP(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
|
|
|
|
2019-12-16 07:51:04 +01:00
|
|
|
int fputstrv(FILE *f, char * const *l, const char *separator, bool *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
|
|
|
bool b = false;
|
2019-12-16 07:51:04 +01:00
|
|
|
char * const *s;
|
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 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;
|
|
|
|
}
|
2019-03-28 17:28:48 +01:00
|
|
|
|
|
|
|
static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const char *value) {
|
|
|
|
char **l;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
l = hashmap_get(h, key);
|
|
|
|
if (l) {
|
|
|
|
/* A list for this key already exists, let's append to it if it is not listed yet */
|
|
|
|
if (strv_contains(l, value))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = strv_extend(&l, value);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
assert_se(hashmap_update(h, key, l) >= 0);
|
|
|
|
} else {
|
|
|
|
/* No list for this key exists yet, create one */
|
|
|
|
_cleanup_strv_free_ char **l2 = NULL;
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
|
|
|
t = strdup(key);
|
|
|
|
if (!t)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = strv_extend(&l2, value);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = hashmap_put(h, t, l2);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
TAKE_PTR(t);
|
|
|
|
TAKE_PTR(l2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-05-28 17:27:11 +02:00
|
|
|
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
|
2019-03-28 17:28:48 +01:00
|
|
|
int r;
|
|
|
|
|
2020-05-28 17:27:11 +02:00
|
|
|
r = _hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
|
2019-03-28 17:28:48 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return string_strv_hashmap_put_internal(*h, key, value);
|
|
|
|
}
|
|
|
|
|
2020-05-28 17:27:11 +02:00
|
|
|
int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS) {
|
2019-03-28 17:28:48 +01:00
|
|
|
int r;
|
|
|
|
|
2020-05-28 17:27:11 +02:00
|
|
|
r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops HASHMAP_DEBUG_PASS_ARGS);
|
2019-03-28 17:28:48 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return string_strv_hashmap_put_internal(PLAIN_HASHMAP(*h), key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_HASH_OPS_FULL(string_strv_hash_ops, char, string_hash_func, string_compare_func, free, char*, strv_free);
|