]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/escape.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 #include "alloc-util.h"
13 int cescape_char(char c
, char *buf
) {
16 /* Needs space for 4 characters in the buffer */
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)) {
67 *(buf
++) = octchar((unsigned char) c
>> 6);
68 *(buf
++) = octchar((unsigned char) c
>> 3);
69 *(buf
++) = octchar((unsigned char) c
);
78 char *cescape_length(const char *s
, size_t n
) {
84 /* Does C style string escaping. May be reversed with
87 r
= new(char, n
*4 + 1);
91 for (f
= s
, t
= r
; f
< s
+ n
; f
++)
92 t
+= cescape_char(*f
, t
);
99 char *cescape(const char *s
) {
102 return cescape_length(s
, strlen(s
));
105 int cunescape_one(const char *p
, size_t length
, char32_t
*ret
, bool *eight_bit
, bool accept_nul
) {
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.
117 if (length
!= (size_t) -1 && length
< 1)
154 /* This is an extension of the XDG syntax files */
159 /* hexadecimal encoding */
162 if (length
!= (size_t) -1 && length
< 3)
173 /* Don't allow NUL bytes */
174 if (a
== 0 && b
== 0 && !accept_nul
)
177 *ret
= (a
<< 4U) | b
;
184 /* C++11 style 16bit unicode */
190 if (length
!= (size_t) -1 && length
< 5)
193 for (i
= 0; i
< 4; i
++) {
194 a
[i
] = unhexchar(p
[1 + i
]);
199 c
= ((uint32_t) a
[0] << 12U) | ((uint32_t) a
[1] << 8U) | ((uint32_t) a
[2] << 4U) | (uint32_t) a
[3];
201 /* Don't allow 0 chars */
202 if (c
== 0 && !accept_nul
)
211 /* C++11 style 32bit unicode */
217 if (length
!= (size_t) -1 && length
< 9)
220 for (i
= 0; i
< 8; i
++) {
221 a
[i
] = unhexchar(p
[1 + i
]);
226 c
= ((uint32_t) a
[0] << 28U) | ((uint32_t) a
[1] << 24U) | ((uint32_t) a
[2] << 20U) | ((uint32_t) a
[3] << 16U) |
227 ((uint32_t) a
[4] << 12U) | ((uint32_t) a
[5] << 8U) | ((uint32_t) a
[6] << 4U) | (uint32_t) a
[7];
229 /* Don't allow 0 chars */
230 if (c
== 0 && !accept_nul
)
233 /* Don't allow invalid code points */
234 if (!unichar_is_valid(c
))
254 if (length
!= (size_t) -1 && length
< 3)
269 /* don't allow NUL bytes */
270 if (a
== 0 && b
== 0 && c
== 0 && !accept_nul
)
273 /* Don't allow bytes above 255 */
274 m
= ((uint32_t) a
<< 6U) | ((uint32_t) b
<< 3U) | (uint32_t) c
;
291 int cunescape_length_with_prefix(const char *s
, size_t length
, const char *prefix
, UnescapeFlags flags
, char **ret
) {
299 /* Undoes C style string escaping, and optionally prefixes it. */
301 pl
= strlen_ptr(prefix
);
303 r
= new(char, pl
+length
+1);
308 memcpy(r
, prefix
, pl
);
310 for (f
= s
, t
= r
+ pl
; f
< s
+ length
; f
++) {
312 bool eight_bit
= false;
316 remaining
= s
+ length
- f
;
317 assert(remaining
> 0);
320 /* A literal, copy verbatim */
325 if (remaining
== 1) {
326 if (flags
& UNESCAPE_RELAX
) {
327 /* A trailing backslash, copy verbatim */
336 k
= cunescape_one(f
+ 1, remaining
- 1, &u
, &eight_bit
, flags
& UNESCAPE_ACCEPT_NUL
);
338 if (flags
& UNESCAPE_RELAX
) {
339 /* Invalid escape code, let's take it literal then */
350 /* One byte? Set directly as specified */
353 /* Otherwise encode as multi-byte UTF-8 */
354 t
+= utf8_encode_unichar(t
, u
);
363 char *xescape_full(const char *s
, const char *bad
, size_t console_width
, bool eight_bits
) {
364 char *ans
, *t
, *prev
, *prev2
;
367 /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
368 * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
369 * This corresponds to non-ASCII printable characters in pre-unicode encodings.
371 * If console_width is reached, output is truncated and "..." is appended. */
373 if (console_width
== 0)
376 ans
= new(char, MIN(strlen(s
), console_width
) * 4 + 1);
380 memset(ans
, '_', MIN(strlen(s
), console_width
) * 4);
381 ans
[MIN(strlen(s
), console_width
) * 4] = 0;
383 for (f
= s
, t
= prev
= prev2
= ans
; ; f
++) {
391 if ((unsigned char) *f
< ' ' || (!eight_bits
&& (unsigned char) *f
>= 127) ||
392 *f
== '\\' || strchr(bad
, *f
)) {
393 if ((size_t) (t
- ans
) + 4 > console_width
)
398 *(t
++) = hexchar(*f
>> 4);
399 *(t
++) = hexchar(*f
);
401 if ((size_t) (t
- ans
) + 1 > console_width
)
407 /* We might need to go back two cycles to fit three dots, so remember two positions */
412 /* We can just write where we want, since chars are one-byte */
413 size_t c
= MIN(console_width
, 3u); /* If the console is too narrow, write fewer dots */
415 if (console_width
- c
>= (size_t) (t
- ans
))
416 off
= (size_t) (t
- ans
);
417 else if (console_width
- c
>= (size_t) (prev
- ans
))
418 off
= (size_t) (prev
- ans
);
419 else if (console_width
- c
>= (size_t) (prev2
- ans
))
420 off
= (size_t) (prev2
- ans
);
422 off
= console_width
- c
;
423 assert(off
<= (size_t) (t
- ans
));
425 memcpy(ans
+ off
, "...", c
);
430 char *escape_non_printable_full(const char *str
, size_t console_width
, bool eight_bit
) {
432 return xescape_full(str
, "", console_width
, true);
434 return utf8_escape_non_printable_full(str
, console_width
);
437 char *octescape(const char *s
, size_t len
) {
441 /* Escapes all chars in bad, in addition to \ and " chars,
442 * in \nnn style escaping. */
444 r
= new(char, len
* 4 + 1);
448 for (f
= s
, t
= r
; f
< s
+ len
; f
++) {
450 if (*f
< ' ' || *f
>= 127 || IN_SET(*f
, '\\', '"')) {
452 *(t
++) = '0' + (*f
>> 6);
453 *(t
++) = '0' + ((*f
>> 3) & 8);
454 *(t
++) = '0' + (*f
& 8);
465 static char *strcpy_backslash_escaped(char *t
, const char *s
, const char *bad
, bool escape_tab_nl
) {
469 if (escape_tab_nl
&& IN_SET(*s
, '\n', '\t')) {
471 *(t
++) = *s
== '\n' ? 'n' : 't';
475 if (*s
== '\\' || strchr(bad
, *s
))
484 char *shell_escape(const char *s
, const char *bad
) {
487 r
= new(char, strlen(s
)*2+1);
491 t
= strcpy_backslash_escaped(r
, s
, bad
, false);
497 char* shell_maybe_quote(const char *s
, EscapeStyle style
) {
503 /* Encloses a string in quotes if necessary to make it OK as a shell
504 * string. Note that we treat benign UTF-8 characters as needing
505 * escaping too, but that should be OK. */
510 strchr(SHELL_NEED_QUOTES
, *p
))
516 r
= new(char, (style
== ESCAPE_POSIX
) + 1 + strlen(s
)*2 + 1 + 1);
522 case ESCAPE_BACKSLASH
:
523 case ESCAPE_BACKSLASH_ONELINE
:
531 assert_not_reached("Bad EscapeStyle");
534 t
= mempcpy(t
, s
, p
- s
);
536 if (IN_SET(style
, ESCAPE_BACKSLASH
, ESCAPE_BACKSLASH_ONELINE
))
537 t
= strcpy_backslash_escaped(t
, p
, SHELL_NEED_ESCAPE
,
538 style
== ESCAPE_BACKSLASH_ONELINE
);
540 t
= strcpy_backslash_escaped(t
, p
, SHELL_NEED_ESCAPE_POSIX
, true);
542 if (IN_SET(style
, ESCAPE_BACKSLASH
, ESCAPE_BACKSLASH_ONELINE
))