From e82f30d17f3dd8d4209b8eec3c0c6ab529e9c182 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 21 Nov 2017 17:52:31 +0100 Subject: [PATCH] specifier: add helper for escaping '%' characters to avoid making them subject for expansion This is ultimately just a wrapper around strreplace(), but it makes things a bit more self-descriptive. --- src/shared/specifier.c | 30 +++++++++++++++++++++ src/shared/specifier.h | 8 ++++++ src/test/meson.build | 4 +++ src/test/test-specifier.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 src/test/test-specifier.c diff --git a/src/shared/specifier.c b/src/shared/specifier.c index b0f00db..dc7be0a9 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -32,6 +32,7 @@ #include "macro.h" #include "specifier.h" #include "string-util.h" +#include "strv.h" /* * Generic infrastructure for replacing %x style specifiers in @@ -191,3 +192,32 @@ int specifier_kernel_release(char specifier, void *data, void *userdata, char ** *ret = n; return 0; } + +int specifier_escape_strv(char **l, char ***ret) { + char **z, **p, **q; + + assert(ret); + + if (strv_isempty(l)) { + *ret = NULL; + return 0; + } + + z = new(char*, strv_length(l)+1); + if (!z) + return -ENOMEM; + + for (p = l, q = z; *p; p++, q++) { + + *q = specifier_escape(*p); + if (!*q) { + strv_free(z); + return -ENOMEM; + } + } + + *q = NULL; + *ret = z; + + return 0; +} diff --git a/src/shared/specifier.h b/src/shared/specifier.h index 5d2b859..0401e13 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -20,6 +20,8 @@ along with systemd; If not, see . ***/ +#include "string-util.h" + typedef int (*SpecifierCallback)(char specifier, void *data, void *userdata, char **ret); typedef struct Specifier { @@ -36,3 +38,9 @@ int specifier_machine_id(char specifier, void *data, void *userdata, char **ret) int specifier_boot_id(char specifier, void *data, void *userdata, char **ret); int specifier_host_name(char specifier, void *data, void *userdata, char **ret); int specifier_kernel_release(char specifier, void *data, void *userdata, char **ret); + +static inline char* specifier_escape(const char *string) { + return strreplace(string, "%", "%%"); +} + +int specifier_escape_strv(char **l, char ***ret); diff --git a/src/test/meson.build b/src/test/meson.build index 9a8d620..efaa259 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -249,6 +249,10 @@ tests += [ [], []], + [['src/test/test-specifier.c'], + [], + []], + [['src/test/test-string-util.c'], [], []], diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c new file mode 100644 index 0000000..6bf3120 --- /dev/null +++ b/src/test/test-specifier.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "alloc-util.h" +#include "log.h" +#include "specifier.h" +#include "string-util.h" +#include "strv.h" + +static void test_specifier_escape_one(const char *a, const char *b) { + _cleanup_free_ char *x = NULL; + + x = specifier_escape(a); + assert_se(streq_ptr(x, b)); +} + +static void test_specifier_escape(void) { + test_specifier_escape_one(NULL, NULL); + test_specifier_escape_one("", ""); + test_specifier_escape_one("%", "%%"); + test_specifier_escape_one("foo bar", "foo bar"); + test_specifier_escape_one("foo%bar", "foo%%bar"); + test_specifier_escape_one("%%%%%", "%%%%%%%%%%"); +} + +static void test_specifier_escape_strv_one(char **a, char **b) { + _cleanup_strv_free_ char **x = NULL; + + assert_se(specifier_escape_strv(a, &x) >= 0); + assert_se(strv_equal(x, b)); +} + +static void test_specifier_escape_strv(void) { + test_specifier_escape_strv_one(NULL, NULL); + test_specifier_escape_strv_one(STRV_MAKE(NULL), STRV_MAKE(NULL)); + test_specifier_escape_strv_one(STRV_MAKE(""), STRV_MAKE("")); + test_specifier_escape_strv_one(STRV_MAKE("foo"), STRV_MAKE("foo")); + test_specifier_escape_strv_one(STRV_MAKE("%"), STRV_MAKE("%%")); + test_specifier_escape_strv_one(STRV_MAKE("foo", "%", "foo%", "%foo", "foo%foo", "quux", "%%%"), STRV_MAKE("foo", "%%", "foo%%", "%%foo", "foo%%foo", "quux", "%%%%%%")); +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + + test_specifier_escape(); + test_specifier_escape_strv(); + + return 0; +} -- 2.7.4