_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
         *     actually 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_template);
        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);

        labels = mfree(labels);
        return 1;
}