]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-bus: add sd_bus_path_{encode,decode}_many() 1392/head
authorDavid Herrmann <dh.herrmann@gmail.com>
Fri, 25 Sep 2015 17:05:23 +0000 (19:05 +0200)
committerDavid Herrmann <dh.herrmann@gmail.com>
Sat, 26 Sep 2015 14:57:23 +0000 (16:57 +0200)
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.

Makefile-man.am
man/sd_bus_path_encode.xml
src/libsystemd/libsystemd.sym
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/systemd/sd-bus.h

index 3b8038611b7e96fd448914af2b5695fe828ce19a..7dd014116f22f6814a3e11e8759529dc41176c49 100644 (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)
 
index 21c22a8f7c7261ee2009411e1152dcafcb73fee0..696dfd00ba89a11e3e9ca825dda3196f7d4159a5 100644 (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>
         <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>
 
     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>
index 518cbbb7ed498ecd3536c1e1d62be9ce68a5e803..843a1e9880da5fb41c3eb6b48c4af2e7ccd6683e 100644 (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;
index 53d1c6f61db93e43f91c56d587dbd1375c3319bf..3310d3859d64178242b8c148e753ff8336c54024 100644 (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;
 
index b203707f2711abde59f6b371251afcb9a6cede8e..ff6bba5988dec355c3ae8cd3215a5fb32f543226 100644 (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;
 }
index 0883203ae7e62ebde179d2fc1f13be2563ad10b2..43cf247cdfdd29260f45daf88801f5f6b49ad11f 100644 (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 */