]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
45e0b1f6 RC |
2 | |
3 | #include "alloc-util.h" | |
4 | #include "escape.h" | |
5 | #include "macro.h" | |
6d7c4033 | 6 | #include "tests.h" |
45e0b1f6 | 7 | |
4f7452a8 | 8 | TEST(cescape) { |
3d41b6b8 | 9 | _cleanup_free_ char *t = NULL; |
45e0b1f6 | 10 | |
70d55819 ZJS |
11 | assert_se(t = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313")); |
12 | assert_se(streq(t, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313")); | |
13 | } | |
14 | ||
4f7452a8 | 15 | TEST(xescape) { |
3d41b6b8 | 16 | _cleanup_free_ char *t = NULL; |
70d55819 ZJS |
17 | |
18 | assert_se(t = xescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "")); | |
19 | assert_se(streq(t, "abc\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb")); | |
20 | } | |
21 | ||
4f7452a8 | 22 | static void test_xescape_full_one(bool eight_bits) { |
70d55819 ZJS |
23 | const char* escaped = !eight_bits ? |
24 | "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\\x7f\\x9c\\xcb" : | |
25 | "a\\x62c\\x5c\"\\x08\\x0c\\x0a\\x0d\\x09\\x0b\\x07\\x03\177\234\313"; | |
26 | const unsigned full_fit = !eight_bits ? 55 : 46; | |
b19f2116 | 27 | XEscapeFlags flags = eight_bits * XESCAPE_8_BIT; |
70d55819 | 28 | |
5ca75ec9 ZJS |
29 | log_info("/* %s */", __func__); |
30 | ||
70d55819 | 31 | for (unsigned i = 0; i < 60; i++) { |
3d41b6b8 | 32 | _cleanup_free_ char *t = NULL, *q = NULL; |
70d55819 | 33 | |
b19f2116 | 34 | assert_se(t = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, flags)); |
70d55819 | 35 | |
c0f86d66 | 36 | log_info("%02u: <%s>", i, t); |
70d55819 ZJS |
37 | |
38 | if (i >= full_fit) | |
39 | assert_se(streq(t, escaped)); | |
40 | else if (i >= 3) { | |
30fd9a2d | 41 | /* We need up to four columns, so up to three columns may be wasted */ |
70d55819 ZJS |
42 | assert_se(strlen(t) == i || strlen(t) == i - 1 || strlen(t) == i - 2 || strlen(t) == i - 3); |
43 | assert_se(strneq(t, escaped, i - 3) || strneq(t, escaped, i - 4) || | |
44 | strneq(t, escaped, i - 5) || strneq(t, escaped, i - 6)); | |
45 | assert_se(endswith(t, "...")); | |
46 | } else { | |
47 | assert_se(strlen(t) == i); | |
48 | assert_se(strneq(t, "...", i)); | |
49 | } | |
fc96e5c0 ZJS |
50 | |
51 | assert_se(q = xescape_full("abc\\\"\b\f\n\r\t\v\a\003\177\234\313", "b", i, | |
52 | flags | XESCAPE_FORCE_ELLIPSIS)); | |
53 | ||
c0f86d66 | 54 | log_info("%02u: <%s>", i, q); |
fc96e5c0 ZJS |
55 | if (i > 0) |
56 | assert_se(endswith(q, ".")); | |
f21b863e YW |
57 | assert_se(strlen(q) <= i); |
58 | assert_se(strlen(q) + 3 >= strlen(t)); | |
70d55819 | 59 | } |
45e0b1f6 RC |
60 | } |
61 | ||
ec9d3fc5 | 62 | TEST(xescape_full) { |
4f7452a8 JJ |
63 | test_xescape_full_one(false); |
64 | test_xescape_full_one(true); | |
65 | } | |
45e0b1f6 | 66 | |
4f7452a8 | 67 | TEST(cunescape) { |
3d41b6b8 | 68 | _cleanup_free_ char *unescaped = NULL; |
5ca75ec9 | 69 | |
45e0b1f6 RC |
70 | assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0); |
71 | assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0); | |
72 | assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00")); | |
73 | unescaped = mfree(unescaped); | |
74 | ||
75 | /* incomplete sequences */ | |
76 | assert_se(cunescape("\\x0", 0, &unescaped) < 0); | |
77 | assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0); | |
78 | assert_se(streq_ptr(unescaped, "\\x0")); | |
79 | unescaped = mfree(unescaped); | |
80 | ||
81 | assert_se(cunescape("\\x", 0, &unescaped) < 0); | |
82 | assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0); | |
83 | assert_se(streq_ptr(unescaped, "\\x")); | |
84 | unescaped = mfree(unescaped); | |
85 | ||
86 | assert_se(cunescape("\\", 0, &unescaped) < 0); | |
87 | assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0); | |
88 | assert_se(streq_ptr(unescaped, "\\")); | |
89 | unescaped = mfree(unescaped); | |
90 | ||
91 | assert_se(cunescape("\\11", 0, &unescaped) < 0); | |
92 | assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0); | |
93 | assert_se(streq_ptr(unescaped, "\\11")); | |
94 | unescaped = mfree(unescaped); | |
95 | ||
96 | assert_se(cunescape("\\1", 0, &unescaped) < 0); | |
97 | assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0); | |
98 | assert_se(streq_ptr(unescaped, "\\1")); | |
99 | unescaped = mfree(unescaped); | |
100 | ||
101 | assert_se(cunescape("\\u0000", 0, &unescaped) < 0); | |
102 | assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0); | |
103 | assert_se(streq_ptr(unescaped, "ßßΠA")); | |
104 | unescaped = mfree(unescaped); | |
105 | ||
106 | assert_se(cunescape("\\073", 0, &unescaped) >= 0); | |
107 | assert_se(streq_ptr(unescaped, ";")); | |
a096d8c8 ZJS |
108 | unescaped = mfree(unescaped); |
109 | ||
110 | assert_se(cunescape("A=A\\\\x0aB", 0, &unescaped) >= 0); | |
111 | assert_se(streq_ptr(unescaped, "A=A\\x0aB")); | |
112 | unescaped = mfree(unescaped); | |
113 | ||
114 | assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0); | |
115 | assert_se(streq_ptr(unescaped, "A=A\\x0aB")); | |
a6a36dea YW |
116 | unescaped = mfree(unescaped); |
117 | ||
118 | assert_se(cunescape("\\x00\\x00\\x00", UNESCAPE_ACCEPT_NUL, &unescaped) == 3); | |
119 | assert_se(memcmp(unescaped, "\0\0\0", 3) == 0); | |
120 | unescaped = mfree(unescaped); | |
121 | ||
122 | assert_se(cunescape("\\u0000\\u0000\\u0000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3); | |
123 | assert_se(memcmp(unescaped, "\0\0\0", 3) == 0); | |
124 | unescaped = mfree(unescaped); | |
125 | ||
126 | assert_se(cunescape("\\U00000000\\U00000000\\U00000000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3); | |
127 | assert_se(memcmp(unescaped, "\0\0\0", 3) == 0); | |
128 | unescaped = mfree(unescaped); | |
129 | ||
130 | assert_se(cunescape("\\000\\000\\000", UNESCAPE_ACCEPT_NUL, &unescaped) == 3); | |
131 | assert_se(memcmp(unescaped, "\0\0\0", 3) == 0); | |
45e0b1f6 RC |
132 | } |
133 | ||
134 | static void test_shell_escape_one(const char *s, const char *bad, const char *expected) { | |
3d41b6b8 | 135 | _cleanup_free_ char *r = NULL; |
45e0b1f6 RC |
136 | |
137 | assert_se(r = shell_escape(s, bad)); | |
0089ab08 | 138 | log_debug("%s → %s (expected %s)", s, r, expected); |
45e0b1f6 RC |
139 | assert_se(streq_ptr(r, expected)); |
140 | } | |
141 | ||
4f7452a8 | 142 | TEST(shell_escape) { |
45e0b1f6 RC |
143 | test_shell_escape_one("", "", ""); |
144 | test_shell_escape_one("\\", "", "\\\\"); | |
145 | test_shell_escape_one("foobar", "", "foobar"); | |
146 | test_shell_escape_one("foobar", "o", "f\\o\\obar"); | |
147 | test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz"); | |
566d06ae | 148 | test_shell_escape_one("foo\nbar\nbaz", ",:", "foo\\nbar\\nbaz"); |
45e0b1f6 RC |
149 | } |
150 | ||
9e53c10a | 151 | static void test_shell_maybe_quote_one(const char *s, ShellEscapeFlags flags, const char *expected) { |
804ee07c ZJS |
152 | _cleanup_free_ char *ret = NULL; |
153 | ||
9e53c10a | 154 | assert_se(ret = shell_maybe_quote(s, flags)); |
804ee07c ZJS |
155 | log_debug("[%s] → [%s] (%s)", s, ret, expected); |
156 | assert_se(streq(ret, expected)); | |
45e0b1f6 RC |
157 | } |
158 | ||
4f7452a8 | 159 | TEST(shell_maybe_quote) { |
9e53c10a | 160 | test_shell_maybe_quote_one("", 0, ""); |
1129cd8a | 161 | test_shell_maybe_quote_one("", SHELL_ESCAPE_EMPTY, "\"\""); |
9e53c10a | 162 | test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX, ""); |
1129cd8a | 163 | test_shell_maybe_quote_one("", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "\"\""); |
9e53c10a ZJS |
164 | test_shell_maybe_quote_one("\\", 0, "\"\\\\\""); |
165 | test_shell_maybe_quote_one("\\", SHELL_ESCAPE_POSIX, "$'\\\\'"); | |
166 | test_shell_maybe_quote_one("\"", 0, "\"\\\"\""); | |
167 | test_shell_maybe_quote_one("\"", SHELL_ESCAPE_POSIX, "$'\"'"); | |
168 | test_shell_maybe_quote_one("foobar", 0, "foobar"); | |
169 | test_shell_maybe_quote_one("foobar", SHELL_ESCAPE_POSIX, "foobar"); | |
170 | test_shell_maybe_quote_one("foo bar", 0, "\"foo bar\""); | |
171 | test_shell_maybe_quote_one("foo bar", SHELL_ESCAPE_POSIX, "$'foo bar'"); | |
172 | test_shell_maybe_quote_one("foo\tbar", 0, "\"foo\\tbar\""); | |
173 | test_shell_maybe_quote_one("foo\tbar", SHELL_ESCAPE_POSIX, "$'foo\\tbar'"); | |
174 | test_shell_maybe_quote_one("foo\nbar", 0, "\"foo\\nbar\""); | |
175 | test_shell_maybe_quote_one("foo\nbar", SHELL_ESCAPE_POSIX, "$'foo\\nbar'"); | |
176 | test_shell_maybe_quote_one("foo \"bar\" waldo", 0, "\"foo \\\"bar\\\" waldo\""); | |
177 | test_shell_maybe_quote_one("foo \"bar\" waldo", SHELL_ESCAPE_POSIX, "$'foo \"bar\" waldo'"); | |
178 | test_shell_maybe_quote_one("foo$bar", 0, "\"foo\\$bar\""); | |
1129cd8a | 179 | test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_EMPTY, "\"foo\\$bar\""); |
9e53c10a | 180 | test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX, "$'foo$bar'"); |
1129cd8a | 181 | test_shell_maybe_quote_one("foo$bar", SHELL_ESCAPE_POSIX | SHELL_ESCAPE_EMPTY, "$'foo$bar'"); |
804ee07c | 182 | |
0089ab08 | 183 | /* Exclamation mark is special in the interactive shell, but we don't treat it so. */ |
9e53c10a ZJS |
184 | test_shell_maybe_quote_one("foo!bar", 0, "\"foo!bar\""); |
185 | test_shell_maybe_quote_one("foo!bar", SHELL_ESCAPE_POSIX, "$'foo!bar'"); | |
0089ab08 ZJS |
186 | |
187 | /* Control characters and unicode */ | |
188 | test_shell_maybe_quote_one("a\nb\001", 0, "\"a\\nb\\001\""); | |
189 | test_shell_maybe_quote_one("a\nb\001", SHELL_ESCAPE_POSIX, "$'a\\nb\\001'"); | |
190 | ||
191 | test_shell_maybe_quote_one("głąb", 0, "głąb"); | |
192 | test_shell_maybe_quote_one("głąb", SHELL_ESCAPE_POSIX, "głąb"); | |
193 | ||
194 | test_shell_maybe_quote_one("głąb\002\003", 0, "\"głąb\\002\\003\""); | |
195 | test_shell_maybe_quote_one("głąb\002\003", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003'"); | |
196 | ||
197 | test_shell_maybe_quote_one("głąb\002\003rząd", 0, "\"głąb\\002\\003rząd\""); | |
198 | test_shell_maybe_quote_one("głąb\002\003rząd", SHELL_ESCAPE_POSIX, "$'głąb\\002\\003rząd'"); | |
582843ee | 199 | |
200 | /* Bogus UTF-8 strings */ | |
201 | test_shell_maybe_quote_one("\250\350", 0, "\"\\250\\350\""); | |
202 | test_shell_maybe_quote_one("\250\350", SHELL_ESCAPE_POSIX, "$'\\250\\350'"); | |
45e0b1f6 RC |
203 | } |
204 | ||
eeb91d29 | 205 | static void test_quote_command_line_one(char **argv, const char *expected) { |
3d41b6b8 | 206 | _cleanup_free_ char *s = NULL; |
eeb91d29 | 207 | |
4ef15008 | 208 | assert_se(s = quote_command_line(argv, SHELL_ESCAPE_EMPTY)); |
eeb91d29 ZJS |
209 | log_info("%s", s); |
210 | assert_se(streq(s, expected)); | |
211 | } | |
212 | ||
4f7452a8 | 213 | TEST(quote_command_line) { |
eeb91d29 ZJS |
214 | test_quote_command_line_one(STRV_MAKE("true", "true"), |
215 | "true true"); | |
216 | test_quote_command_line_one(STRV_MAKE("true", "with a space"), | |
217 | "true \"with a space\""); | |
218 | test_quote_command_line_one(STRV_MAKE("true", "with a 'quote'"), | |
219 | "true \"with a 'quote'\""); | |
220 | test_quote_command_line_one(STRV_MAKE("true", "with a \"quote\""), | |
221 | "true \"with a \\\"quote\\\"\""); | |
222 | test_quote_command_line_one(STRV_MAKE("true", "$dollar"), | |
223 | "true \"\\$dollar\""); | |
224 | } | |
225 | ||
4f438c63 | 226 | static void test_octescape_one(const char *s, const char *expected) { |
3d41b6b8 | 227 | _cleanup_free_ char *ret = NULL; |
4f438c63 YW |
228 | |
229 | assert_se(ret = octescape(s, strlen_ptr(s))); | |
230 | log_debug("octescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected); | |
231 | assert_se(streq(ret, expected)); | |
232 | } | |
233 | ||
234 | TEST(octescape) { | |
235 | test_octescape_one(NULL, ""); | |
236 | test_octescape_one("", ""); | |
237 | test_octescape_one("foo", "foo"); | |
238 | test_octescape_one("\"\\\"", "\\042\\134\\042"); | |
239 | test_octescape_one("\123\213\222", "\123\\213\\222"); | |
240 | } | |
241 | ||
4f7452a8 | 242 | DEFINE_TEST_MAIN(LOG_DEBUG); |