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