]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
4f5dd394 | 2 | |
11c3a366 TA |
3 | #include <errno.h> |
4 | #include <stdlib.h> | |
5 | #include <string.h> | |
6 | ||
b5efdb8a | 7 | #include "alloc-util.h" |
e4e73a63 LP |
8 | #include "escape.h" |
9 | #include "hexdecoct.h" | |
11c3a366 | 10 | #include "macro.h" |
eeb91d29 | 11 | #include "strv.h" |
4f5dd394 | 12 | #include "utf8.h" |
4f5dd394 | 13 | |
b778252b ZJS |
14 | int cescape_char(char c, char *buf) { |
15 | char *buf_old = buf; | |
4f5dd394 | 16 | |
76a35973 LP |
17 | /* Needs space for 4 characters in the buffer */ |
18 | ||
4f5dd394 LP |
19 | switch (c) { |
20 | ||
21 | case '\a': | |
22 | *(buf++) = '\\'; | |
23 | *(buf++) = 'a'; | |
24 | break; | |
25 | case '\b': | |
26 | *(buf++) = '\\'; | |
27 | *(buf++) = 'b'; | |
28 | break; | |
29 | case '\f': | |
30 | *(buf++) = '\\'; | |
31 | *(buf++) = 'f'; | |
32 | break; | |
33 | case '\n': | |
34 | *(buf++) = '\\'; | |
35 | *(buf++) = 'n'; | |
36 | break; | |
37 | case '\r': | |
38 | *(buf++) = '\\'; | |
39 | *(buf++) = 'r'; | |
40 | break; | |
41 | case '\t': | |
42 | *(buf++) = '\\'; | |
43 | *(buf++) = 't'; | |
44 | break; | |
45 | case '\v': | |
46 | *(buf++) = '\\'; | |
47 | *(buf++) = 'v'; | |
48 | break; | |
49 | case '\\': | |
50 | *(buf++) = '\\'; | |
51 | *(buf++) = '\\'; | |
52 | break; | |
53 | case '"': | |
54 | *(buf++) = '\\'; | |
55 | *(buf++) = '"'; | |
56 | break; | |
57 | case '\'': | |
58 | *(buf++) = '\\'; | |
59 | *(buf++) = '\''; | |
60 | break; | |
61 | ||
62 | default: | |
63 | /* For special chars we prefer octal over | |
64 | * hexadecimal encoding, simply because glib's | |
65 | * g_strescape() does the same */ | |
66 | if ((c < ' ') || (c >= 127)) { | |
67 | *(buf++) = '\\'; | |
68 | *(buf++) = octchar((unsigned char) c >> 6); | |
69 | *(buf++) = octchar((unsigned char) c >> 3); | |
70 | *(buf++) = octchar((unsigned char) c); | |
71 | } else | |
72 | *(buf++) = c; | |
73 | break; | |
74 | } | |
75 | ||
76 | return buf - buf_old; | |
77 | } | |
78 | ||
31be0e9e | 79 | char* cescape_length(const char *s, size_t n) { |
4f5dd394 | 80 | const char *f; |
a5ef3638 | 81 | char *r, *t; |
4f5dd394 | 82 | |
a5ef3638 | 83 | assert(s || n == 0); |
4f5dd394 LP |
84 | |
85 | /* Does C style string escaping. May be reversed with | |
86 | * cunescape(). */ | |
87 | ||
a5ef3638 | 88 | r = new(char, n*4 + 1); |
4f5dd394 LP |
89 | if (!r) |
90 | return NULL; | |
91 | ||
a5ef3638 | 92 | for (f = s, t = r; f < s + n; f++) |
4f5dd394 LP |
93 | t += cescape_char(*f, t); |
94 | ||
95 | *t = 0; | |
96 | ||
97 | return r; | |
98 | } | |
99 | ||
31be0e9e | 100 | char* cescape(const char *s) { |
a5ef3638 LP |
101 | assert(s); |
102 | ||
103 | return cescape_length(s, strlen(s)); | |
104 | } | |
105 | ||
0e72e469 | 106 | int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) { |
4f5dd394 LP |
107 | int r = 1; |
108 | ||
109 | assert(p); | |
4f5dd394 LP |
110 | assert(ret); |
111 | ||
3565e095 ZJS |
112 | /* Unescapes C style. Returns the unescaped character in ret. |
113 | * Sets *eight_bit to true if the escaped sequence either fits in | |
114 | * one byte in UTF-8 or is a non-unicode literal byte and should | |
115 | * instead be copied directly. | |
116 | */ | |
4f5dd394 | 117 | |
f5fbe71d | 118 | if (length != SIZE_MAX && length < 1) |
4f5dd394 LP |
119 | return -EINVAL; |
120 | ||
121 | switch (p[0]) { | |
122 | ||
123 | case 'a': | |
124 | *ret = '\a'; | |
125 | break; | |
126 | case 'b': | |
127 | *ret = '\b'; | |
128 | break; | |
129 | case 'f': | |
130 | *ret = '\f'; | |
131 | break; | |
132 | case 'n': | |
133 | *ret = '\n'; | |
134 | break; | |
135 | case 'r': | |
136 | *ret = '\r'; | |
137 | break; | |
138 | case 't': | |
139 | *ret = '\t'; | |
140 | break; | |
141 | case 'v': | |
142 | *ret = '\v'; | |
143 | break; | |
144 | case '\\': | |
145 | *ret = '\\'; | |
146 | break; | |
147 | case '"': | |
148 | *ret = '"'; | |
149 | break; | |
150 | case '\'': | |
151 | *ret = '\''; | |
152 | break; | |
153 | ||
154 | case 's': | |
155 | /* This is an extension of the XDG syntax files */ | |
156 | *ret = ' '; | |
157 | break; | |
158 | ||
159 | case 'x': { | |
160 | /* hexadecimal encoding */ | |
161 | int a, b; | |
162 | ||
f5fbe71d | 163 | if (length != SIZE_MAX && length < 3) |
4f5dd394 LP |
164 | return -EINVAL; |
165 | ||
166 | a = unhexchar(p[1]); | |
167 | if (a < 0) | |
168 | return -EINVAL; | |
169 | ||
170 | b = unhexchar(p[2]); | |
171 | if (b < 0) | |
172 | return -EINVAL; | |
173 | ||
174 | /* Don't allow NUL bytes */ | |
0e72e469 | 175 | if (a == 0 && b == 0 && !accept_nul) |
4f5dd394 LP |
176 | return -EINVAL; |
177 | ||
3565e095 ZJS |
178 | *ret = (a << 4U) | b; |
179 | *eight_bit = true; | |
4f5dd394 LP |
180 | r = 3; |
181 | break; | |
182 | } | |
183 | ||
184 | case 'u': { | |
da890466 | 185 | /* C++11 style 16-bit unicode */ |
4f5dd394 LP |
186 | |
187 | int a[4]; | |
da6053d0 | 188 | size_t i; |
4f5dd394 LP |
189 | uint32_t c; |
190 | ||
f5fbe71d | 191 | if (length != SIZE_MAX && length < 5) |
4f5dd394 LP |
192 | return -EINVAL; |
193 | ||
194 | for (i = 0; i < 4; i++) { | |
195 | a[i] = unhexchar(p[1 + i]); | |
196 | if (a[i] < 0) | |
197 | return a[i]; | |
198 | } | |
199 | ||
200 | c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; | |
201 | ||
202 | /* Don't allow 0 chars */ | |
0e72e469 | 203 | if (c == 0 && !accept_nul) |
4f5dd394 LP |
204 | return -EINVAL; |
205 | ||
3565e095 | 206 | *ret = c; |
4f5dd394 LP |
207 | r = 5; |
208 | break; | |
209 | } | |
210 | ||
211 | case 'U': { | |
da890466 | 212 | /* C++11 style 32-bit unicode */ |
4f5dd394 LP |
213 | |
214 | int a[8]; | |
da6053d0 | 215 | size_t i; |
c932fb71 | 216 | char32_t c; |
4f5dd394 | 217 | |
f5fbe71d | 218 | if (length != SIZE_MAX && length < 9) |
4f5dd394 LP |
219 | return -EINVAL; |
220 | ||
221 | for (i = 0; i < 8; i++) { | |
222 | a[i] = unhexchar(p[1 + i]); | |
223 | if (a[i] < 0) | |
224 | return a[i]; | |
225 | } | |
226 | ||
dcd12626 LP |
227 | c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | |
228 | ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; | |
4f5dd394 LP |
229 | |
230 | /* Don't allow 0 chars */ | |
0e72e469 | 231 | if (c == 0 && !accept_nul) |
4f5dd394 LP |
232 | return -EINVAL; |
233 | ||
234 | /* Don't allow invalid code points */ | |
235 | if (!unichar_is_valid(c)) | |
236 | return -EINVAL; | |
237 | ||
3565e095 | 238 | *ret = c; |
4f5dd394 LP |
239 | r = 9; |
240 | break; | |
241 | } | |
242 | ||
243 | case '0': | |
244 | case '1': | |
245 | case '2': | |
246 | case '3': | |
247 | case '4': | |
248 | case '5': | |
249 | case '6': | |
250 | case '7': { | |
251 | /* octal encoding */ | |
252 | int a, b, c; | |
c932fb71 | 253 | char32_t m; |
4f5dd394 | 254 | |
f5fbe71d | 255 | if (length != SIZE_MAX && length < 3) |
4f5dd394 LP |
256 | return -EINVAL; |
257 | ||
258 | a = unoctchar(p[0]); | |
259 | if (a < 0) | |
260 | return -EINVAL; | |
261 | ||
262 | b = unoctchar(p[1]); | |
263 | if (b < 0) | |
264 | return -EINVAL; | |
265 | ||
266 | c = unoctchar(p[2]); | |
267 | if (c < 0) | |
268 | return -EINVAL; | |
269 | ||
270 | /* don't allow NUL bytes */ | |
0e72e469 | 271 | if (a == 0 && b == 0 && c == 0 && !accept_nul) |
4f5dd394 LP |
272 | return -EINVAL; |
273 | ||
274 | /* Don't allow bytes above 255 */ | |
dcd12626 | 275 | m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; |
4f5dd394 LP |
276 | if (m > 255) |
277 | return -EINVAL; | |
278 | ||
279 | *ret = m; | |
3565e095 | 280 | *eight_bit = true; |
4f5dd394 LP |
281 | r = 3; |
282 | break; | |
283 | } | |
284 | ||
285 | default: | |
286 | return -EINVAL; | |
287 | } | |
288 | ||
289 | return r; | |
290 | } | |
291 | ||
e437538f | 292 | ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { |
ddedf7ca ZJS |
293 | _cleanup_free_ char *ans = NULL; |
294 | char *t; | |
4f5dd394 LP |
295 | const char *f; |
296 | size_t pl; | |
ddedf7ca | 297 | int r; |
4f5dd394 LP |
298 | |
299 | assert(s); | |
300 | assert(ret); | |
301 | ||
302 | /* Undoes C style string escaping, and optionally prefixes it. */ | |
303 | ||
7bf7ce28 | 304 | pl = strlen_ptr(prefix); |
4f5dd394 | 305 | |
ddedf7ca ZJS |
306 | ans = new(char, pl+length+1); |
307 | if (!ans) | |
4f5dd394 LP |
308 | return -ENOMEM; |
309 | ||
310 | if (prefix) | |
ddedf7ca | 311 | memcpy(ans, prefix, pl); |
4f5dd394 | 312 | |
ddedf7ca | 313 | for (f = s, t = ans + pl; f < s + length; f++) { |
4f5dd394 | 314 | size_t remaining; |
3565e095 | 315 | bool eight_bit = false; |
c932fb71 | 316 | char32_t u; |
4f5dd394 LP |
317 | |
318 | remaining = s + length - f; | |
319 | assert(remaining > 0); | |
320 | ||
321 | if (*f != '\\') { | |
629ff674 | 322 | /* A literal, copy verbatim */ |
4f5dd394 LP |
323 | *(t++) = *f; |
324 | continue; | |
325 | } | |
326 | ||
327 | if (remaining == 1) { | |
328 | if (flags & UNESCAPE_RELAX) { | |
329 | /* A trailing backslash, copy verbatim */ | |
330 | *(t++) = *f; | |
331 | continue; | |
332 | } | |
333 | ||
4f5dd394 LP |
334 | return -EINVAL; |
335 | } | |
336 | ||
ddedf7ca ZJS |
337 | r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL); |
338 | if (r < 0) { | |
4f5dd394 LP |
339 | if (flags & UNESCAPE_RELAX) { |
340 | /* Invalid escape code, let's take it literal then */ | |
341 | *(t++) = '\\'; | |
342 | continue; | |
343 | } | |
344 | ||
ddedf7ca | 345 | return r; |
4f5dd394 LP |
346 | } |
347 | ||
ddedf7ca | 348 | f += r; |
3565e095 ZJS |
349 | if (eight_bit) |
350 | /* One byte? Set directly as specified */ | |
351 | *(t++) = u; | |
4f5dd394 | 352 | else |
3565e095 | 353 | /* Otherwise encode as multi-byte UTF-8 */ |
4f5dd394 | 354 | t += utf8_encode_unichar(t, u); |
4f5dd394 LP |
355 | } |
356 | ||
357 | *t = 0; | |
358 | ||
1421705d | 359 | assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */ |
ddedf7ca ZJS |
360 | *ret = TAKE_PTR(ans); |
361 | return t - *ret; | |
4f5dd394 LP |
362 | } |
363 | ||
b19f2116 | 364 | char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) { |
70d55819 | 365 | char *ans, *t, *prev, *prev2; |
4f5dd394 LP |
366 | const char *f; |
367 | ||
70d55819 | 368 | /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be |
b19f2116 ZJS |
369 | * reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through |
370 | * unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings. | |
70d55819 | 371 | * |
fc96e5c0 ZJS |
372 | * If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is |
373 | * appended. */ | |
4f5dd394 | 374 | |
70d55819 ZJS |
375 | if (console_width == 0) |
376 | return strdup(""); | |
377 | ||
378 | ans = new(char, MIN(strlen(s), console_width) * 4 + 1); | |
379 | if (!ans) | |
4f5dd394 LP |
380 | return NULL; |
381 | ||
70d55819 ZJS |
382 | memset(ans, '_', MIN(strlen(s), console_width) * 4); |
383 | ans[MIN(strlen(s), console_width) * 4] = 0; | |
384 | ||
fc96e5c0 ZJS |
385 | bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS); |
386 | ||
70d55819 ZJS |
387 | for (f = s, t = prev = prev2 = ans; ; f++) { |
388 | char *tmp_t = t; | |
389 | ||
390 | if (!*f) { | |
fc96e5c0 ZJS |
391 | if (force_ellipsis) |
392 | break; | |
393 | ||
70d55819 ZJS |
394 | *t = 0; |
395 | return ans; | |
396 | } | |
397 | ||
b19f2116 ZJS |
398 | if ((unsigned char) *f < ' ' || |
399 | (!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) || | |
70d55819 | 400 | *f == '\\' || strchr(bad, *f)) { |
fc96e5c0 | 401 | if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width) |
70d55819 | 402 | break; |
4f5dd394 | 403 | |
4f5dd394 LP |
404 | *(t++) = '\\'; |
405 | *(t++) = 'x'; | |
406 | *(t++) = hexchar(*f >> 4); | |
407 | *(t++) = hexchar(*f); | |
70d55819 | 408 | } else { |
fc96e5c0 | 409 | if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width) |
70d55819 ZJS |
410 | break; |
411 | ||
4f5dd394 | 412 | *(t++) = *f; |
70d55819 | 413 | } |
4f5dd394 | 414 | |
70d55819 ZJS |
415 | /* We might need to go back two cycles to fit three dots, so remember two positions */ |
416 | prev2 = prev; | |
417 | prev = tmp_t; | |
418 | } | |
4f5dd394 | 419 | |
70d55819 ZJS |
420 | /* We can just write where we want, since chars are one-byte */ |
421 | size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */ | |
422 | size_t off; | |
423 | if (console_width - c >= (size_t) (t - ans)) | |
424 | off = (size_t) (t - ans); | |
425 | else if (console_width - c >= (size_t) (prev - ans)) | |
426 | off = (size_t) (prev - ans); | |
427 | else if (console_width - c >= (size_t) (prev2 - ans)) | |
428 | off = (size_t) (prev2 - ans); | |
429 | else | |
430 | off = console_width - c; | |
431 | assert(off <= (size_t) (t - ans)); | |
432 | ||
433 | memcpy(ans + off, "...", c); | |
434 | ans[off + c] = '\0'; | |
435 | return ans; | |
4f5dd394 LP |
436 | } |
437 | ||
b19f2116 ZJS |
438 | char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) { |
439 | if (FLAGS_SET(flags, XESCAPE_8_BIT)) | |
440 | return xescape_full(str, "", console_width, flags); | |
e3b4efd2 | 441 | else |
fc96e5c0 ZJS |
442 | return utf8_escape_non_printable_full(str, |
443 | console_width, | |
444 | FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS)); | |
e3b4efd2 ZJS |
445 | } |
446 | ||
31be0e9e | 447 | char* octescape(const char *s, size_t len) { |
76519cec | 448 | char *buf, *t; |
95052df3 | 449 | |
76519cec | 450 | /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */ |
95052df3 | 451 | |
76519cec YW |
452 | assert(s || len == 0); |
453 | ||
c6342e35 LP |
454 | if (len == SIZE_MAX) |
455 | len = strlen(s); | |
456 | ||
60cf4059 | 457 | if (len > (SIZE_MAX - 1) / 4) |
c6342e35 LP |
458 | return NULL; |
459 | ||
76519cec YW |
460 | t = buf = new(char, len * 4 + 1); |
461 | if (!buf) | |
95052df3 ZJS |
462 | return NULL; |
463 | ||
76519cec YW |
464 | for (size_t i = 0; i < len; i++) { |
465 | uint8_t u = (uint8_t) s[i]; | |
95052df3 | 466 | |
76519cec | 467 | if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) { |
95052df3 | 468 | *(t++) = '\\'; |
76519cec YW |
469 | *(t++) = '0' + (u >> 6); |
470 | *(t++) = '0' + ((u >> 3) & 7); | |
471 | *(t++) = '0' + (u & 7); | |
95052df3 | 472 | } else |
76519cec | 473 | *(t++) = u; |
95052df3 ZJS |
474 | } |
475 | ||
476 | *t = 0; | |
76519cec | 477 | return buf; |
95052df3 ZJS |
478 | } |
479 | ||
b699f5f2 RP |
480 | char* decescape(const char *s, const char *bad, size_t len) { |
481 | char *buf, *t; | |
482 | ||
483 | /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */ | |
484 | ||
485 | assert(s || len == 0); | |
486 | ||
487 | t = buf = new(char, len * 4 + 1); | |
488 | if (!buf) | |
489 | return NULL; | |
490 | ||
491 | for (size_t i = 0; i < len; i++) { | |
492 | uint8_t u = (uint8_t) s[i]; | |
493 | ||
494 | if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) { | |
495 | *(t++) = '\\'; | |
496 | *(t++) = '0' + (u / 100); | |
497 | *(t++) = '0' + ((u / 10) % 10); | |
498 | *(t++) = '0' + (u % 10); | |
499 | } else | |
500 | *(t++) = u; | |
501 | } | |
502 | ||
503 | *t = 0; | |
504 | return buf; | |
505 | } | |
506 | ||
566d06ae | 507 | static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) { |
4f5dd394 | 508 | assert(bad); |
0b82a6fa | 509 | assert(t); |
510 | assert(s); | |
4f5dd394 | 511 | |
00f57157 | 512 | while (*s) { |
513 | int l = utf8_encoded_valid_unichar(s, SIZE_MAX); | |
514 | ||
515 | if (char_is_cc(*s) || l < 0) | |
516 | t += cescape_char(*(s++), t); | |
517 | else if (l == 1) { | |
0089ab08 ZJS |
518 | if (*s == '\\' || strchr(bad, *s)) |
519 | *(t++) = '\\'; | |
00f57157 | 520 | *(t++) = *(s++); |
521 | } else { | |
522 | t = mempcpy(t, s, l); | |
523 | s += l; | |
804ee07c | 524 | } |
00f57157 | 525 | } |
804ee07c | 526 | |
4f5dd394 LP |
527 | return t; |
528 | } | |
529 | ||
31be0e9e | 530 | char* shell_escape(const char *s, const char *bad) { |
0089ab08 | 531 | char *buf, *t; |
4f5dd394 | 532 | |
0089ab08 ZJS |
533 | buf = new(char, strlen(s)*4+1); |
534 | if (!buf) | |
4f5dd394 LP |
535 | return NULL; |
536 | ||
0089ab08 | 537 | t = strcpy_backslash_escaped(buf, s, bad); |
4f5dd394 LP |
538 | *t = 0; |
539 | ||
0089ab08 | 540 | return buf; |
4f5dd394 LP |
541 | } |
542 | ||
9e53c10a | 543 | char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) { |
4f5dd394 | 544 | const char *p; |
0089ab08 | 545 | char *buf, *t; |
4f5dd394 LP |
546 | |
547 | assert(s); | |
548 | ||
0089ab08 | 549 | /* Encloses a string in quotes if necessary to make it OK as a shell string. */ |
4f5dd394 | 550 | |
1129cd8a ZJS |
551 | if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s)) |
552 | return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */ | |
553 | ||
00f57157 | 554 | for (p = s; *p; ) { |
555 | int l = utf8_encoded_valid_unichar(p, SIZE_MAX); | |
556 | ||
557 | if (char_is_cc(*p) || l < 0 || | |
0089ab08 | 558 | strchr(WHITESPACE SHELL_NEED_QUOTES, *p)) |
4f5dd394 LP |
559 | break; |
560 | ||
00f57157 | 561 | p += l; |
562 | } | |
563 | ||
4f5dd394 LP |
564 | if (!*p) |
565 | return strdup(s); | |
566 | ||
0089ab08 ZJS |
567 | buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1); |
568 | if (!buf) | |
4f5dd394 LP |
569 | return NULL; |
570 | ||
0089ab08 | 571 | t = buf; |
9e53c10a | 572 | if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) { |
804ee07c ZJS |
573 | *(t++) = '$'; |
574 | *(t++) = '\''; | |
9e53c10a ZJS |
575 | } else |
576 | *(t++) = '"'; | |
804ee07c | 577 | |
4f5dd394 LP |
578 | t = mempcpy(t, s, p - s); |
579 | ||
9e53c10a | 580 | t = strcpy_backslash_escaped(t, p, |
566d06ae | 581 | FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE); |
4f5dd394 | 582 | |
9e53c10a | 583 | if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) |
804ee07c | 584 | *(t++) = '\''; |
9e53c10a ZJS |
585 | else |
586 | *(t++) = '"'; | |
4f5dd394 LP |
587 | *t = 0; |
588 | ||
0089ab08 | 589 | return str_realloc(buf); |
4f5dd394 | 590 | } |
eeb91d29 | 591 | |
4ef15008 | 592 | char* quote_command_line(char **argv, ShellEscapeFlags flags) { |
eeb91d29 ZJS |
593 | _cleanup_free_ char *result = NULL; |
594 | ||
595 | assert(argv); | |
596 | ||
eeb91d29 ZJS |
597 | STRV_FOREACH(a, argv) { |
598 | _cleanup_free_ char *t = NULL; | |
599 | ||
4ef15008 | 600 | t = shell_maybe_quote(*a, flags); |
eeb91d29 ZJS |
601 | if (!t) |
602 | return NULL; | |
603 | ||
604 | if (!strextend_with_separator(&result, " ", t)) | |
605 | return NULL; | |
606 | } | |
607 | ||
7d0cede0 | 608 | return str_realloc(TAKE_PTR(result)); |
eeb91d29 | 609 | } |