1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
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 "strv.h"
12 #include "utf8.h"
13 
cescape_char(char c,char * buf)14 int cescape_char(char c, char *buf) {
15         char *buf_old = buf;
16 
17         /* Needs space for 4 characters in the buffer */
18 
19         switch (c) {
20 
21                 case '\a':
22                         *(buf++) = '\\';
23                         *(buf++) = 'a';
24                         break;
25                 case '\b':
26                         *(buf++) = '\\';
27                         *(buf++) = 'b';
28                         break;
29                 case '\f':
30                         *(buf++) = '\\';
31                         *(buf++) = 'f';
32                         break;
33                 case '\n':
34                         *(buf++) = '\\';
35                         *(buf++) = 'n';
36                         break;
37                 case '\r':
38                         *(buf++) = '\\';
39                         *(buf++) = 'r';
40                         break;
41                 case '\t':
42                         *(buf++) = '\\';
43                         *(buf++) = 't';
44                         break;
45                 case '\v':
46                         *(buf++) = '\\';
47                         *(buf++) = 'v';
48                         break;
49                 case '\\':
50                         *(buf++) = '\\';
51                         *(buf++) = '\\';
52                         break;
53                 case '"':
54                         *(buf++) = '\\';
55                         *(buf++) = '"';
56                         break;
57                 case '\'':
58                         *(buf++) = '\\';
59                         *(buf++) = '\'';
60                         break;
61 
62                 default:
63                         /* For special chars we prefer octal over
64                          * hexadecimal encoding, simply because glib's
65                          * g_strescape() does the same */
66                         if ((c < ' ') || (c >= 127)) {
67                                 *(buf++) = '\\';
68                                 *(buf++) = octchar((unsigned char) c >> 6);
69                                 *(buf++) = octchar((unsigned char) c >> 3);
70                                 *(buf++) = octchar((unsigned char) c);
71                         } else
72                                 *(buf++) = c;
73                         break;
74         }
75 
76         return buf - buf_old;
77 }
78 
cescape_length(const char * s,size_t n)79 char* cescape_length(const char *s, size_t n) {
80         const char *f;
81         char *r, *t;
82 
83         assert(s || n == 0);
84 
85         /* Does C style string escaping. May be reversed with
86          * cunescape(). */
87 
88         r = new(char, n*4 + 1);
89         if (!r)
90                 return NULL;
91 
92         for (f = s, t = r; f < s + n; f++)
93                 t += cescape_char(*f, t);
94 
95         *t = 0;
96 
97         return r;
98 }
99 
cescape(const char * s)100 char* cescape(const char *s) {
101         assert(s);
102 
103         return cescape_length(s, strlen(s));
104 }
105 
cunescape_one(const char * p,size_t length,char32_t * ret,bool * eight_bit,bool accept_nul)106 int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
107         int r = 1;
108 
109         assert(p);
110         assert(ret);
111 
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          */
117 
118         if (length != SIZE_MAX && 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_MAX && 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 && !accept_nul)
176                         return -EINVAL;
177 
178                 *ret = (a << 4U) | b;
179                 *eight_bit = true;
180                 r = 3;
181                 break;
182         }
183 
184         case 'u': {
185                 /* C++11 style 16bit unicode */
186 
187                 int a[4];
188                 size_t i;
189                 uint32_t c;
190 
191                 if (length != SIZE_MAX && 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 && !accept_nul)
204                         return -EINVAL;
205 
206                 *ret = c;
207                 r = 5;
208                 break;
209         }
210 
211         case 'U': {
212                 /* C++11 style 32bit unicode */
213 
214                 int a[8];
215                 size_t i;
216                 char32_t c;
217 
218                 if (length != SIZE_MAX && 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 
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];
229 
230                 /* Don't allow 0 chars */
231                 if (c == 0 && !accept_nul)
232                         return -EINVAL;
233 
234                 /* Don't allow invalid code points */
235                 if (!unichar_is_valid(c))
236                         return -EINVAL;
237 
238                 *ret = c;
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;
253                 char32_t m;
254 
255                 if (length != SIZE_MAX && 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 && !accept_nul)
272                         return -EINVAL;
273 
274                 /* Don't allow bytes above 255 */
275                 m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
276                 if (m > 255)
277                         return -EINVAL;
278 
279                 *ret = m;
280                 *eight_bit = true;
281                 r = 3;
282                 break;
283         }
284 
285         default:
286                 return -EINVAL;
287         }
288 
289         return r;
290 }
291 
cunescape_length_with_prefix(const char * s,size_t length,const char * prefix,UnescapeFlags flags,char ** ret)292 ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
293         _cleanup_free_ char *ans = NULL;
294         char *t;
295         const char *f;
296         size_t pl;
297         int r;
298 
299         assert(s);
300         assert(ret);
301 
302         /* Undoes C style string escaping, and optionally prefixes it. */
303 
304         pl = strlen_ptr(prefix);
305 
306         ans = new(char, pl+length+1);
307         if (!ans)
308                 return -ENOMEM;
309 
310         if (prefix)
311                 memcpy(ans, prefix, pl);
312 
313         for (f = s, t = ans + pl; f < s + length; f++) {
314                 size_t remaining;
315                 bool eight_bit = false;
316                 char32_t u;
317 
318                 remaining = s + length - f;
319                 assert(remaining > 0);
320 
321                 if (*f != '\\') {
322                         /* A literal, copy verbatim */
323                         *(t++) = *f;
324                         continue;
325                 }
326 
327                 if (remaining == 1) {
328                         if (flags & UNESCAPE_RELAX) {
329                                 /* A trailing backslash, copy verbatim */
330                                 *(t++) = *f;
331                                 continue;
332                         }
333 
334                         return -EINVAL;
335                 }
336 
337                 r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
338                 if (r < 0) {
339                         if (flags & UNESCAPE_RELAX) {
340                                 /* Invalid escape code, let's take it literal then */
341                                 *(t++) = '\\';
342                                 continue;
343                         }
344 
345                         return r;
346                 }
347 
348                 f += r;
349                 if (eight_bit)
350                         /* One byte? Set directly as specified */
351                         *(t++) = u;
352                 else
353                         /* Otherwise encode as multi-byte UTF-8 */
354                         t += utf8_encode_unichar(t, u);
355         }
356 
357         *t = 0;
358 
359         assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */
360         *ret = TAKE_PTR(ans);
361         return t - *ret;
362 }
363 
xescape_full(const char * s,const char * bad,size_t console_width,XEscapeFlags flags)364 char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
365         char *ans, *t, *prev, *prev2;
366         const char *f;
367 
368         /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
369          * reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
370          * unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
371          *
372          * If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is
373          * appended. */
374 
375         if (console_width == 0)
376                 return strdup("");
377 
378         ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
379         if (!ans)
380                 return NULL;
381 
382         memset(ans, '_', MIN(strlen(s), console_width) * 4);
383         ans[MIN(strlen(s), console_width) * 4] = 0;
384 
385         bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS);
386 
387         for (f = s, t = prev = prev2 = ans; ; f++) {
388                 char *tmp_t = t;
389 
390                 if (!*f) {
391                         if (force_ellipsis)
392                                 break;
393 
394                         *t = 0;
395                         return ans;
396                 }
397 
398                 if ((unsigned char) *f < ' ' ||
399                     (!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
400                     *f == '\\' || strchr(bad, *f)) {
401                         if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
402                                 break;
403 
404                         *(t++) = '\\';
405                         *(t++) = 'x';
406                         *(t++) = hexchar(*f >> 4);
407                         *(t++) = hexchar(*f);
408                 } else {
409                         if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width)
410                                 break;
411 
412                         *(t++) = *f;
413                 }
414 
415                 /* We might need to go back two cycles to fit three dots, so remember two positions */
416                 prev2 = prev;
417                 prev = tmp_t;
418         }
419 
420         /* We can just write where we want, since chars are one-byte */
421         size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
422         size_t off;
423         if (console_width - c >= (size_t) (t - ans))
424                 off = (size_t) (t - ans);
425         else if (console_width - c >= (size_t) (prev - ans))
426                 off = (size_t) (prev - ans);
427         else if (console_width - c >= (size_t) (prev2 - ans))
428                 off = (size_t) (prev2 - ans);
429         else
430                 off = console_width - c;
431         assert(off <= (size_t) (t - ans));
432 
433         memcpy(ans + off, "...", c);
434         ans[off + c] = '\0';
435         return ans;
436 }
437 
escape_non_printable_full(const char * str,size_t console_width,XEscapeFlags flags)438 char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
439         if (FLAGS_SET(flags, XESCAPE_8_BIT))
440                 return xescape_full(str, "", console_width, flags);
441         else
442                 return utf8_escape_non_printable_full(str,
443                                                       console_width,
444                                                       FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
445 }
446 
octescape(const char * s,size_t len)447 char* octescape(const char *s, size_t len) {
448         char *r, *t;
449         const char *f;
450 
451         /* Escapes all chars in bad, in addition to \ and " chars,
452          * in \nnn style escaping. */
453 
454         r = new(char, len * 4 + 1);
455         if (!r)
456                 return NULL;
457 
458         for (f = s, t = r; f < s + len; f++) {
459 
460                 if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
461                         *(t++) = '\\';
462                         *(t++) = '0' + (*f >> 6);
463                         *(t++) = '0' + ((*f >> 3) & 8);
464                         *(t++) = '0' + (*f & 8);
465                 } else
466                         *(t++) = *f;
467         }
468 
469         *t = 0;
470 
471         return r;
472 
473 }
474 
strcpy_backslash_escaped(char * t,const char * s,const char * bad)475 static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
476         assert(bad);
477 
478         for (; *s; s++)
479                 if (char_is_cc(*s))
480                         t += cescape_char(*s, t);
481                 else {
482                         if (*s == '\\' || strchr(bad, *s))
483                                 *(t++) = '\\';
484                         *(t++) = *s;
485                 }
486 
487         return t;
488 }
489 
shell_escape(const char * s,const char * bad)490 char* shell_escape(const char *s, const char *bad) {
491         char *buf, *t;
492 
493         buf = new(char, strlen(s)*4+1);
494         if (!buf)
495                 return NULL;
496 
497         t = strcpy_backslash_escaped(buf, s, bad);
498         *t = 0;
499 
500         return buf;
501 }
502 
shell_maybe_quote(const char * s,ShellEscapeFlags flags)503 char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
504         const char *p;
505         char *buf, *t;
506 
507         assert(s);
508 
509         /* Encloses a string in quotes if necessary to make it OK as a shell string. */
510 
511         if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s))
512                 return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */
513 
514         for (p = s; *p; p++)
515                 if (char_is_cc(*p) ||
516                     strchr(WHITESPACE SHELL_NEED_QUOTES, *p))
517                         break;
518 
519         if (!*p)
520                 return strdup(s);
521 
522         buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1);
523         if (!buf)
524                 return NULL;
525 
526         t = buf;
527         if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) {
528                 *(t++) = '$';
529                 *(t++) = '\'';
530         } else
531                 *(t++) = '"';
532 
533         t = mempcpy(t, s, p - s);
534 
535         t = strcpy_backslash_escaped(t, p,
536                                      FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE);
537 
538         if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX))
539                 *(t++) = '\'';
540         else
541                 *(t++) = '"';
542         *t = 0;
543 
544         return str_realloc(buf);
545 }
546 
quote_command_line(char ** argv,ShellEscapeFlags flags)547 char* quote_command_line(char **argv, ShellEscapeFlags flags) {
548         _cleanup_free_ char *result = NULL;
549 
550         assert(argv);
551 
552         STRV_FOREACH(a, argv) {
553                 _cleanup_free_ char *t = NULL;
554 
555                 t = shell_maybe_quote(*a, flags);
556                 if (!t)
557                         return NULL;
558 
559                 if (!strextend_with_separator(&result, " ", t))
560                         return NULL;
561         }
562 
563         return str_realloc(TAKE_PTR(result));
564 }
565