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