1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
12 #include "alloc-util.h"
14 #include "hexdecoct.h"
18 int cescape_char(char c, char *buf) {
21 /* Needs space for 4 characters in the buffer */
67 /* For special chars we prefer octal over
68 * hexadecimal encoding, simply because glib's
69 * g_strescape() does the same */
70 if ((c < ' ') || (c >= 127)) {
72 *(buf++) = octchar((unsigned char) c >> 6);
73 *(buf++) = octchar((unsigned char) c >> 3);
74 *(buf++) = octchar((unsigned char) c);
83 char *cescape_length(const char *s, size_t n) {
89 /* Does C style string escaping. May be reversed with
92 r = new(char, n*4 + 1);
96 for (f = s, t = r; f < s + n; f++)
97 t += cescape_char(*f, t);
104 char *cescape(const char *s) {
107 return cescape_length(s, strlen(s));
110 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
117 /* Unescapes C style. Returns the unescaped character in ret.
118 * Sets *eight_bit to true if the escaped sequence either fits in
119 * one byte in UTF-8 or is a non-unicode literal byte and should
120 * instead be copied directly.
123 if (length != (size_t) -1 && length < 1)
160 /* This is an extension of the XDG syntax files */
165 /* hexadecimal encoding */
168 if (length != (size_t) -1 && length < 3)
179 /* Don't allow NUL bytes */
180 if (a == 0 && b == 0)
183 *ret = (a << 4U) | b;
190 /* C++11 style 16bit unicode */
196 if (length != (size_t) -1 && length < 5)
199 for (i = 0; i < 4; i++) {
200 a[i] = unhexchar(p[1 + i]);
205 c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
207 /* Don't allow 0 chars */
217 /* C++11 style 32bit unicode */
223 if (length != (size_t) -1 && length < 9)
226 for (i = 0; i < 8; i++) {
227 a[i] = unhexchar(p[1 + i]);
232 c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
233 ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
235 /* Don't allow 0 chars */
239 /* Don't allow invalid code points */
240 if (!unichar_is_valid(c))
260 if (length != (size_t) -1 && length < 3)
275 /* don't allow NUL bytes */
276 if (a == 0 && b == 0 && c == 0)
279 /* Don't allow bytes above 255 */
280 m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
297 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
305 /* Undoes C style string escaping, and optionally prefixes it. */
307 pl = strlen_ptr(prefix);
309 r = new(char, pl+length+1);
314 memcpy(r, prefix, pl);
316 for (f = s, t = r + pl; f < s + length; f++) {
318 bool eight_bit = false;
322 remaining = s + length - f;
323 assert(remaining > 0);
326 /* A literal, copy verbatim */
331 if (remaining == 1) {
332 if (flags & UNESCAPE_RELAX) {
333 /* A trailing backslash, copy verbatim */
342 k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
344 if (flags & UNESCAPE_RELAX) {
345 /* Invalid escape code, let's take it literal then */
356 /* One byte? Set directly as specified */
359 /* Otherwise encode as multi-byte UTF-8 */
360 t += utf8_encode_unichar(t, u);
369 int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
370 return cunescape_length_with_prefix(s, length, NULL, flags, ret);
373 int cunescape(const char *s, UnescapeFlags flags, char **ret) {
374 return cunescape_length(s, strlen(s), flags, ret);
377 char *xescape(const char *s, const char *bad) {
381 /* Escapes all chars in bad, in addition to \ and all special
382 * chars, in \xFF style escaping. May be reversed with
385 r = new(char, strlen(s) * 4 + 1);
389 for (f = s, t = r; *f; f++) {
391 if ((*f < ' ') || (*f >= 127) ||
392 (*f == '\\') || strchr(bad, *f)) {
395 *(t++) = hexchar(*f >> 4);
396 *(t++) = hexchar(*f);
406 #if 0 /// UNNEEDED by elogind
407 char *octescape(const char *s, size_t len) {
411 /* Escapes all chars in bad, in addition to \ and " chars,
412 * in \nnn style escaping. */
414 r = new(char, len * 4 + 1);
418 for (f = s, t = r; f < s + len; f++) {
420 if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
422 *(t++) = '0' + (*f >> 6);
423 *(t++) = '0' + ((*f >> 3) & 8);
424 *(t++) = '0' + (*f & 8);
435 static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
439 if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
441 *(t++) = *s == '\n' ? 'n' : 't';
445 if (*s == '\\' || strchr(bad, *s))
454 char *shell_escape(const char *s, const char *bad) {
457 r = new(char, strlen(s)*2+1);
461 t = strcpy_backslash_escaped(r, s, bad, false);
467 char* shell_maybe_quote(const char *s, EscapeStyle style) {
473 /* Encloses a string in quotes if necessary to make it OK as a shell
474 * string. Note that we treat benign UTF-8 characters as needing
475 * escaping too, but that should be OK. */
480 strchr(SHELL_NEED_QUOTES, *p))
486 r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
491 if (style == ESCAPE_BACKSLASH)
493 else if (style == ESCAPE_POSIX) {
497 assert_not_reached("Bad EscapeStyle");
499 t = mempcpy(t, s, p - s);
501 if (style == ESCAPE_BACKSLASH)
502 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
504 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
506 if (style == ESCAPE_BACKSLASH)