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:
David Herrmann 2015-09-25 19:05:23 +02:00
parent 2e396de8b2
commit dfb815c36d
6 changed files with 248 additions and 0 deletions

View File

@ -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)

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */