mirror of
https://github.com/systemd/systemd.git
synced 2025-03-14 04:58:28 +03:00
sd-bus: add sd_bus_path_{encode,decode}_many()
This introduces two new helpers alongside sd_bus_path_{encode,decode}(), which work similarly to their counterparts, but accept a format-string as input. This allows encoding and decoding multiple labels of a format string at the same time.
This commit is contained in:
parent
2e396de8b2
commit
dfb815c36d
@ -292,6 +292,8 @@ MANPAGES_ALIAS += \
|
||||
man/sd_bus_open_system_remote.3 \
|
||||
man/sd_bus_open_user.3 \
|
||||
man/sd_bus_path_decode.3 \
|
||||
man/sd_bus_path_decode_many.3 \
|
||||
man/sd_bus_path_encode_many.3 \
|
||||
man/sd_bus_ref.3 \
|
||||
man/sd_bus_release_name.3 \
|
||||
man/sd_bus_unref.3 \
|
||||
@ -578,6 +580,8 @@ man/sd_bus_open_system_machine.3: man/sd_bus_default.3
|
||||
man/sd_bus_open_system_remote.3: man/sd_bus_default.3
|
||||
man/sd_bus_open_user.3: man/sd_bus_default.3
|
||||
man/sd_bus_path_decode.3: man/sd_bus_path_encode.3
|
||||
man/sd_bus_path_decode_many.3: man/sd_bus_path_encode.3
|
||||
man/sd_bus_path_encode_many.3: man/sd_bus_path_encode.3
|
||||
man/sd_bus_ref.3: man/sd_bus_new.3
|
||||
man/sd_bus_release_name.3: man/sd_bus_request_name.3
|
||||
man/sd_bus_unref.3: man/sd_bus_new.3
|
||||
@ -1124,6 +1128,12 @@ man/sd_bus_open_user.html: man/sd_bus_default.html
|
||||
man/sd_bus_path_decode.html: man/sd_bus_path_encode.html
|
||||
$(html-alias)
|
||||
|
||||
man/sd_bus_path_decode_many.html: man/sd_bus_path_encode.html
|
||||
$(html-alias)
|
||||
|
||||
man/sd_bus_path_encode_many.html: man/sd_bus_path_encode.html
|
||||
$(html-alias)
|
||||
|
||||
man/sd_bus_ref.html: man/sd_bus_new.html
|
||||
$(html-alias)
|
||||
|
||||
|
@ -44,7 +44,9 @@
|
||||
|
||||
<refnamediv>
|
||||
<refname>sd_bus_path_encode</refname>
|
||||
<refname>sd_bus_path_encode_many</refname>
|
||||
<refname>sd_bus_path_decode</refname>
|
||||
<refname>sd_bus_path_decode_many</refname>
|
||||
|
||||
<refpurpose>Convert an external identifier into an object path and back</refpurpose>
|
||||
</refnamediv>
|
||||
@ -60,12 +62,26 @@
|
||||
<paramdef>char **<parameter>ret_path</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_bus_path_encode_many</function></funcdef>
|
||||
<paramdef>char **<parameter>out</parameter></paramdef>
|
||||
<paramdef>const char *<parameter>path_template</parameter></paramdef>
|
||||
<paramdef>...</paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_bus_path_decode</function></funcdef>
|
||||
<paramdef>const char *<parameter>path</parameter></paramdef>
|
||||
<paramdef>const char *<parameter>prefix</parameter></paramdef>
|
||||
<paramdef>char **<parameter>ret_external_id</parameter></paramdef>
|
||||
</funcprototype>
|
||||
|
||||
<funcprototype>
|
||||
<funcdef>int <function>sd_bus_path_decode_many</function></funcdef>
|
||||
<paramdef>const char *<parameter>path</parameter></paramdef>
|
||||
<paramdef>const char *<parameter>path_template</parameter></paramdef>
|
||||
<paramdef>...</paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -109,6 +125,28 @@
|
||||
invalid in a bus object path by <literal>_</literal>, followed by a
|
||||
hexadecimal value. As a special case, the empty string will be
|
||||
replaced by a lone <literal>_</literal>.</para>
|
||||
|
||||
<para><function>sd_bus_path_encode_many()</function> works like
|
||||
its counterpart <function>sd_bus_path_encode()</function>, but
|
||||
takes a path-template as argument and encodes multiple labels
|
||||
according to its embedded directives. For each
|
||||
<literal>%</literal> character found in the template, the caller
|
||||
must provide a string via var-args, which will be encoded and
|
||||
embedded at the position of the <literal>%</literal> character.
|
||||
Any other character in the template is copied verbatim into the
|
||||
encoded path.</para>
|
||||
|
||||
<para><function>sd_bus_path_decode_many()</function> does the
|
||||
reverse of <function>sd_bus_path_encode_many()</function>. It
|
||||
decodes the passed object path, according to the given
|
||||
path-template. For each <literal>%</literal> character in the
|
||||
template, the caller must provide an output storage
|
||||
(<literal>char **</literal>) via var-args. The decoded label
|
||||
will be stored there. Each <literal>%</literal> character will
|
||||
only match the current label. It will never match across labels.
|
||||
Furthermore, only a single such directive is allowed per label.
|
||||
If <literal>NULL</literal> is passed as output storage, the
|
||||
label is verified but not returned to the caller.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -477,4 +477,6 @@ global:
|
||||
LIBSYSTEMD_227 {
|
||||
global:
|
||||
sd_bus_default_flush_close;
|
||||
sd_bus_path_decode_many;
|
||||
sd_bus_path_encode_many;
|
||||
} LIBSYSTEMD_226;
|
||||
|
@ -3454,6 +3454,171 @@ _public_ int sd_bus_path_decode(const char *path, const char *prefix, char **ext
|
||||
return 1;
|
||||
}
|
||||
|
||||
_public_ int sd_bus_path_encode_many(char **out, const char *path_template, ...) {
|
||||
_cleanup_strv_free_ char **labels = NULL;
|
||||
char *path, *path_pos, **label_pos;
|
||||
const char *sep, *template_pos;
|
||||
size_t path_length;
|
||||
va_list list;
|
||||
int r;
|
||||
|
||||
assert_return(out, -EINVAL);
|
||||
assert_return(path_template, -EINVAL);
|
||||
|
||||
path_length = strlen(path_template);
|
||||
|
||||
va_start(list, out);
|
||||
for (sep = strchr(path_template, '%'); sep; sep = strchr(sep + 1, '%')) {
|
||||
const char *arg;
|
||||
char *label;
|
||||
|
||||
arg = va_arg(list, const char *);
|
||||
if (!arg) {
|
||||
va_end(list);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
label = bus_label_escape(arg);
|
||||
if (!label) {
|
||||
va_end(list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = strv_consume(&labels, label);
|
||||
if (r < 0) {
|
||||
va_end(list);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* add label length, but account for the format character */
|
||||
path_length += strlen(label) - 1;
|
||||
}
|
||||
va_end(list);
|
||||
|
||||
path = malloc(path_length + 1);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
path_pos = path;
|
||||
label_pos = labels;
|
||||
|
||||
for (template_pos = path_template; *template_pos; ) {
|
||||
sep = strchrnul(template_pos, '%');
|
||||
path_pos = mempcpy(path_pos, template_pos, sep - template_pos);
|
||||
if (!*sep)
|
||||
break;
|
||||
|
||||
path_pos = stpcpy(path_pos, *label_pos++);
|
||||
template_pos = sep + 1;
|
||||
}
|
||||
|
||||
*path_pos = 0;
|
||||
*out = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_bus_path_decode_many(const char *path, const char *path_template, ...) {
|
||||
_cleanup_strv_free_ char **labels = NULL;
|
||||
const char *template_pos, *path_pos;
|
||||
char **label_pos;
|
||||
va_list list;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* This decodes an object-path based on a template argument. The
|
||||
* template consists of a verbatim path, optionally including special
|
||||
* directives:
|
||||
*
|
||||
* - Each occurrence of '%' in the template matches an arbitrary
|
||||
* substring of a label in the given path. At most one such
|
||||
* directive is allowed per label. For each such directive, the
|
||||
* caller must provide an output parameter (char **) via va_arg. If
|
||||
* NULL is passed, the given label is verified, but not returned.
|
||||
* For each matched label, the *decoded* label is stored in the
|
||||
* passed output argument, and the caller is responsible to free
|
||||
* it. Note that the output arguments are only modified if the
|
||||
* actualy path matched the template. Otherwise, they're left
|
||||
* untouched.
|
||||
*
|
||||
* This function returns <0 on error, 0 if the path does not match the
|
||||
* template, 1 if it matched.
|
||||
*/
|
||||
|
||||
assert_return(path, -EINVAL);
|
||||
assert_return(path_template, -EINVAL);
|
||||
|
||||
path_pos = path;
|
||||
|
||||
for (template_pos = path_template; *template_pos; ) {
|
||||
const char *sep;
|
||||
size_t length;
|
||||
char *label;
|
||||
|
||||
/* verify everything until the next '%' matches verbatim */
|
||||
sep = strchrnul(template_pos, '%');
|
||||
length = sep - template_pos;
|
||||
if (strncmp(path_pos, template_pos, length))
|
||||
return 0;
|
||||
|
||||
path_pos += length;
|
||||
template_pos += length;
|
||||
|
||||
if (!*template_pos)
|
||||
break;
|
||||
|
||||
/* We found the next '%' character. Everything up until here
|
||||
* matched. We now skip ahead to the end of this label and make
|
||||
* sure it matches the tail of the label in the path. Then we
|
||||
* decode the string in-between and save it for later use. */
|
||||
|
||||
++template_pos; /* skip over '%' */
|
||||
|
||||
sep = strchrnul(template_pos, '/');
|
||||
length = sep - template_pos; /* length of suffix to match verbatim */
|
||||
|
||||
/* verify the suffixes match */
|
||||
sep = strchrnul(path_pos, '/');
|
||||
if (sep - path_pos < (ssize_t)length ||
|
||||
strncmp(sep - length, template_pos, length))
|
||||
return 0;
|
||||
|
||||
template_pos += length; /* skip over matched label */
|
||||
length = sep - path_pos - length; /* length of sub-label to decode */
|
||||
|
||||
/* store unescaped label for later use */
|
||||
label = bus_label_unescape_n(path_pos, length);
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&labels, label);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path_pos = sep; /* skip decoded label and suffix */
|
||||
}
|
||||
|
||||
/* end of template must match end of path */
|
||||
if (*path_pos)
|
||||
return 0;
|
||||
|
||||
/* copy the labels over to the caller */
|
||||
va_start(list, path);
|
||||
for (label_pos = labels; label_pos && *label_pos; ++label_pos) {
|
||||
char **arg;
|
||||
|
||||
arg = va_arg(list, char **);
|
||||
if (arg)
|
||||
*arg = *label_pos;
|
||||
else
|
||||
free(*label_pos);
|
||||
}
|
||||
va_end(list);
|
||||
|
||||
free(labels);
|
||||
labels = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_public_ int sd_bus_try_close(sd_bus *bus) {
|
||||
int r;
|
||||
|
||||
|
@ -66,6 +66,36 @@ static void test_bus_path_encode(void) {
|
||||
assert_se(sd_bus_path_decode(e, "/foo/bar", &f) > 0 && streq(f, "foo.bar"));
|
||||
}
|
||||
|
||||
static void test_bus_path_encode_many(void) {
|
||||
_cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *f = NULL;
|
||||
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/prefix/bar", "/prefix/%bar", NULL) == 1);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar", "/prefix/%/suffix", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/prefix/foobar/suffix", "/prefix/%/suffix", &a) == 1 && streq_ptr(a, "foobar"));
|
||||
assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", &b, &c) == 1 && streq_ptr(b, "foo") && streq_ptr(c, "bar"));
|
||||
assert_se(sd_bus_path_decode_many("/prefix/one_foo_two/mid/three_bar_four/suffix", "/prefix/one_%_two/mid/three_%_four/suffix", NULL, &d) == 1 && streq_ptr(d, "bar"));
|
||||
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar", "/foo/bar/%", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar%", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/bar", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%bar", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/bar/suffix") == 1);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%%/suffix", NULL, NULL) == 0); /* multiple '%' are treated verbatim */
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffi", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/suffix", &e) == 1 && streq_ptr(e, "bar"));
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/foo/%/%", NULL, NULL) == 1);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/%", NULL, NULL, NULL) == 1);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%/%/%", NULL, NULL, NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%", NULL, NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/%/", NULL, NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%/", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "/%", NULL) == 0);
|
||||
assert_se(sd_bus_path_decode_many("/foo/bar/suffix", "%", NULL) == 0);
|
||||
|
||||
assert_se(sd_bus_path_encode_many(&f, "/prefix/one_%_two/mid/three_%_four/suffix", "foo", "bar") >= 0 && streq_ptr(f, "/prefix/one_foo_two/mid/three_bar_four/suffix"));
|
||||
}
|
||||
|
||||
static void test_bus_label_escape_one(const char *a, const char *b) {
|
||||
_cleanup_free_ char *t = NULL, *x = NULL, *y = NULL;
|
||||
|
||||
@ -393,6 +423,7 @@ int main(int argc, char *argv[]) {
|
||||
test_bus_label_escape();
|
||||
test_bus_path_encode();
|
||||
test_bus_path_encode_unique();
|
||||
test_bus_path_encode_many();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -420,7 +420,9 @@ int sd_bus_error_add_map(const sd_bus_error_map *map);
|
||||
/* Label escaping */
|
||||
|
||||
int sd_bus_path_encode(const char *prefix, const char *external_id, char **ret_path);
|
||||
int sd_bus_path_encode_many(char **out, const char *path_template, ...);
|
||||
int sd_bus_path_decode(const char *path, const char *prefix, char **ret_external_id);
|
||||
int sd_bus_path_decode_many(const char *path, const char *path_template, ...);
|
||||
|
||||
/* Tracking peers */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user