Merge pull request #16635 from keszybz/do-not-for-each-word

Drop FOREACH_WORD
This commit is contained in:
Lennart Poettering 2020-09-09 17:43:38 +02:00 committed by GitHub
commit 12ce0f4173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 686 additions and 641 deletions

View File

@ -74,9 +74,6 @@ ForEachMacros:
- FOREACH_INOTIFY_EVENT
- FOREACH_STRING
- FOREACH_SUBSYSTEM
- _FOREACH_WORD
- FOREACH_WORD
- FOREACH_WORD_SEPARATOR
- HASHMAP_FOREACH
- HASHMAP_FOREACH_IDX
- HASHMAP_FOREACH_KEY

View File

@ -35,7 +35,7 @@
<funcprototype>
<funcdef>int <function>sd_machine_get_ifindices</function></funcdef>
<paramdef>const char* <parameter>machine</parameter></paramdef>
<paramdef>int **<parameter>ifindices</parameter></paramdef>
<paramdef>int **<parameter>ret_ifindices</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
@ -53,21 +53,22 @@
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
<para><function>sd_machine_get_ifindices()</function> may be used
to determine the numeric indices of the network interfaces on the
host that are pointing towards the specified locally running
virtual machine or container that is registered with
<para><function>sd_machine_get_ifindices()</function> may be used to determine the numeric indices of the
network interfaces on the host that are pointing towards the specified locally running virtual machine or
container. The vm or container must be registered with
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
The returned array needs to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
The output parameter <parameter>ret_ifindices</parameter> may be passed as <constant>NULL</constant> when
the output value is not needed. The returned array needs to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
use.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative
errno-style error code.</para>
<para>On success, these functions return a non-negative integer.
<function>sd_machine_get_ifindices()</function> returns the number of the relevant network interfaces.
On failure, these calls return a negative errno-style error code.</para>
<refsect2>
<title>Errors</title>

View File

@ -38,9 +38,9 @@
<funcprototype>
<funcdef>int <function>sd_seat_get_sessions</function></funcdef>
<paramdef>const char *<parameter>seat</parameter></paramdef>
<paramdef>char ***<parameter>sessions</parameter></paramdef>
<paramdef>uid_t **<parameter>uid</parameter></paramdef>
<paramdef>unsigned int *<parameter>n_uids</parameter></paramdef>
<paramdef>char ***<parameter>ret_sessions</parameter></paramdef>
<paramdef>uid_t **<parameter>ret_uids</parameter></paramdef>
<paramdef>unsigned int *<parameter>ret_n_uids</parameter></paramdef>
</funcprototype>
<funcprototype>
@ -68,21 +68,16 @@
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use.</para>
<para><function>sd_seat_get_sessions()</function> may be used to
determine all sessions on the specified seat. Returns two arrays,
one (<constant>NULL</constant> terminated) with the session
identifiers of the sessions and one with the user identifiers of
the Unix users the sessions belong to. An additional parameter may
be used to return the number of entries in the latter array. This
value is the same the return value, if the latter is nonnegative.
The two arrays and the last parameter may be passed as
<constant>NULL</constant> in case these values need not to be
determined. The arrays and the strings referenced by them need to
be freed with the libc
<citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
call after use. Note that instead of an empty array
<constant>NULL</constant> may be returned and should be considered
equivalent to an empty array.</para>
<para><function>sd_seat_get_sessions()</function> may be used to determine all sessions on the specified
seat. Returns two arrays, one (<constant>NULL</constant> terminated) with the session identifiers of the
sessions and one with the user identifiers of the Unix users the sessions belong to. An additional
parameter may be used to return the number of entries in the latter array. This value is the same as the
return value if the return value is nonnegative. The output parameters may be passed as
<constant>NULL</constant> in case these output values are not needed. The arrays and the strings
referenced by them need to be freed with the libc <citerefentry
project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after
use. Note that instead of an empty array <constant>NULL</constant> may be returned and should be
considered equivalent to an empty array.</para>
<para><function>sd_seat_can_tty()</function> may be used to
determine whether a specific seat provides TTY functionality, i.e.

View File

@ -652,14 +652,13 @@ int cg_remove_xattr(const char *controller, const char *path, const char *name)
return 0;
}
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
_cleanup_fclose_ FILE *f = NULL;
const char *fs, *controller_str;
int unified, r;
size_t cs = 0;
assert(path);
assert(pid >= 0);
assert(ret_path);
if (controller) {
if (!cg_controller_is_valid(controller))
@ -675,8 +674,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
else
controller_str = controller;
cs = strlen(controller_str);
}
fs = procfs_file_alloca(pid, "cgroup");
@ -688,13 +685,13 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
for (;;) {
_cleanup_free_ char *line = NULL;
char *e, *p;
char *e;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return r;
if (r == 0)
break;
return -ENODATA;
if (unified) {
e = startswith(line, "0:");
@ -706,9 +703,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
continue;
} else {
char *l;
size_t k;
const char *word, *state;
bool found = false;
l = strchr(line, ':');
if (!l)
@ -718,31 +712,27 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
e = strchr(l, ':');
if (!e)
continue;
*e = 0;
FOREACH_WORD_SEPARATOR(word, k, l, ",", state)
if (k == cs && memcmp(word, controller_str, cs) == 0) {
found = true;
break;
}
if (!found)
r = string_contains_word(l, ",", controller_str);
if (r < 0)
return r;
if (r == 0)
continue;
}
p = strdup(e + 1);
if (!p)
char *path = strdup(e + 1);
if (!path)
return -ENOMEM;
/* Truncate suffix indicating the process is a zombie */
e = endswith(p, " (deleted)");
e = endswith(path, " (deleted)");
if (e)
*e = 0;
*path = p;
*ret_path = path;
return 0;
}
return -ENODATA;
}
int cg_install_release_agent(const char *controller, const char *agent) {

View File

@ -687,7 +687,7 @@ char **replace_env_argv(char **argv, char **env) {
if (e) {
int r;
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);

View File

@ -14,6 +14,7 @@
#include "log.h"
#include "macro.h"
#include "string-util.h"
#include "strv.h"
#include "utf8.h"
int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {

View File

@ -538,6 +538,9 @@ static inline int __coverity_check_and_return__(int condition) {
(y) = (_t); \
} while (false)
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */
#define FOREACH_POINTER(p, x, ...) \
for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \

View File

@ -8,12 +8,14 @@
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
#include "gunicode.h"
#include "locale-util.h"
#include "macro.h"
#include "memory-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
@ -110,83 +112,6 @@ char* first_word(const char *s, const char *word) {
return (char*) p;
}
static size_t strcspn_escaped(const char *s, const char *reject) {
bool escaped = false;
int n;
for (n = 0; s[n] != '\0'; n++) {
if (escaped)
escaped = false;
else if (s[n] == '\\')
escaped = true;
else if (strchr(reject, s[n]))
break;
}
return n;
}
/* Split a string into words. */
const char* split(
const char **state,
size_t *l,
const char *separator,
SplitFlags flags) {
const char *current;
assert(state);
assert(l);
if (!separator)
separator = WHITESPACE;
current = *state;
if (*current == '\0') /* already at the end? */
return NULL;
current += strspn(current, separator); /* skip leading separators */
if (*current == '\0') { /* at the end now? */
*state = current;
return NULL;
}
if (FLAGS_SET(flags, SPLIT_QUOTES)) {
if (strchr(QUOTES, *current)) {
/* We are looking at a quote */
*l = strcspn_escaped(current + 1, CHAR_TO_STR(*current));
if (current[*l + 1] != *current ||
(current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
if (FLAGS_SET(flags, SPLIT_RELAX)) {
*state = current + *l + 1 + (current[*l + 1] != '\0');
return current + 1;
}
*state = current;
return NULL;
}
*state = current++ + *l + 2;
} else {
/* We are looking at a something that is not a quote */
*l = strcspn_escaped(current, separator);
if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) {
/* unfinished escape */
*state = current;
return NULL;
}
*state = current + *l;
}
} else {
*l = strcspn(current, separator);
*state = current + *l;
}
return current;
}
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
@ -1207,3 +1132,30 @@ int string_extract_line(const char *s, size_t i, char **ret) {
c++;
}
}
int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) {
/* In the default mode with no separators specified, we split on whitespace and
* don't coalesce separators. */
const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0;
const char *found = NULL;
for (const char *p = string;;) {
_cleanup_free_ char *w = NULL;
int r;
r = extract_first_word(&p, &w, separators, flags);
if (r < 0)
return r;
if (r == 0)
break;
found = strv_find(words, w);
if (found)
break;
}
if (ret_word)
*ret_word = found;
return !!found;
}

View File

@ -108,24 +108,6 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
typedef enum SplitFlags {
SPLIT_QUOTES = 0x01 << 0,
SPLIT_RELAX = 0x01 << 1,
} SplitFlags;
/* Smelly. Do not use this anymore. Use extract_first_word() instead! */
const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
/* Similar, don't use this anymore */
#define FOREACH_WORD(word, length, s, state) \
_FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
_FOREACH_WORD(word, length, s, separator, 0, state)
#define _FOREACH_WORD(word, length, s, separator, flags, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
char *strnappend(const char *s, const char *suffix, size_t length);
char *strjoin_real(const char *x, ...) _sentinel_;
@ -280,3 +262,8 @@ char* string_erase(char *x);
int string_truncate_lines(const char *s, size_t n_lines, char **ret);
int string_extract_line(const char *s, size_t i, char **ret);
int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word);
static inline int string_contains_word(const char *string, const char *separators, const char *word) {
return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL);
}

View File

@ -256,44 +256,6 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
return 0;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags) {
const char *word, *state;
size_t l;
size_t n, i;
char **r;
assert(s);
if (!separator)
separator = WHITESPACE;
s += strspn(s, separator);
if (isempty(s))
return new0(char*, 1);
n = 0;
_FOREACH_WORD(word, l, s, separator, flags, state)
n++;
r = new(char*, n+1);
if (!r)
return NULL;
i = 0;
_FOREACH_WORD(word, l, s, separator, flags, state) {
r[i] = strndup(word, l);
if (!r[i]) {
strv_free(r);
return NULL;
}
i++;
}
r[i] = NULL;
return r;
}
char **strv_split_newlines(const char *s) {
char **l;
size_t n;
@ -317,7 +279,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
size_t n = 0, allocated = 0;
int r;

View File

@ -72,13 +72,19 @@ static inline bool strv_isempty(char * const *l) {
return !l || !*l;
}
char **strv_split_full(const char *s, const char *separator, SplitFlags flags);
static inline char **strv_split(const char *s, const char *separator) {
return strv_split_full(s, separator, 0);
}
char **strv_split_newlines(const char *s);
int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags);
static inline char **strv_split(const char *s, const char *separators) {
char **ret;
int r;
r = strv_split_full(&ret, s, separators, 0);
if (r < 0)
return NULL;
return ret;
}
/* Given a string containing white-space separated tuples of words themselves separated by ':',
* returns a vector of strings. If the second element in a tuple is missing, the corresponding
@ -123,10 +129,6 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
char **strv_sort(char **l);
void strv_print(char * const *l);
#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
#define strv_from_stdarg_alloca(first) \
({ \
char **_l; \

View File

@ -4438,15 +4438,13 @@ int config_parse_set_status(
void *data,
void *userdata) {
size_t l;
const char *word, *state;
int r;
ExitStatusSet *status_set = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
assert(status_set);
/* Empty assignment resets the list */
if (isempty(rvalue)) {
@ -4454,25 +4452,26 @@ int config_parse_set_status(
return 0;
}
FOREACH_WORD(word, l, rvalue, state) {
_cleanup_free_ char *temp;
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
Bitmap *bitmap;
temp = strndup(word, l);
if (!temp)
return log_oom();
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse %s: %m", lvalue);
if (r == 0)
return 0;
/* We need to call exit_status_from_string() first, because we want
* to parse numbers as exit statuses, not signals. */
r = exit_status_from_string(temp);
r = exit_status_from_string(word);
if (r >= 0) {
assert(r >= 0 && r < 256);
bitmap = &status_set->status;
} else {
r = signal_from_string(temp);
if (r <= 0) {
r = signal_from_string(word);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Failed to parse value, ignoring: %s", word);
continue;
@ -4484,10 +4483,6 @@ int config_parse_set_status(
if (r < 0)
return log_error_errno(r, "Failed to set signal or status %s: %m", word);
}
if (!isempty(state))
log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
int config_parse_namespace_path_strv(

View File

@ -288,19 +288,19 @@ static int parse_one_option(const char *option) {
}
static int parse_options(const char *options) {
const char *word, *state;
size_t l;
int r;
assert(options);
FOREACH_WORD_SEPARATOR(word, l, options, ",", state) {
_cleanup_free_ char *o;
for (;;) {
_cleanup_free_ char *word = NULL;
int r;
o = strndup(word, l);
if (!o)
return -ENOMEM;
r = parse_one_option(o);
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_debug_errno(r, "Failed to parse options: %m");
if (r == 0)
break;
r = parse_one_option(word);
if (r < 0)
return r;
}

View File

@ -541,28 +541,33 @@ static int help(void) {
}
static int parse_flags(const char *flag_str, int flags) {
const char *word, *state;
size_t l;
for (;;) {
_cleanup_free_ char *word = NULL;
int r;
FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) {
if (strneq("masked", word, l))
r = extract_first_word(&flag_str, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return flags;
if (streq(word, "masked"))
flags |= SHOW_MASKED;
else if (strneq ("equivalent", word, l))
else if (streq(word, "equivalent"))
flags |= SHOW_EQUIVALENT;
else if (strneq("redirected", word, l))
else if (streq(word, "redirected"))
flags |= SHOW_REDIRECTED;
else if (strneq("overridden", word, l))
else if (streq(word, "overridden"))
flags |= SHOW_OVERRIDDEN;
else if (strneq("unchanged", word, l))
else if (streq(word, "unchanged"))
flags |= SHOW_UNCHANGED;
else if (strneq("extended", word, l))
else if (streq(word, "extended"))
flags |= SHOW_EXTENDED;
else if (strneq("default", word, l))
else if (streq(word, "default"))
flags |= SHOW_DEFAULTS;
else
return -EINVAL;
}
return flags;
}
static int parse_argv(int argc, char *argv[]) {

View File

@ -99,89 +99,85 @@ static int verify_tty(const char *name) {
return 0;
}
static int run_container(void) {
_cleanup_free_ char *container_ttys = NULL;
int r;
log_debug("Automatically adding console shell.");
r = add_symlink("console-getty.service", "console-getty.service");
if (r < 0)
return r;
/* When $container_ttys is set for PID 1, spawn gettys on all ptys named therein.
* Note that despite the variable name we only support ptys here. */
(void) getenv_for_pid(1, "container_ttys", &container_ttys);
for (const char *p = container_ttys;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse $container_ttys: %m");
if (r == 0)
return 0;
const char *tty = word;
/* First strip off /dev/ if it is specified */
tty = path_startswith(tty, "/dev/") ?: tty;
/* Then, make sure it's actually a pty */
tty = path_startswith(tty, "pts/");
if (!tty)
continue;
r = add_container_getty(tty);
if (r < 0)
return r;
}
}
static int run(const char *dest, const char *dest_early, const char *dest_late) {
_cleanup_free_ char *active = NULL;
const char *j;
int r;
assert_se(arg_dest = dest);
if (detect_container() > 0) {
_cleanup_free_ char *container_ttys = NULL;
if (detect_container() > 0)
/* Add console shell and look at $container_ttys, but don't do add any
* further magic if we are in a container. */
return run_container();
log_debug("Automatically adding console shell.");
/* Automatically add in a serial getty on all active kernel consoles */
_cleanup_free_ char *active = NULL;
(void) read_one_line_file("/sys/class/tty/console/active", &active);
for (const char *p = active;;) {
_cleanup_free_ char *tty = NULL;
r = add_symlink("console-getty.service", "console-getty.service");
r = extract_first_word(&p, &tty, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse /sys/class/tty/console/active: %m");
if (r == 0)
break;
/* We assume that gettys on virtual terminals are started via manual configuration and do
* this magic only for non-VC terminals. */
if (isempty(tty) || tty_is_vc(tty))
continue;
if (verify_tty(tty) < 0)
continue;
r = add_serial_getty(tty);
if (r < 0)
return r;
/* When $container_ttys is set for PID 1, spawn
* gettys on all ptys named therein. Note that despite
* the variable name we only support ptys here. */
r = getenv_for_pid(1, "container_ttys", &container_ttys);
if (r > 0) {
const char *word, *state;
size_t l;
FOREACH_WORD(word, l, container_ttys, state) {
const char *t;
char tty[l + 1];
memcpy(tty, word, l);
tty[l] = 0;
/* First strip off /dev/ if it is specified */
t = path_startswith(tty, "/dev/");
if (!t)
t = tty;
/* Then, make sure it's actually a pty */
t = path_startswith(t, "pts/");
if (!t)
continue;
r = add_container_getty(t);
if (r < 0)
return r;
}
}
/* Don't add any further magic if we are in a container */
return 0;
}
if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
const char *word, *state;
size_t l;
/* Automatically add in a serial getty on all active
* kernel consoles */
FOREACH_WORD(word, l, active, state) {
_cleanup_free_ char *tty = NULL;
tty = strndup(word, l);
if (!tty)
return log_oom();
/* We assume that gettys on virtual terminals are
* started via manual configuration and do this magic
* only for non-VC terminals. */
if (isempty(tty) || tty_is_vc(tty))
continue;
if (verify_tty(tty) < 0)
continue;
r = add_serial_getty(tty);
if (r < 0)
return r;
}
}
/* Automatically add in a serial getty on the first
* virtualizer console */
const char *j;
FOREACH_STRING(j,
"hvc0",
"xvc0",

View File

@ -124,7 +124,7 @@ static int spawn_getter(const char *getter) {
_cleanup_strv_free_ char **words = NULL;
assert(getter);
r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");

View File

@ -950,74 +950,69 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) {
}
_public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
const char *word, *state;
size_t l;
unsigned long long seqnum, monotonic, realtime, xor_hash;
bool
seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
bool seqnum_id_set = false,
seqnum_set = false,
boot_id_set = false,
monotonic_set = false,
realtime_set = false,
xor_hash_set = false;
sd_id128_t seqnum_id, boot_id;
int r;
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
assert_return(!isempty(cursor), -EINVAL);
FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) {
char *item;
int k = 0;
for (const char *p = cursor;;) {
_cleanup_free_ char *word = NULL;
if (l < 2 || word[1] != '=')
r = extract_first_word(&p, &word, ";", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
break;
if (word[0] == '\0' || word[1] != '=')
return -EINVAL;
item = strndup(word, l);
if (!item)
return -ENOMEM;
switch (word[0]) {
case 's':
seqnum_id_set = true;
k = sd_id128_from_string(item+2, &seqnum_id);
r = sd_id128_from_string(word + 2, &seqnum_id);
if (r < 0)
return r;
break;
case 'i':
seqnum_set = true;
if (sscanf(item+2, "%llx", &seqnum) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &seqnum) != 1)
return -EINVAL;
break;
case 'b':
boot_id_set = true;
k = sd_id128_from_string(item+2, &boot_id);
r = sd_id128_from_string(word + 2, &boot_id);
break;
case 'm':
monotonic_set = true;
if (sscanf(item+2, "%llx", &monotonic) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &monotonic) != 1)
return -EINVAL;
break;
case 't':
realtime_set = true;
if (sscanf(item+2, "%llx", &realtime) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &realtime) != 1)
return -EINVAL;
break;
case 'x':
xor_hash_set = true;
if (sscanf(item+2, "%llx", &xor_hash) != 1)
k = -EINVAL;
if (sscanf(word + 2, "%llx", &xor_hash) != 1)
return -EINVAL;
break;
}
free(item);
if (k < 0)
return k;
}
if ((!seqnum_set || !seqnum_id_set) &&

View File

@ -101,7 +101,7 @@ _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
e = getenv("LISTEN_FDNAMES");
if (e) {
n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
n_names = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (n_names < 0) {
unsetenv_all(unset_environment);
return n_names;

View File

@ -316,34 +316,33 @@ static int device_amend(sd_device *device, const char *key, const char *value) {
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value);
} else if (streq(key, "DEVLINKS")) {
const char *word, *state;
size_t l;
for (const char *p = value;;) {
_cleanup_free_ char *word = NULL;
FOREACH_WORD(word, l, value, state) {
char devlink[l + 1];
strncpy(devlink, word, l);
devlink[l] = '\0';
r = device_add_devlink(device, devlink);
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink);
return r;
if (r == 0)
break;
r = device_add_devlink(device, word);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word);
}
} else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) {
const char *word, *state;
size_t l;
for (const char *p = value;;) {
_cleanup_free_ char *word = NULL;
FOREACH_WORD_SEPARATOR(word, l, value, ":", state) {
char tag[l + 1];
(void) strncpy(tag, word, l);
tag[l] = '\0';
r = device_add_tag(device, tag, streq(key, "CURRENT_TAGS"));
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag);
}
return r;
if (r == 0)
break;
r = device_add_tag(device, word, streq(key, "CURRENT_TAGS"));
if (r < 0)
return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word);
}
} else {
r = device_add_property_internal(device, key, value);
if (r < 0)

View File

@ -12,6 +12,7 @@
#include "dirent-util.h"
#include "env-file.h"
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
@ -22,6 +23,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "socket-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "user-util.h"
@ -331,35 +333,29 @@ static int file_of_seat(const char *seat, char **_p) {
}
_public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
_cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
size_t l;
_cleanup_free_ char *filename = NULL, *content = NULL;
int r;
const char *word, *variable, *state;
assert_return(uid_is_valid(uid), -EINVAL);
r = file_of_seat(seat, &p);
r = file_of_seat(seat, &filename);
if (r < 0)
return r;
variable = require_active ? "ACTIVE_UID" : "UIDS";
r = parse_env_file(NULL, p, variable, &s);
r = parse_env_file(NULL, filename,
require_active ? "ACTIVE_UID" : "UIDS",
&content);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
if (isempty(s))
if (isempty(content))
return 0;
if (asprintf(&t, UID_FMT, uid) < 0)
return -ENOMEM;
char t[DECIMAL_STR_MAX(uid_t)];
xsprintf(t, UID_FMT, uid);
FOREACH_WORD(word, l, s, state)
if (strneq(t, word, l))
return 1;
return 0;
return string_contains_word(content, ",", t);
}
static int uid_get_array(uid_t uid, const char *variable, char ***array) {
@ -382,7 +378,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) {
if (r < 0)
return r;
a = strv_split(s, " ");
a = strv_split(s, NULL);
if (!a)
return -ENOMEM;
@ -654,73 +650,70 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
return 0;
}
_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
_cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
_cleanup_strv_free_ char **a = NULL;
_cleanup_free_ uid_t *b = NULL;
unsigned n = 0;
_public_ int sd_seat_get_sessions(
const char *seat,
char ***ret_sessions,
uid_t **ret_uids,
unsigned *ret_n_uids) {
_cleanup_free_ char *fname = NULL, *session_line = NULL, *uid_line = NULL;
_cleanup_strv_free_ char **sessions = NULL;
_cleanup_free_ uid_t *uids = NULL;
unsigned n_sessions = 0;
int r;
r = file_of_seat(seat, &p);
r = file_of_seat(seat, &fname);
if (r < 0)
return r;
r = parse_env_file(NULL, p,
"SESSIONS", &s,
"UIDS", &t);
r = parse_env_file(NULL, fname,
"SESSIONS", &session_line,
"UIDS", &uid_line);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
return r;
if (s) {
a = strv_split(s, " ");
if (!a)
if (session_line) {
sessions = strv_split(session_line, NULL);
if (!sessions)
return -ENOMEM;
}
if (uids && t) {
const char *word, *state;
size_t l;
n_sessions = strv_length(sessions);
};
FOREACH_WORD(word, l, t, state)
n++;
if (ret_uids && uid_line) {
uids = new(uid_t, n_sessions);
if (!uids)
return -ENOMEM;
if (n > 0) {
unsigned i = 0;
size_t n = 0;
for (const char *p = uid_line;;) {
_cleanup_free_ char *word = NULL;
b = new(uid_t, n);
if (!b)
return -ENOMEM;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return r;
if (r == 0)
break;
FOREACH_WORD(word, l, t, state) {
_cleanup_free_ char *k = NULL;
k = strndup(word, l);
if (!k)
return -ENOMEM;
r = parse_uid(k, b + i);
if (r < 0)
return r;
i++;
}
r = parse_uid(word, &uids[n++]);
if (r < 0)
return r;
}
if (n != n_sessions)
return -EUCLEAN;
}
r = (int) strv_length(a);
if (ret_sessions)
*ret_sessions = TAKE_PTR(sessions);
if (ret_uids)
*ret_uids = TAKE_PTR(uids);
if (ret_n_uids)
*ret_n_uids = n_sessions;
if (sessions)
*sessions = TAKE_PTR(a);
if (uids)
*uids = TAKE_PTR(b);
if (n_uids)
*n_uids = n;
return r;
return n_sessions;
}
static int seat_get_can(const char *seat, const char *variable) {
@ -901,47 +894,52 @@ _public_ int sd_machine_get_class(const char *machine, char **class) {
return 0;
}
_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) {
_cleanup_free_ char *netif = NULL;
size_t l, allocated = 0, nr = 0;
int *ni = NULL;
const char *p, *word, *state;
_public_ int sd_machine_get_ifindices(const char *machine, int **ret_ifindices) {
_cleanup_free_ char *netif_line = NULL;
const char *p;
int r;
assert_return(machine_name_is_valid(machine), -EINVAL);
assert_return(ifindices, -EINVAL);
p = strjoina("/run/systemd/machines/", machine);
r = parse_env_file(NULL, p, "NETIF", &netif);
r = parse_env_file(NULL, p, "NETIF", &netif_line);
if (r == -ENOENT)
return -ENXIO;
if (r < 0)
return r;
if (!netif) {
*ifindices = NULL;
if (!netif_line) {
*ret_ifindices = NULL;
return 0;
}
FOREACH_WORD(word, l, netif, state) {
char buf[l+1];
int ifi;
_cleanup_strv_free_ char **tt = strv_split(netif_line, NULL);
if (!tt)
return -ENOMEM;
*(char*) (mempcpy(buf, word, l)) = 0;
ifi = parse_ifindex(buf);
if (ifi < 0)
continue;
if (!GREEDY_REALLOC(ni, allocated, nr+1)) {
free(ni);
size_t n = 0;
int *ifindices;
if (ret_ifindices) {
ifindices = new(int, strv_length(tt));
if (!ifindices)
return -ENOMEM;
}
ni[nr++] = ifi;
}
*ifindices = ni;
return nr;
for (size_t i = 0; tt[i]; i++) {
int ind;
ind = parse_ifindex(tt[i]);
if (ind < 0)
/* Return -EUCLEAN to distinguish from -EINVAL for invalid args */
return ind == -EINVAL ? -EUCLEAN : ind;
if (ret_ifindices)
ifindices[n] = ind;
n++;
}
if (ret_ifindices)
*ret_ifindices = ifindices;
return n;
}
static int MONITOR_TO_FD(sd_login_monitor *m) {

View File

@ -251,7 +251,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
if (in_section && first_word(l, "Option")) {
_cleanup_strv_free_ char **a = NULL;
r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;
@ -274,7 +274,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
} else if (!in_section && first_word(l, "Section")) {
_cleanup_strv_free_ char **a = NULL;
r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return -ENOMEM;
@ -489,7 +489,7 @@ static int read_next_mapping(const char* filename,
if (IN_SET(l[0], 0, '#'))
continue;
r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;

View File

@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "env-file.h"
#include "errno-list.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@ -481,31 +482,39 @@ const char *inhibit_what_to_string(InhibitWhat w) {
return buffer;
}
InhibitWhat inhibit_what_from_string(const char *s) {
int inhibit_what_from_string(const char *s) {
InhibitWhat what = 0;
const char *word, *state;
size_t l;
FOREACH_WORD_SEPARATOR(word, l, s, ":", state) {
if (l == 8 && strneq(word, "shutdown", l))
for (const char *p = s;;) {
_cleanup_free_ char *word = NULL;
int r;
/* A sanity check that our return values fit in an int */
assert_cc((int) _INHIBIT_WHAT_MAX == _INHIBIT_WHAT_MAX);
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return what;
if (streq(word, "shutdown"))
what |= INHIBIT_SHUTDOWN;
else if (l == 5 && strneq(word, "sleep", l))
else if (streq(word, "sleep"))
what |= INHIBIT_SLEEP;
else if (l == 4 && strneq(word, "idle", l))
else if (streq(word, "idle"))
what |= INHIBIT_IDLE;
else if (l == 16 && strneq(word, "handle-power-key", l))
else if (streq(word, "handle-power-key"))
what |= INHIBIT_HANDLE_POWER_KEY;
else if (l == 18 && strneq(word, "handle-suspend-key", l))
else if (streq(word, "handle-suspend-key"))
what |= INHIBIT_HANDLE_SUSPEND_KEY;
else if (l == 20 && strneq(word, "handle-hibernate-key", l))
else if (streq(word, "handle-hibernate-key"))
what |= INHIBIT_HANDLE_HIBERNATE_KEY;
else if (l == 17 && strneq(word, "handle-lid-switch", l))
else if (streq(word, "handle-lid-switch"))
what |= INHIBIT_HANDLE_LID_SWITCH;
else
return _INHIBIT_WHAT_INVALID;
}
return what;
}
static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {

View File

@ -66,7 +66,7 @@ InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm);
bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending);
const char *inhibit_what_to_string(InhibitWhat k);
InhibitWhat inhibit_what_from_string(const char *s);
int inhibit_what_from_string(const char *s);
const char *inhibit_mode_to_string(InhibitMode k);
InhibitMode inhibit_mode_from_string(const char *s);

View File

@ -301,7 +301,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl
CustomMount *m;
int k;
k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
k = strv_split_full(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (k < 0)
return k;
if (k < 2)

View File

@ -88,13 +88,12 @@ int change_uid_gid_raw(
int change_uid_gid(const char *user, char **_home) {
char *x, *u, *g, *h;
const char *word, *state;
_cleanup_free_ gid_t *gids = NULL;
_cleanup_free_ char *home = NULL, *line = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
unsigned n_gids = 0;
size_t sz = 0, l;
size_t sz = 0;
uid_t uid;
gid_t gid;
pid_t pid;
@ -208,16 +207,19 @@ int change_uid_gid(const char *user, char **_home) {
x += strcspn(x, WHITESPACE);
x += strspn(x, WHITESPACE);
FOREACH_WORD(word, l, x, state) {
char c[l+1];
for (const char *p = x;;) {
_cleanup_free_ char *word = NULL;
memcpy(c, word, l);
c[l] = 0;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to parse group data from getent: %m");
if (r == 0)
break;
if (!GREEDY_REALLOC(gids, sz, n_gids+1))
return log_oom();
r = parse_gid(c, &gids[n_gids++]);
r = parse_gid(word, &gids[n_gids++]);
if (r < 0)
return log_error_errno(r, "Failed to parse group data from getent: %m");
}

View File

@ -340,7 +340,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
return log_error_errno(r, "Failed to parse path: %m");
}
r = strv_split_extract(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse command line: %m");

View File

@ -239,10 +239,10 @@ typedef enum Disabled {
#define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \
CONFIG_PARSER_PROTOTYPE(function) { \
type **enums = data, x, *ys; \
type **enums = data; \
_cleanup_free_ type *xs = NULL; \
const char *word, *state; \
size_t l, i = 0; \
size_t i = 0; \
int r; \
\
assert(filename); \
assert(lvalue); \
@ -255,29 +255,32 @@ typedef enum Disabled {
\
*xs = invalid; \
\
FOREACH_WORD(word, l, rvalue, state) { \
for (const char *p = rvalue;;) { \
_cleanup_free_ char *en = NULL; \
type *new_xs; \
type x, *new_xs; \
\
en = strndup(word, l); \
if (!en) \
r = extract_first_word(&p, &en, NULL, 0); \
if (r == -ENOMEM) \
return log_oom(); \
if (r < 0) \
return log_syntax(unit, LOG_ERR, filename, line, 0, \
msg ": %s", en); \
if (r == 0) \
break; \
\
if ((x = name##_from_string(en)) < 0) { \
log_syntax(unit, LOG_WARNING, filename, line, 0, \
log_syntax(unit, LOG_WARNING, filename, line, 0, \
msg ", ignoring: %s", en); \
continue; \
} \
\
for (ys = xs; x != invalid && *ys != invalid; ys++) { \
if (*ys == x) { \
log_syntax(unit, LOG_NOTICE, filename, \
line, 0, \
"Duplicate entry, ignoring: %s", \
for (type *ys = xs; x != invalid && *ys != invalid; ys++) \
if (*ys == x) { \
log_syntax(unit, LOG_NOTICE, filename, line, 0, \
"Duplicate entry, ignoring: %s", \
en); \
x = invalid; \
} \
} \
\
if (x == invalid) \
continue; \
@ -292,6 +295,5 @@ typedef enum Disabled {
*(xs + i) = invalid; \
} \
\
free_and_replace(*enums, xs); \
return 0; \
return free_and_replace(*enums, xs); \
}

View File

@ -81,50 +81,53 @@ int fstab_is_mount_point(const char *mount) {
int fstab_filter_options(const char *opts, const char *names,
const char **ret_namefound, char **ret_value, char **ret_filtered) {
const char *name, *n = NULL, *x;
const char *name, *namefound = NULL, *x;
_cleanup_strv_free_ char **stor = NULL;
_cleanup_free_ char *v = NULL, **strv = NULL;
int r;
assert(names && *names);
if (!opts)
goto answer;
/* If !value and !filtered, this function is not allowed to fail. */
/* If !ret_value and !ret_filtered, this function is not allowed to fail. */
if (!ret_filtered) {
const char *word, *state;
size_t l;
for (const char *word = opts;;) {
const char *end = word + strcspn(word, ",");
FOREACH_WORD_SEPARATOR(word, l, opts, ",", state)
NULSTR_FOREACH(name, names) {
if (l < strlen(name))
if (end < word + strlen(name))
continue;
if (!strneq(word, name, strlen(name)))
continue;
/* we know that the string is NUL
* terminated, so *x is valid */
/* We know that the string is NUL terminated, so *x is valid */
x = word + strlen(name);
if (IN_SET(*x, '\0', '=', ',')) {
n = name;
namefound = name;
if (ret_value) {
free(v);
if (IN_SET(*x, '\0', ','))
v = NULL;
else {
assert(*x == '=');
x++;
v = strndup(x, l - strlen(name) - 1);
if (!v)
return -ENOMEM;
}
bool eq = *x == '=';
assert(eq || IN_SET(*x, ',', '\0'));
r = free_and_strndup(&v,
eq ? x + 1 : NULL,
eq ? end - x - 1 : 0);
if (r < 0)
return r;
}
break;
}
}
} else {
char **t, **s;
if (*end)
word = end + 1;
else
break;
}
} else {
stor = strv_split(opts, ",");
if (!stor)
return -ENOMEM;
@ -132,7 +135,8 @@ int fstab_filter_options(const char *opts, const char *names,
if (!strv)
return -ENOMEM;
for (s = t = strv; *s; s++) {
char **t = strv;
for (char **s = strv; *s; s++) {
NULSTR_FOREACH(name, names) {
x = startswith(*s, name);
if (x && IN_SET(*x, '\0', '='))
@ -144,18 +148,12 @@ int fstab_filter_options(const char *opts, const char *names,
continue;
found:
/* Keep the last occurrence found */
n = name;
namefound = name;
if (ret_value) {
free(v);
if (*x == '\0')
v = NULL;
else {
assert(*x == '=');
x++;
v = strdup(x);
if (!v)
return -ENOMEM;
}
assert(IN_SET(*x, '=', '\0'));
r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
if (r < 0)
return r;
}
}
*t = NULL;
@ -163,7 +161,7 @@ int fstab_filter_options(const char *opts, const char *names,
answer:
if (ret_namefound)
*ret_namefound = n;
*ret_namefound = namefound;
if (ret_filtered) {
char *f;
@ -176,7 +174,7 @@ answer:
if (ret_value)
*ret_value = TAKE_PTR(v);
return !!n;
return !!namefound;
}
int fstab_extract_values(const char *opts, const char *name, char ***values) {

View File

@ -100,9 +100,8 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
}
int can_sleep_state(char **types) {
char **type;
_cleanup_free_ char *text = NULL;
int r;
_cleanup_free_ char *p = NULL;
if (strv_isempty(types))
return true;
@ -113,34 +112,27 @@ int can_sleep_state(char **types) {
return false;
}
r = read_one_line_file("/sys/power/state", &p);
r = read_one_line_file("/sys/power/state", &text);
if (r < 0) {
log_debug_errno(r, "Failed to read /sys/power/state, cannot sleep: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
size_t l, k;
k = strlen(*type);
FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
if (l == k && memcmp(word, *type, l) == 0) {
log_debug("Sleep mode \"%s\" is supported by the kernel.", *type);
return true;
}
}
if (DEBUG_LOGGING) {
const char *found;
r = string_contains_word_strv(text, NULL, types, &found);
if (r < 0)
return log_debug_errno(r, "Failed to parse /sys/power/state: %m");
if (r > 0)
log_debug("Sleep mode \"%s\" is supported by the kernel.", found);
else if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(types, "/");
log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t));
}
return false;
return r;
}
int can_sleep_disk(char **types) {
_cleanup_free_ char *p = NULL;
char **type;
_cleanup_free_ char *text = NULL;
int r;
if (strv_isempty(types))
@ -152,29 +144,38 @@ int can_sleep_disk(char **types) {
return false;
}
r = read_one_line_file("/sys/power/disk", &p);
r = read_one_line_file("/sys/power/disk", &text);
if (r < 0) {
log_debug_errno(r, "Couldn't read /sys/power/disk: %m");
return false;
}
STRV_FOREACH(type, types) {
const char *word, *state;
size_t l, k;
for (const char *p = text;;) {
_cleanup_free_ char *word = NULL;
k = strlen(*type);
FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
if (l == k && memcmp(word, *type, l) == 0)
return true;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
if (r == 0)
break;
if (l == k + 2 &&
word[0] == '[' &&
memcmp(word + 1, *type, l - 2) == 0 &&
word[l-1] == ']')
return true;
char *s = word;
size_t l = strlen(s);
if (s[0] == '[' && s[l-1] == ']') {
s[l-1] = '\0';
s++;
}
if (strv_contains(types, s)) {
log_debug("Disk sleep mode \"%s\" is supported by the kernel.", s);
return true;
}
}
if (DEBUG_LOGGING) {
_cleanup_free_ char *t = strv_join(types, "/");
log_debug("Disk sleep mode %s not supported by the kernel, sorry.", strnull(t));
}
return false;
}

View File

@ -180,7 +180,11 @@ int sd_seat_get_active(const char *seat, char **session, uid_t *uid);
/* Return sessions and users on seat. Returns number of sessions.
* If sessions is NULL, this returns only the number of sessions. */
int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids);
int sd_seat_get_sessions(
const char *seat,
char ***ret_sessions,
uid_t **ret_uids,
unsigned *ret_n_uids);
/* Return whether the seat is multi-session capable */
int sd_seat_can_multi_session(const char *seat) _sd_deprecated_;
@ -195,7 +199,7 @@ int sd_seat_can_graphical(const char *seat);
int sd_machine_get_class(const char *machine, char **clazz);
/* Return the list if host-side network interface indices of a machine */
int sd_machine_get_ifindices(const char *machine, int **ifindices);
int sd_machine_get_ifindices(const char *machine, int **ret_ifindices);
/* Get all seats, store in *seats. Returns the number of seats. If
* seats is NULL, this only returns the number of seats. */

View File

@ -11,6 +11,8 @@ static void test_extract_first_word(void) {
const char *p, *original;
char *t;
log_info("/* %s */", __func__);
p = original = "foobar waldo";
assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "foobar"));
@ -387,6 +389,8 @@ static void test_extract_first_word_and_warn(void) {
const char *p, *original;
char *t;
log_info("/* %s */", __func__);
p = original = "foobar waldo";
assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobar"));
@ -531,6 +535,8 @@ static void test_extract_many_words(void) {
const char *p, *original;
char *a, *b, *c, *d, *e, *f;
log_info("/* %s */", __func__);
p = original = "foobar waldi piep";
assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
assert_se(isempty(p));

View File

@ -10,8 +10,9 @@
#include "util.h"
static void test_string_erase(void) {
char *x;
log_info("/* %s */", __func__);
char *x;
x = strdupa("");
assert_se(streq(string_erase(x), ""));
@ -33,17 +34,17 @@ static void test_string_erase(void) {
}
static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
int r;
log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)",
__func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
r = free_and_strndup(t, src, l);
int r = free_and_strndup(t, src, l);
assert_se(streq_ptr(*t, expected));
assert_se(r == change); /* check that change occurs only when necessary */
}
static void test_free_and_strndup(void) {
log_info("/* %s */", __func__);
static const struct test_case {
const char *src;
size_t len;
@ -91,6 +92,7 @@ static void test_free_and_strndup(void) {
}
static void test_ascii_strcasecmp_n(void) {
log_info("/* %s */", __func__);
assert_se(ascii_strcasecmp_n("", "", 0) == 0);
assert_se(ascii_strcasecmp_n("", "", 1) == 0);
@ -118,6 +120,8 @@ static void test_ascii_strcasecmp_n(void) {
}
static void test_ascii_strcasecmp_nn(void) {
log_info("/* %s */", __func__);
assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0);
assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0);
assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0);
@ -137,6 +141,8 @@ static void test_ascii_strcasecmp_nn(void) {
static void test_cellescape(void) {
char buf[40];
log_info("/* %s */", __func__);
assert_se(streq(cellescape(buf, 1, ""), ""));
assert_se(streq(cellescape(buf, 1, "1"), ""));
assert_se(streq(cellescape(buf, 1, "12"), ""));
@ -216,19 +222,24 @@ static void test_cellescape(void) {
}
static void test_streq_ptr(void) {
log_info("/* %s */", __func__);
assert_se(streq_ptr(NULL, NULL));
assert_se(!streq_ptr("abc", "cdef"));
}
static void test_strstrip(void) {
char *r;
char input[] = " hello, waldo. ";
log_info("/* %s */", __func__);
r = strstrip(input);
assert_se(streq(r, "hello, waldo."));
char *ret, input[] = " hello, waldo. ";
ret = strstrip(input);
assert_se(streq(ret, "hello, waldo."));
}
static void test_strextend(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *str = NULL;
assert_se(strextend(&str, NULL));
@ -240,6 +251,8 @@ static void test_strextend(void) {
}
static void test_strextend_with_separator(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *str = NULL;
assert_se(strextend_with_separator(&str, NULL, NULL));
@ -263,6 +276,8 @@ static void test_strextend_with_separator(void) {
}
static void test_strrep(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *one, *three, *zero;
one = strrep("waldo", 1);
three = strrep("waldo", 3);
@ -288,11 +303,15 @@ static void test_string_has_cc(void) {
}
static void test_ascii_strlower(void) {
log_info("/* %s */", __func__);
char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK";
assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk"));
}
static void test_strshorten(void) {
log_info("/* %s */", __func__);
char s[] = "foobar";
assert_se(strlen(strshorten(s, 6)) == 6);
@ -302,6 +321,8 @@ static void test_strshorten(void) {
}
static void test_strjoina(void) {
log_info("/* %s */", __func__);
char *actual;
actual = strjoina("", "foo", "bar");
@ -359,6 +380,8 @@ static void test_strjoin(void) {
}
static void test_strcmp_ptr(void) {
log_info("/* %s */", __func__);
assert_se(strcmp_ptr(NULL, NULL) == 0);
assert_se(strcmp_ptr("", NULL) > 0);
assert_se(strcmp_ptr("foo", NULL) > 0);
@ -371,26 +394,36 @@ static void test_strcmp_ptr(void) {
}
static void test_foreach_word(void) {
const char *word, *state;
size_t l;
int i = 0;
const char test[] = "test abc d\te f ";
log_info("/* %s */", __func__);
const char *test = "test abc d\te f ";
const char * const expected[] = {
"test",
"abc",
"d",
"e",
"f",
"",
NULL
};
FOREACH_WORD(word, l, test, state)
assert_se(strneq(expected[i++], word, l));
size_t i = 0;
int r;
for (const char *p = test;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r == 0) {
assert_se(i == ELEMENTSOF(expected));
break;
}
assert_se(r > 0);
assert_se(streq(expected[i++], word));
}
}
static void check(const char *test, char** expected, bool trailing) {
int i = 0, r;
size_t i = 0;
int r;
printf("<<<%s>>>\n", test);
for (;;) {
@ -412,6 +445,8 @@ static void check(const char *test, char** expected, bool trailing) {
}
static void test_foreach_word_quoted(void) {
log_info("/* %s */", __func__);
check("test a b c 'd' e '' '' hhh '' '' \"a b c\"",
STRV_MAKE("test",
"a",
@ -437,6 +472,8 @@ static void test_foreach_word_quoted(void) {
}
static void test_endswith(void) {
log_info("/* %s */", __func__);
assert_se(endswith("foobar", "bar"));
assert_se(endswith("foobar", ""));
assert_se(endswith("foobar", "foobar"));
@ -447,6 +484,8 @@ static void test_endswith(void) {
}
static void test_endswith_no_case(void) {
log_info("/* %s */", __func__);
assert_se(endswith_no_case("fooBAR", "bar"));
assert_se(endswith_no_case("foobar", ""));
assert_se(endswith_no_case("foobar", "FOOBAR"));
@ -457,6 +496,8 @@ static void test_endswith_no_case(void) {
}
static void test_delete_chars(void) {
log_info("/* %s */", __func__);
char *s, input[] = " hello, waldo. abc";
s = delete_chars(input, WHITESPACE);
@ -465,6 +506,7 @@ static void test_delete_chars(void) {
}
static void test_delete_trailing_chars(void) {
log_info("/* %s */", __func__);
char *s,
input1[] = " \n \r k \n \r ",
@ -489,6 +531,8 @@ static void test_delete_trailing_chars(void) {
}
static void test_delete_trailing_slashes(void) {
log_info("/* %s */", __func__);
char s1[] = "foobar//",
s2[] = "foobar/",
s3[] = "foobar",
@ -502,6 +546,8 @@ static void test_delete_trailing_slashes(void) {
}
static void test_skip_leading_chars(void) {
log_info("/* %s */", __func__);
char input1[] = " \n \r k \n \r ",
input2[] = "kkkkthiskkkiskkkaktestkkk",
input3[] = "abcdef";
@ -514,11 +560,15 @@ static void test_skip_leading_chars(void) {
}
static void test_in_charset(void) {
log_info("/* %s */", __func__);
assert_se(in_charset("dddaaabbbcccc", "abcd"));
assert_se(!in_charset("dddaaabbbcccc", "abc f"));
}
static void test_split_pair(void) {
log_info("/* %s */", __func__);
_cleanup_free_ char *a = NULL, *b = NULL;
assert_se(split_pair("", "", &a, &b) == -EINVAL);
@ -541,6 +591,8 @@ static void test_split_pair(void) {
}
static void test_first_word(void) {
log_info("/* %s */", __func__);
assert_se(first_word("Hello", ""));
assert_se(first_word("Hello", "Hello"));
assert_se(first_word("Hello world", "Hello"));
@ -555,12 +607,16 @@ static void test_first_word(void) {
}
static void test_strlen_ptr(void) {
log_info("/* %s */", __func__);
assert_se(strlen_ptr("foo") == 3);
assert_se(strlen_ptr("") == 0);
assert_se(strlen_ptr(NULL) == 0);
}
static void test_memory_startswith(void) {
log_info("/* %s */", __func__);
assert_se(streq(memory_startswith("", 0, ""), ""));
assert_se(streq(memory_startswith("", 1, ""), ""));
assert_se(streq(memory_startswith("x", 2, ""), "x"));
@ -573,6 +629,8 @@ static void test_memory_startswith(void) {
}
static void test_memory_startswith_no_case(void) {
log_info("/* %s */", __func__);
assert_se(streq(memory_startswith_no_case("", 0, ""), ""));
assert_se(streq(memory_startswith_no_case("", 1, ""), ""));
assert_se(streq(memory_startswith_no_case("x", 2, ""), "x"));
@ -605,6 +663,8 @@ static void test_string_truncate_lines_one(const char *input, size_t n_lines, co
}
static void test_string_truncate_lines(void) {
log_info("/* %s */", __func__);
test_string_truncate_lines_one("", 0, "", false);
test_string_truncate_lines_one("", 1, "", false);
test_string_truncate_lines_one("", 2, "", false);
@ -676,6 +736,8 @@ static void test_string_extract_lines_one(const char *input, size_t i, const cha
}
static void test_string_extract_line(void) {
log_info("/* %s */", __func__);
test_string_extract_lines_one("", 0, "", false);
test_string_extract_lines_one("", 1, "", false);
test_string_extract_lines_one("", 2, "", false);
@ -742,6 +804,88 @@ static void test_string_extract_line(void) {
test_string_extract_lines_one("\n\n\nx\n", 3, "x", false);
}
static void test_string_contains_word_strv(void) {
log_info("/* %s */", __func__);
const char *w;
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), NULL));
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), &w));
assert_se(streq(w, "a"));
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE("d"), &w));
assert_se(w == NULL);
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", "a"), &w));
assert_se(streq(w, "a"));
assert_se(string_contains_word_strv("b a b cc", NULL, STRV_MAKE("b", "a", "b"), &w));
assert_se(streq(w, "b"));
assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", ""), &w));
assert_se(streq(w, "b"));
assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE(""), &w));
assert_se(w == NULL);
assert_se(string_contains_word_strv("a b cc", " ", STRV_MAKE(""), &w));
assert_se(streq(w, ""));
}
static void test_string_contains_word(void) {
log_info("/* %s */", __func__);
assert_se( string_contains_word("a b cc", NULL, "a"));
assert_se( string_contains_word("a b cc", NULL, "b"));
assert_se(!string_contains_word("a b cc", NULL, "c"));
assert_se( string_contains_word("a b cc", NULL, "cc"));
assert_se(!string_contains_word("a b cc", NULL, "d"));
assert_se(!string_contains_word("a b cc", NULL, "a b"));
assert_se(!string_contains_word("a b cc", NULL, "a b c"));
assert_se(!string_contains_word("a b cc", NULL, "b c"));
assert_se(!string_contains_word("a b cc", NULL, "b cc"));
assert_se(!string_contains_word("a b cc", NULL, "a "));
assert_se(!string_contains_word("a b cc", NULL, " b "));
assert_se(!string_contains_word("a b cc", NULL, " cc"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "a"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "b"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "c"));
assert_se( string_contains_word(" a b\t\tcc", NULL, "cc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "d"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b\t\tc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tcc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, "a "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " b "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " cc"));
assert_se(!string_contains_word(" a b\t\tcc", NULL, ""));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " "));
assert_se(!string_contains_word(" a b\t\tcc", NULL, " "));
assert_se( string_contains_word(" a b\t\tcc", " ", ""));
assert_se( string_contains_word(" a b\t\tcc", "\t", ""));
assert_se( string_contains_word(" a b\t\tcc", WHITESPACE, ""));
assert_se( string_contains_word("a:b:cc", ":#", "a"));
assert_se( string_contains_word("a:b:cc", ":#", "b"));
assert_se(!string_contains_word("a:b:cc", ":#", "c"));
assert_se( string_contains_word("a:b:cc", ":#", "cc"));
assert_se(!string_contains_word("a:b:cc", ":#", "d"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:b"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:b:c"));
assert_se(!string_contains_word("a:b:cc", ":#", "b:c"));
assert_se(!string_contains_word("a#b#cc", ":#", "b:cc"));
assert_se( string_contains_word("a#b#cc", ":#", "b"));
assert_se( string_contains_word("a#b#cc", ":#", "cc"));
assert_se(!string_contains_word("a:b:cc", ":#", "a:"));
assert_se(!string_contains_word("a:b cc", ":#", "b"));
assert_se( string_contains_word("a:b cc", ":#", "b cc"));
assert_se(!string_contains_word("a:b:cc", ":#", ":cc"));
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
@ -777,6 +921,8 @@ int main(int argc, char *argv[]) {
test_memory_startswith_no_case();
test_string_truncate_lines();
test_string_extract_line();
test_string_contains_word_strv();
test_string_contains_word();
return 0;
}

View File

@ -100,6 +100,12 @@ static const char* const input_table_quoted[] = {
NULL,
};
static const char* const input_table_quoted_joined[] = {
"one",
" two\t three " " four five",
NULL,
};
static const char* const input_table_one[] = {
"one",
NULL,
@ -232,7 +238,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
log_info("/* %s */", __func__);
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(r == (int) strv_length(list));
assert_se(s);
j = strv_join(s, " | ");
@ -251,7 +257,7 @@ static void test_invalid_unquote(const char *quoted) {
log_info("/* %s */", __func__);
r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(s == NULL);
assert_se(r == -EINVAL);
}
@ -281,47 +287,39 @@ static void test_strv_split(void) {
strv_free_erase(l);
l = strv_split_full(" one two\t three", NULL, 0);
assert_se(l);
assert_se(strv_split_full(&l, " one two\t three", NULL, 0) == 3);
assert_se(strv_equal(l, (char**) input_table_multiple));
strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES);
assert_se(l);
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five'", NULL, EXTRACT_UNQUOTE) == 3);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing last quote ignores the last element. */
l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES);
assert_se(l);
/* missing last quote causes extraction to fail. */
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE) == -EINVAL);
assert_se(!l);
/* missing last quote, but the last element is _not_ ignored with EXTRACT_RELAX. */
assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 3);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */
l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
/* missing separator between items */
assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five'", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 2);
assert_se(strv_equal(l, (char**) input_table_quoted_joined));
strv_free_erase(l);
l = strv_free_erase(l);
/* missing separator between */
l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five", NULL,
EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 2);
assert_se(strv_equal(l, (char**) input_table_quoted_joined));
strv_free_erase(l);
l = strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
strv_free_erase(l);
l = strv_split_full("\\", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 1);
assert_se(strv_equal(l, STRV_MAKE("\\")));
}
@ -333,71 +331,70 @@ static void test_strv_split_empty(void) {
l = strv_split("", WHITESPACE);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split("", NULL);
assert_se(l = strv_split("", NULL));
assert_se(strv_isempty(l));
l = strv_free(l);
assert_se(strv_split_full(&l, "", NULL, 0) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", NULL, 0);
assert_se(strv_split_full(&l, "", NULL, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", NULL, SPLIT_QUOTES);
assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES);
assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split(" ", WHITESPACE);
assert_se(l);
assert_se(strv_isempty(l));
strv_free(l);
l = strv_split(" ", NULL);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, 0);
assert_se(strv_split_full(&l, " ", NULL, 0) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES);
assert_se(strv_split_full(&l, " ", WHITESPACE, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, SPLIT_QUOTES);
assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE) == 0);
assert_se(l);
assert_se(strv_isempty(l));
l = strv_free(l);
strv_free(l);
l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0);
assert_se(l);
assert_se(strv_isempty(l));
}
static void test_strv_split_extract(void) {
static void test_strv_split_full(void) {
_cleanup_strv_free_ char **l = NULL;
const char *str = ":foo\\:bar::waldo:";
int r;
log_info("/* %s */", __func__);
r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
r = strv_split_full(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[0], ""));
assert_se(streq_ptr(l[1], "foo:bar"));
@ -1026,7 +1023,7 @@ int main(int argc, char *argv[]) {
test_strv_split();
test_strv_split_empty();
test_strv_split_extract();
test_strv_split_full();
test_strv_split_colon_pairs();
test_strv_split_newlines();
test_strv_split_nulstr();

View File

@ -109,6 +109,7 @@ UdevBuiltinCommand udev_builtin_lookup(const char *command) {
int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command, bool test) {
_cleanup_strv_free_ char **argv = NULL;
int r;
assert(dev);
assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
@ -117,9 +118,10 @@ int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command
if (!builtins[cmd])
return -EOPNOTSUPP;
argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX);
if (!argv)
return -ENOMEM;
r = strv_split_full(&argv, command, NULL,
EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
/* we need '0' here to reset the internal state */
optind = 0;

View File

@ -747,9 +747,9 @@ int udev_event_spawn(UdevEvent *event,
return log_device_error_errno(event->dev, errno,
"Failed to create pipe for command '%s': %m", cmd);
argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX);
if (!argv)
return log_oom();
r = strv_split_full(&argv, cmd, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return log_device_error_errno(event->dev, r, "Failed to split command: %m");
if (isempty(argv[0]))
return log_device_error_errno(event->dev, SYNTHETIC_ERRNO(EINVAL),

View File

@ -385,9 +385,9 @@ int xdg_autostart_format_exec_start(
* NOTE: Technically, XDG only specifies " as quotes, while this also
* accepts '.
*/
exec_split = strv_split_full(exec, WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
if (!exec_split)
return -ENOMEM;
r = strv_split_full(&exec_split, exec, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX);
if (r < 0)
return r;
if (strv_isempty(exec_split))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty");