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