]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/escape.c
Merge pull request #1768 from vcaputo/sd-daemon-listen-fds-overflow-bis
[thirdparty/systemd.git] / src / basic / escape.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "alloc-util.h"
23 #include "escape.h"
24 #include "hexdecoct.h"
25 #include "string-util.h"
26 #include "utf8.h"
27 #include "util.h"
28
29 size_t cescape_char(char c, char *buf) {
30 char * buf_old = buf;
31
32 switch (c) {
33
34 case '\a':
35 *(buf++) = '\\';
36 *(buf++) = 'a';
37 break;
38 case '\b':
39 *(buf++) = '\\';
40 *(buf++) = 'b';
41 break;
42 case '\f':
43 *(buf++) = '\\';
44 *(buf++) = 'f';
45 break;
46 case '\n':
47 *(buf++) = '\\';
48 *(buf++) = 'n';
49 break;
50 case '\r':
51 *(buf++) = '\\';
52 *(buf++) = 'r';
53 break;
54 case '\t':
55 *(buf++) = '\\';
56 *(buf++) = 't';
57 break;
58 case '\v':
59 *(buf++) = '\\';
60 *(buf++) = 'v';
61 break;
62 case '\\':
63 *(buf++) = '\\';
64 *(buf++) = '\\';
65 break;
66 case '"':
67 *(buf++) = '\\';
68 *(buf++) = '"';
69 break;
70 case '\'':
71 *(buf++) = '\\';
72 *(buf++) = '\'';
73 break;
74
75 default:
76 /* For special chars we prefer octal over
77 * hexadecimal encoding, simply because glib's
78 * g_strescape() does the same */
79 if ((c < ' ') || (c >= 127)) {
80 *(buf++) = '\\';
81 *(buf++) = octchar((unsigned char) c >> 6);
82 *(buf++) = octchar((unsigned char) c >> 3);
83 *(buf++) = octchar((unsigned char) c);
84 } else
85 *(buf++) = c;
86 break;
87 }
88
89 return buf - buf_old;
90 }
91
92 char *cescape(const char *s) {
93 char *r, *t;
94 const char *f;
95
96 assert(s);
97
98 /* Does C style string escaping. May be reversed with
99 * cunescape(). */
100
101 r = new(char, strlen(s)*4 + 1);
102 if (!r)
103 return NULL;
104
105 for (f = s, t = r; *f; f++)
106 t += cescape_char(*f, t);
107
108 *t = 0;
109
110 return r;
111 }
112
113 int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
114 int r = 1;
115
116 assert(p);
117 assert(*p);
118 assert(ret);
119
120 /* Unescapes C style. Returns the unescaped character in ret,
121 * unless we encountered a \u sequence in which case the full
122 * unicode character is returned in ret_unicode, instead. */
123
124 if (length != (size_t) -1 && length < 1)
125 return -EINVAL;
126
127 switch (p[0]) {
128
129 case 'a':
130 *ret = '\a';
131 break;
132 case 'b':
133 *ret = '\b';
134 break;
135 case 'f':
136 *ret = '\f';
137 break;
138 case 'n':
139 *ret = '\n';
140 break;
141 case 'r':
142 *ret = '\r';
143 break;
144 case 't':
145 *ret = '\t';
146 break;
147 case 'v':
148 *ret = '\v';
149 break;
150 case '\\':
151 *ret = '\\';
152 break;
153 case '"':
154 *ret = '"';
155 break;
156 case '\'':
157 *ret = '\'';
158 break;
159
160 case 's':
161 /* This is an extension of the XDG syntax files */
162 *ret = ' ';
163 break;
164
165 case 'x': {
166 /* hexadecimal encoding */
167 int a, b;
168
169 if (length != (size_t) -1 && length < 3)
170 return -EINVAL;
171
172 a = unhexchar(p[1]);
173 if (a < 0)
174 return -EINVAL;
175
176 b = unhexchar(p[2]);
177 if (b < 0)
178 return -EINVAL;
179
180 /* Don't allow NUL bytes */
181 if (a == 0 && b == 0)
182 return -EINVAL;
183
184 *ret = (char) ((a << 4U) | b);
185 r = 3;
186 break;
187 }
188
189 case 'u': {
190 /* C++11 style 16bit unicode */
191
192 int a[4];
193 unsigned i;
194 uint32_t c;
195
196 if (length != (size_t) -1 && length < 5)
197 return -EINVAL;
198
199 for (i = 0; i < 4; i++) {
200 a[i] = unhexchar(p[1 + i]);
201 if (a[i] < 0)
202 return a[i];
203 }
204
205 c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
206
207 /* Don't allow 0 chars */
208 if (c == 0)
209 return -EINVAL;
210
211 if (c < 128)
212 *ret = c;
213 else {
214 if (!ret_unicode)
215 return -EINVAL;
216
217 *ret = 0;
218 *ret_unicode = c;
219 }
220
221 r = 5;
222 break;
223 }
224
225 case 'U': {
226 /* C++11 style 32bit unicode */
227
228 int a[8];
229 unsigned i;
230 uint32_t c;
231
232 if (length != (size_t) -1 && length < 9)
233 return -EINVAL;
234
235 for (i = 0; i < 8; i++) {
236 a[i] = unhexchar(p[1 + i]);
237 if (a[i] < 0)
238 return a[i];
239 }
240
241 c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
242 ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
243
244 /* Don't allow 0 chars */
245 if (c == 0)
246 return -EINVAL;
247
248 /* Don't allow invalid code points */
249 if (!unichar_is_valid(c))
250 return -EINVAL;
251
252 if (c < 128)
253 *ret = c;
254 else {
255 if (!ret_unicode)
256 return -EINVAL;
257
258 *ret = 0;
259 *ret_unicode = c;
260 }
261
262 r = 9;
263 break;
264 }
265
266 case '0':
267 case '1':
268 case '2':
269 case '3':
270 case '4':
271 case '5':
272 case '6':
273 case '7': {
274 /* octal encoding */
275 int a, b, c;
276 uint32_t m;
277
278 if (length != (size_t) -1 && length < 3)
279 return -EINVAL;
280
281 a = unoctchar(p[0]);
282 if (a < 0)
283 return -EINVAL;
284
285 b = unoctchar(p[1]);
286 if (b < 0)
287 return -EINVAL;
288
289 c = unoctchar(p[2]);
290 if (c < 0)
291 return -EINVAL;
292
293 /* don't allow NUL bytes */
294 if (a == 0 && b == 0 && c == 0)
295 return -EINVAL;
296
297 /* Don't allow bytes above 255 */
298 m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
299 if (m > 255)
300 return -EINVAL;
301
302 *ret = m;
303 r = 3;
304 break;
305 }
306
307 default:
308 return -EINVAL;
309 }
310
311 return r;
312 }
313
314 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
315 char *r, *t;
316 const char *f;
317 size_t pl;
318
319 assert(s);
320 assert(ret);
321
322 /* Undoes C style string escaping, and optionally prefixes it. */
323
324 pl = prefix ? strlen(prefix) : 0;
325
326 r = new(char, pl+length+1);
327 if (!r)
328 return -ENOMEM;
329
330 if (prefix)
331 memcpy(r, prefix, pl);
332
333 for (f = s, t = r + pl; f < s + length; f++) {
334 size_t remaining;
335 uint32_t u;
336 char c;
337 int k;
338
339 remaining = s + length - f;
340 assert(remaining > 0);
341
342 if (*f != '\\') {
343 /* A literal literal, copy verbatim */
344 *(t++) = *f;
345 continue;
346 }
347
348 if (remaining == 1) {
349 if (flags & UNESCAPE_RELAX) {
350 /* A trailing backslash, copy verbatim */
351 *(t++) = *f;
352 continue;
353 }
354
355 free(r);
356 return -EINVAL;
357 }
358
359 k = cunescape_one(f + 1, remaining - 1, &c, &u);
360 if (k < 0) {
361 if (flags & UNESCAPE_RELAX) {
362 /* Invalid escape code, let's take it literal then */
363 *(t++) = '\\';
364 continue;
365 }
366
367 free(r);
368 return k;
369 }
370
371 if (c != 0)
372 /* Non-Unicode? Let's encode this directly */
373 *(t++) = c;
374 else
375 /* Unicode? Then let's encode this in UTF-8 */
376 t += utf8_encode_unichar(t, u);
377
378 f += k;
379 }
380
381 *t = 0;
382
383 *ret = r;
384 return t - r;
385 }
386
387 int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
388 return cunescape_length_with_prefix(s, length, NULL, flags, ret);
389 }
390
391 int cunescape(const char *s, UnescapeFlags flags, char **ret) {
392 return cunescape_length(s, strlen(s), flags, ret);
393 }
394
395 char *xescape(const char *s, const char *bad) {
396 char *r, *t;
397 const char *f;
398
399 /* Escapes all chars in bad, in addition to \ and all special
400 * chars, in \xFF style escaping. May be reversed with
401 * cunescape(). */
402
403 r = new(char, strlen(s) * 4 + 1);
404 if (!r)
405 return NULL;
406
407 for (f = s, t = r; *f; f++) {
408
409 if ((*f < ' ') || (*f >= 127) ||
410 (*f == '\\') || strchr(bad, *f)) {
411 *(t++) = '\\';
412 *(t++) = 'x';
413 *(t++) = hexchar(*f >> 4);
414 *(t++) = hexchar(*f);
415 } else
416 *(t++) = *f;
417 }
418
419 *t = 0;
420
421 return r;
422 }
423
424 static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
425 assert(bad);
426
427 for (; *s; s++) {
428 if (*s == '\\' || strchr(bad, *s))
429 *(t++) = '\\';
430
431 *(t++) = *s;
432 }
433
434 return t;
435 }
436
437 char *shell_escape(const char *s, const char *bad) {
438 char *r, *t;
439
440 r = new(char, strlen(s)*2+1);
441 if (!r)
442 return NULL;
443
444 t = strcpy_backslash_escaped(r, s, bad);
445 *t = 0;
446
447 return r;
448 }
449
450 char *shell_maybe_quote(const char *s) {
451 const char *p;
452 char *r, *t;
453
454 assert(s);
455
456 /* Encloses a string in double quotes if necessary to make it
457 * OK as shell string. */
458
459 for (p = s; *p; p++)
460 if (*p <= ' ' ||
461 *p >= 127 ||
462 strchr(SHELL_NEED_QUOTES, *p))
463 break;
464
465 if (!*p)
466 return strdup(s);
467
468 r = new(char, 1+strlen(s)*2+1+1);
469 if (!r)
470 return NULL;
471
472 t = r;
473 *(t++) = '"';
474 t = mempcpy(t, s, p - s);
475
476 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
477
478 *(t++)= '"';
479 *t = 0;
480
481 return r;
482 }