]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/escape.c
util: introduce memcmp_safe()
[thirdparty/systemd.git] / src / basic / escape.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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"
4f5dd394 11#include "utf8.h"
4f5dd394 12
b778252b
ZJS
13int 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
a5ef3638 78char *cescape_length(const char *s, size_t n) {
4f5dd394 79 const char *f;
a5ef3638 80 char *r, *t;
4f5dd394 81
a5ef3638 82 assert(s || n == 0);
4f5dd394
LP
83
84 /* Does C style string escaping. May be reversed with
85 * cunescape(). */
86
a5ef3638 87 r = new(char, n*4 + 1);
4f5dd394
LP
88 if (!r)
89 return NULL;
90
a5ef3638 91 for (f = s, t = r; f < s + n; f++)
4f5dd394
LP
92 t += cescape_char(*f, t);
93
94 *t = 0;
95
96 return r;
97}
98
a5ef3638
LP
99char *cescape(const char *s) {
100 assert(s);
101
102 return cescape_length(s, strlen(s));
103}
104
c932fb71 105int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
4f5dd394
LP
106 int r = 1;
107
108 assert(p);
109 assert(*p);
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
LP
117
118 if (length != (size_t) -1 && length < 1)
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
163 if (length != (size_t) -1 && length < 3)
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 */
175 if (a == 0 && b == 0)
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': {
185 /* C++11 style 16bit unicode */
186
187 int a[4];
da6053d0 188 size_t i;
4f5dd394
LP
189 uint32_t c;
190
191 if (length != (size_t) -1 && length < 5)
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 */
203 if (c == 0)
204 return -EINVAL;
205
3565e095 206 *ret = c;
4f5dd394
LP
207 r = 5;
208 break;
209 }
210
211 case 'U': {
212 /* C++11 style 32bit unicode */
213
214 int a[8];
da6053d0 215 size_t i;
c932fb71 216 char32_t c;
4f5dd394
LP
217
218 if (length != (size_t) -1 && length < 9)
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 */
231 if (c == 0)
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
LP
254
255 if (length != (size_t) -1 && length < 3)
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 */
271 if (a == 0 && b == 0 && c == 0)
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
292int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
293 char *r, *t;
294 const char *f;
295 size_t pl;
296
297 assert(s);
298 assert(ret);
299
300 /* Undoes C style string escaping, and optionally prefixes it. */
301
7bf7ce28 302 pl = strlen_ptr(prefix);
4f5dd394
LP
303
304 r = new(char, pl+length+1);
305 if (!r)
306 return -ENOMEM;
307
308 if (prefix)
309 memcpy(r, prefix, pl);
310
311 for (f = s, t = r + pl; f < s + length; f++) {
312 size_t remaining;
3565e095 313 bool eight_bit = false;
c932fb71 314 char32_t u;
4f5dd394
LP
315 int k;
316
317 remaining = s + length - f;
318 assert(remaining > 0);
319
320 if (*f != '\\') {
629ff674 321 /* A literal, copy verbatim */
4f5dd394
LP
322 *(t++) = *f;
323 continue;
324 }
325
326 if (remaining == 1) {
327 if (flags & UNESCAPE_RELAX) {
328 /* A trailing backslash, copy verbatim */
329 *(t++) = *f;
330 continue;
331 }
332
333 free(r);
334 return -EINVAL;
335 }
336
3565e095 337 k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
4f5dd394
LP
338 if (k < 0) {
339 if (flags & UNESCAPE_RELAX) {
340 /* Invalid escape code, let's take it literal then */
341 *(t++) = '\\';
342 continue;
343 }
344
345 free(r);
346 return k;
347 }
348
3565e095
ZJS
349 f += k;
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
360 *ret = r;
361 return t - r;
362}
363
364int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
365 return cunescape_length_with_prefix(s, length, NULL, flags, ret);
366}
367
368int cunescape(const char *s, UnescapeFlags flags, char **ret) {
369 return cunescape_length(s, strlen(s), flags, ret);
370}
371
372char *xescape(const char *s, const char *bad) {
373 char *r, *t;
374 const char *f;
375
376 /* Escapes all chars in bad, in addition to \ and all special
377 * chars, in \xFF style escaping. May be reversed with
378 * cunescape(). */
379
380 r = new(char, strlen(s) * 4 + 1);
381 if (!r)
382 return NULL;
383
384 for (f = s, t = r; *f; f++) {
385
386 if ((*f < ' ') || (*f >= 127) ||
387 (*f == '\\') || strchr(bad, *f)) {
388 *(t++) = '\\';
389 *(t++) = 'x';
390 *(t++) = hexchar(*f >> 4);
391 *(t++) = hexchar(*f);
392 } else
393 *(t++) = *f;
394 }
395
396 *t = 0;
397
398 return r;
399}
400
95052df3
ZJS
401char *octescape(const char *s, size_t len) {
402 char *r, *t;
403 const char *f;
404
405 /* Escapes all chars in bad, in addition to \ and " chars,
406 * in \nnn style escaping. */
407
408 r = new(char, len * 4 + 1);
409 if (!r)
410 return NULL;
411
412 for (f = s, t = r; f < s + len; f++) {
413
4c701096 414 if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
95052df3
ZJS
415 *(t++) = '\\';
416 *(t++) = '0' + (*f >> 6);
417 *(t++) = '0' + ((*f >> 3) & 8);
418 *(t++) = '0' + (*f & 8);
419 } else
420 *(t++) = *f;
421 }
422
423 *t = 0;
424
425 return r;
426
427}
428
804ee07c 429static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
4f5dd394
LP
430 assert(bad);
431
432 for (; *s; s++) {
804ee07c
ZJS
433 if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
434 *(t++) = '\\';
435 *(t++) = *s == '\n' ? 'n' : 't';
436 continue;
437 }
438
4f5dd394
LP
439 if (*s == '\\' || strchr(bad, *s))
440 *(t++) = '\\';
441
442 *(t++) = *s;
443 }
444
445 return t;
446}
447
448char *shell_escape(const char *s, const char *bad) {
449 char *r, *t;
450
451 r = new(char, strlen(s)*2+1);
452 if (!r)
453 return NULL;
454
804ee07c 455 t = strcpy_backslash_escaped(r, s, bad, false);
4f5dd394
LP
456 *t = 0;
457
458 return r;
459}
460
804ee07c 461char* shell_maybe_quote(const char *s, EscapeStyle style) {
4f5dd394
LP
462 const char *p;
463 char *r, *t;
464
465 assert(s);
466
804ee07c
ZJS
467 /* Encloses a string in quotes if necessary to make it OK as a shell
468 * string. Note that we treat benign UTF-8 characters as needing
469 * escaping too, but that should be OK. */
4f5dd394
LP
470
471 for (p = s; *p; p++)
472 if (*p <= ' ' ||
473 *p >= 127 ||
474 strchr(SHELL_NEED_QUOTES, *p))
475 break;
476
477 if (!*p)
478 return strdup(s);
479
804ee07c 480 r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
4f5dd394
LP
481 if (!r)
482 return NULL;
483
484 t = r;
804ee07c
ZJS
485 if (style == ESCAPE_BACKSLASH)
486 *(t++) = '"';
487 else if (style == ESCAPE_POSIX) {
488 *(t++) = '$';
489 *(t++) = '\'';
490 } else
491 assert_not_reached("Bad EscapeStyle");
492
4f5dd394
LP
493 t = mempcpy(t, s, p - s);
494
804ee07c
ZJS
495 if (style == ESCAPE_BACKSLASH)
496 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
497 else
498 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
4f5dd394 499
804ee07c
ZJS
500 if (style == ESCAPE_BACKSLASH)
501 *(t++) = '"';
502 else
503 *(t++) = '\'';
4f5dd394
LP
504 *t = 0;
505
506 return r;
507}