Merge pull request #25360 from poettering/strv-fixes

nulstr fixes
This commit is contained in:
Yu Watanabe
2022-11-13 20:17:10 +09:00
committed by GitHub
17 changed files with 308 additions and 188 deletions
+1
View File
@@ -13,6 +13,7 @@
#include "hashmap.h"
#include "log.h"
#include "macro.h"
#include "nulstr-util.h"
#include "path-util.h"
#include "set.h"
#include "sort-util.h"
+1
View File
@@ -21,6 +21,7 @@
#include "log.h"
#include "macro.h"
#include "mkdir.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
+112
View File
@@ -2,6 +2,118 @@
#include "nulstr-util.h"
#include "string-util.h"
#include "strv.h"
char** strv_parse_nulstr(const char *s, size_t l) {
/* 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. */
_cleanup_strv_free_ char **v = NULL;
size_t c = 0, i = 0;
assert(s || l <= 0);
if (l <= 0)
return new0(char*, 1);
for (const char *p = s; p < s + l; p++)
if (*p == 0)
c++;
if (s[l-1] != 0)
c++;
v = new0(char*, c+1);
if (!v)
return NULL;
for (const char *p = s; p < s + l; ) {
const char *e;
e = memchr(p, 0, s + l - p);
v[i] = memdup_suffix0(p, e ? e - p : s + l - p);
if (!v[i])
return NULL;
i++;
if (!e)
break;
p = e + 1;
}
assert(i == c);
return TAKE_PTR(v);
}
char** strv_split_nulstr(const char *s) {
_cleanup_strv_free_ char **l = NULL;
/* This parses a nulstr, without specification of size, and stops at an empty string. This cannot
* parse nulstrs with embedded empty strings hence, as an empty string is an end marker. Use
* strv_parse_nulstr() above to parse a nulstr with embedded empty strings (which however requires a
* size to be specified) */
NULSTR_FOREACH(i, s)
if (strv_extend(&l, i) < 0)
return NULL;
return l ? TAKE_PTR(l) : strv_new(NULL);
}
int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
/* Builds a nulstr and returns it together with the size. An extra NUL byte will be appended (⚠️ but
* not included in the size! ⚠️). This is done so that the nulstr can be used both in
* strv_parse_nulstr() and in NULSTR_FOREACH()/strv_split_nulstr() contexts, i.e. with and without a
* size parameter. In the former case we can include empty strings, in the latter case we cannot (as
* that is the end marker).
*
* When NULSTR_FOREACH()/strv_split_nulstr() is used it is often assumed that the nulstr ends in two
* NUL bytes (which it will, if not empty). To ensure that this assumption *always* holds, we'll
* return a buffer with two NUL bytes in that case, but return a size of zero. */
_cleanup_free_ char *m = NULL;
size_t n = 0;
assert(ret);
assert(ret_size);
STRV_FOREACH(i, l) {
size_t z;
z = strlen(*i);
if (!GREEDY_REALLOC(m, n + z + 2))
return -ENOMEM;
memcpy(m + n, *i, z + 1);
n += z + 1;
}
if (!m) {
/* return a buffer with an extra NUL, so that the assumption that we always have two trailing NULs holds */
m = new0(char, 2);
if (!m)
return -ENOMEM;
n = 0;
} else
/* Make sure there is a second extra NUL at the end of resulting nulstr (not counted in return size) */
m[n] = '\0';
*ret = TAKE_PTR(m);
*ret_size = n;
return 0;
}
const char* nulstr_get(const char *nulstr, const char *needle) {
if (!nulstr)
+19
View File
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <errno.h>
#include <macro.h>
#include <stdbool.h>
#include <string.h>
@@ -15,3 +17,20 @@ const char* nulstr_get(const char *nulstr, const char *needle);
static inline bool nulstr_contains(const char *nulstr, const char *needle) {
return nulstr_get(nulstr, needle);
}
char** strv_parse_nulstr(const char *s, size_t l);
char** strv_split_nulstr(const char *s);
int strv_make_nulstr(char * const *l, char **p, size_t *n);
static inline int strv_from_nulstr(char ***ret, const char *nulstr) {
char **t;
assert(ret);
t = strv_split_nulstr(nulstr);
if (!t)
return -ENOMEM;
*ret = t;
return 0;
}
+1
View File
@@ -37,6 +37,7 @@
#include "missing_sched.h"
#include "missing_syscall.h"
#include "namespace-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
-112
View File
@@ -623,118 +623,6 @@ char** strv_remove(char **l, const char *s) {
return l;
}
char** strv_parse_nulstr(const char *s, size_t l) {
/* 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.
*/
size_t c = 0, i = 0;
char **v;
assert(s || l <= 0);
if (l <= 0)
return new0(char*, 1);
for (const char *p = s; p < s + l; p++)
if (*p == 0)
c++;
if (s[l-1] != 0)
c++;
v = new0(char*, c+1);
if (!v)
return NULL;
for (const char *p = s; p < s + l; ) {
const char *e;
e = memchr(p, 0, s + l - p);
v[i] = strndup(p, e ? e - p : s + l - p);
if (!v[i]) {
strv_free(v);
return NULL;
}
i++;
if (!e)
break;
p = e + 1;
}
assert(i == c);
return v;
}
char** strv_split_nulstr(const char *s) {
char **r = NULL;
NULSTR_FOREACH(i, s)
if (strv_extend(&r, i) < 0) {
strv_free(r);
return NULL;
}
if (!r)
return strv_new(NULL);
return r;
}
int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
/* 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.
*/
_cleanup_free_ char *m = NULL;
size_t n = 0;
assert(ret);
assert(ret_size);
STRV_FOREACH(i, l) {
size_t z;
z = strlen(*i);
if (!GREEDY_REALLOC(m, n + z + 2))
return -ENOMEM;
memcpy(m + n, *i, z + 1);
n += z + 1;
}
if (!m) {
m = new0(char, 2);
if (!m)
return -ENOMEM;
n = 1;
} else
/* make sure there is a second extra NUL at the end of resulting nulstr */
m[n] = '\0';
assert(n > 0);
*ret = TAKE_PTR(m);
*ret_size = n - 1;
return 0;
}
bool strv_overlap(char * const *a, char * const *b) {
STRV_FOREACH(i, a)
if (strv_contains(b, *i))
-14
View File
@@ -124,20 +124,6 @@ static inline char *strv_join(char * const *l, const char *separator) {
return strv_join_full(l, separator, NULL, false);
}
char** strv_parse_nulstr(const char *s, size_t l);
char** strv_split_nulstr(const char *s);
int strv_make_nulstr(char * const *l, char **p, size_t *n);
static inline int strv_from_nulstr(char ***a, const char *nulstr) {
char **t;
t = strv_split_nulstr(nulstr);
if (!t)
return -ENOMEM;
*a = t;
return 0;
}
bool strv_overlap(char * const *a, char * const *b) _pure_;
#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \