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