chiark / gitweb /
tree-wide: remove Emacs lines from all files
[elogind.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_length(const char *s, size_t n) {
93         const char *f;
94         char *r, *t;
95
96         assert(s || n == 0);
97
98         /* Does C style string escaping. May be reversed with
99          * cunescape(). */
100
101         r = new(char, n*4 + 1);
102         if (!r)
103                 return NULL;
104
105         for (f = s, t = r; f < s + n; f++)
106                 t += cescape_char(*f, t);
107
108         *t = 0;
109
110         return r;
111 }
112
113 char *cescape(const char *s) {
114         assert(s);
115
116         return cescape_length(s, strlen(s));
117 }
118
119 int cunescape_one(const char *p, size_t length, uint32_t *ret, bool *eight_bit) {
120         int r = 1;
121
122         assert(p);
123         assert(*p);
124         assert(ret);
125
126         /* Unescapes C style. Returns the unescaped character in ret.
127          * Sets *eight_bit to true if the escaped sequence either fits in
128          * one byte in UTF-8 or is a non-unicode literal byte and should
129          * instead be copied directly.
130          */
131
132         if (length != (size_t) -1 && length < 1)
133                 return -EINVAL;
134
135         switch (p[0]) {
136
137         case 'a':
138                 *ret = '\a';
139                 break;
140         case 'b':
141                 *ret = '\b';
142                 break;
143         case 'f':
144                 *ret = '\f';
145                 break;
146         case 'n':
147                 *ret = '\n';
148                 break;
149         case 'r':
150                 *ret = '\r';
151                 break;
152         case 't':
153                 *ret = '\t';
154                 break;
155         case 'v':
156                 *ret = '\v';
157                 break;
158         case '\\':
159                 *ret = '\\';
160                 break;
161         case '"':
162                 *ret = '"';
163                 break;
164         case '\'':
165                 *ret = '\'';
166                 break;
167
168         case 's':
169                 /* This is an extension of the XDG syntax files */
170                 *ret = ' ';
171                 break;
172
173         case 'x': {
174                 /* hexadecimal encoding */
175                 int a, b;
176
177                 if (length != (size_t) -1 && length < 3)
178                         return -EINVAL;
179
180                 a = unhexchar(p[1]);
181                 if (a < 0)
182                         return -EINVAL;
183
184                 b = unhexchar(p[2]);
185                 if (b < 0)
186                         return -EINVAL;
187
188                 /* Don't allow NUL bytes */
189                 if (a == 0 && b == 0)
190                         return -EINVAL;
191
192                 *ret = (a << 4U) | b;
193                 *eight_bit = true;
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                 *ret = c;
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                 *ret = c;
253                 r = 9;
254                 break;
255         }
256
257         case '0':
258         case '1':
259         case '2':
260         case '3':
261         case '4':
262         case '5':
263         case '6':
264         case '7': {
265                 /* octal encoding */
266                 int a, b, c;
267                 uint32_t m;
268
269                 if (length != (size_t) -1 && length < 3)
270                         return -EINVAL;
271
272                 a = unoctchar(p[0]);
273                 if (a < 0)
274                         return -EINVAL;
275
276                 b = unoctchar(p[1]);
277                 if (b < 0)
278                         return -EINVAL;
279
280                 c = unoctchar(p[2]);
281                 if (c < 0)
282                         return -EINVAL;
283
284                 /* don't allow NUL bytes */
285                 if (a == 0 && b == 0 && c == 0)
286                         return -EINVAL;
287
288                 /* Don't allow bytes above 255 */
289                 m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
290                 if (m > 255)
291                         return -EINVAL;
292
293                 *ret = m;
294                 *eight_bit = true;
295                 r = 3;
296                 break;
297         }
298
299         default:
300                 return -EINVAL;
301         }
302
303         return r;
304 }
305
306 int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
307         char *r, *t;
308         const char *f;
309         size_t pl;
310
311         assert(s);
312         assert(ret);
313
314         /* Undoes C style string escaping, and optionally prefixes it. */
315
316         pl = prefix ? strlen(prefix) : 0;
317
318         r = new(char, pl+length+1);
319         if (!r)
320                 return -ENOMEM;
321
322         if (prefix)
323                 memcpy(r, prefix, pl);
324
325         for (f = s, t = r + pl; f < s + length; f++) {
326                 size_t remaining;
327                 uint32_t u;
328                 bool eight_bit = false;
329                 int k;
330
331                 remaining = s + length - f;
332                 assert(remaining > 0);
333
334                 if (*f != '\\') {
335                         /* A literal literal, copy verbatim */
336                         *(t++) = *f;
337                         continue;
338                 }
339
340                 if (remaining == 1) {
341                         if (flags & UNESCAPE_RELAX) {
342                                 /* A trailing backslash, copy verbatim */
343                                 *(t++) = *f;
344                                 continue;
345                         }
346
347                         free(r);
348                         return -EINVAL;
349                 }
350
351                 k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
352                 if (k < 0) {
353                         if (flags & UNESCAPE_RELAX) {
354                                 /* Invalid escape code, let's take it literal then */
355                                 *(t++) = '\\';
356                                 continue;
357                         }
358
359                         free(r);
360                         return k;
361                 }
362
363                 f += k;
364                 if (eight_bit)
365                         /* One byte? Set directly as specified */
366                         *(t++) = u;
367                 else
368                         /* Otherwise encode as multi-byte UTF-8 */
369                         t += utf8_encode_unichar(t, u);
370         }
371
372         *t = 0;
373
374         *ret = r;
375         return t - r;
376 }
377
378 int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
379         return cunescape_length_with_prefix(s, length, NULL, flags, ret);
380 }
381
382 int cunescape(const char *s, UnescapeFlags flags, char **ret) {
383         return cunescape_length(s, strlen(s), flags, ret);
384 }
385
386 char *xescape(const char *s, const char *bad) {
387         char *r, *t;
388         const char *f;
389
390         /* Escapes all chars in bad, in addition to \ and all special
391          * chars, in \xFF style escaping. May be reversed with
392          * cunescape(). */
393
394         r = new(char, strlen(s) * 4 + 1);
395         if (!r)
396                 return NULL;
397
398         for (f = s, t = r; *f; f++) {
399
400                 if ((*f < ' ') || (*f >= 127) ||
401                     (*f == '\\') || strchr(bad, *f)) {
402                         *(t++) = '\\';
403                         *(t++) = 'x';
404                         *(t++) = hexchar(*f >> 4);
405                         *(t++) = hexchar(*f);
406                 } else
407                         *(t++) = *f;
408         }
409
410         *t = 0;
411
412         return r;
413 }
414
415 static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
416         assert(bad);
417
418         for (; *s; s++) {
419                 if (*s == '\\' || strchr(bad, *s))
420                         *(t++) = '\\';
421
422                 *(t++) = *s;
423         }
424
425         return t;
426 }
427
428 char *shell_escape(const char *s, const char *bad) {
429         char *r, *t;
430
431         r = new(char, strlen(s)*2+1);
432         if (!r)
433                 return NULL;
434
435         t = strcpy_backslash_escaped(r, s, bad);
436         *t = 0;
437
438         return r;
439 }
440
441 char *shell_maybe_quote(const char *s) {
442         const char *p;
443         char *r, *t;
444
445         assert(s);
446
447         /* Encloses a string in double quotes if necessary to make it
448          * OK as shell string. */
449
450         for (p = s; *p; p++)
451                 if (*p <= ' ' ||
452                     *p >= 127 ||
453                     strchr(SHELL_NEED_QUOTES, *p))
454                         break;
455
456         if (!*p)
457                 return strdup(s);
458
459         r = new(char, 1+strlen(s)*2+1+1);
460         if (!r)
461                 return NULL;
462
463         t = r;
464         *(t++) = '"';
465         t = mempcpy(t, s, p - s);
466
467         t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
468
469         *(t++)= '"';
470         *t = 0;
471
472         return r;
473 }