]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/utf8.c
Merge pull request #156 from filbranden/journal_leading_whitespace
[thirdparty/systemd.git] / src / basic / utf8.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2008-2011 Kay Sievers
7 Copyright 2012 Lennart Poettering
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 /* Parts of this file are based on the GLIB utf8 validation functions. The
24 * original license text follows. */
25
26 /* gutf8.c - Operations on UTF-8 strings.
27 *
28 * Copyright (C) 1999 Tom Tromey
29 * Copyright (C) 2000 Red Hat, Inc.
30 *
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
35 *
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
40 *
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the Free Software
43 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
44 */
45
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <inttypes.h>
49 #include <string.h>
50 #include <stdbool.h>
51
52 #include "utf8.h"
53 #include "util.h"
54
55 bool unichar_is_valid(uint32_t ch) {
56
57 if (ch >= 0x110000) /* End of unicode space */
58 return false;
59 if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
60 return false;
61 if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
62 return false;
63 if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
64 return false;
65
66 return true;
67 }
68
69 static bool unichar_is_control(uint32_t ch) {
70
71 /*
72 0 to ' '-1 is the C0 range.
73 DEL=0x7F, and DEL+1 to 0x9F is C1 range.
74 '\t' is in C0 range, but more or less harmless and commonly used.
75 */
76
77 return (ch < ' ' && ch != '\t' && ch != '\n') ||
78 (0x7F <= ch && ch <= 0x9F);
79 }
80
81 /* count of characters used to encode one unicode char */
82 static int utf8_encoded_expected_len(const char *str) {
83 unsigned char c;
84
85 assert(str);
86
87 c = (unsigned char) str[0];
88 if (c < 0x80)
89 return 1;
90 if ((c & 0xe0) == 0xc0)
91 return 2;
92 if ((c & 0xf0) == 0xe0)
93 return 3;
94 if ((c & 0xf8) == 0xf0)
95 return 4;
96 if ((c & 0xfc) == 0xf8)
97 return 5;
98 if ((c & 0xfe) == 0xfc)
99 return 6;
100
101 return 0;
102 }
103
104 /* decode one unicode char */
105 int utf8_encoded_to_unichar(const char *str) {
106 int unichar, len, i;
107
108 assert(str);
109
110 len = utf8_encoded_expected_len(str);
111
112 switch (len) {
113 case 1:
114 return (int)str[0];
115 case 2:
116 unichar = str[0] & 0x1f;
117 break;
118 case 3:
119 unichar = (int)str[0] & 0x0f;
120 break;
121 case 4:
122 unichar = (int)str[0] & 0x07;
123 break;
124 case 5:
125 unichar = (int)str[0] & 0x03;
126 break;
127 case 6:
128 unichar = (int)str[0] & 0x01;
129 break;
130 default:
131 return -EINVAL;
132 }
133
134 for (i = 1; i < len; i++) {
135 if (((int)str[i] & 0xc0) != 0x80)
136 return -EINVAL;
137 unichar <<= 6;
138 unichar |= (int)str[i] & 0x3f;
139 }
140
141 return unichar;
142 }
143
144 bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
145 const char *p;
146
147 assert(str);
148
149 for (p = str; length;) {
150 int encoded_len, val;
151
152 encoded_len = utf8_encoded_valid_unichar(p);
153 if (encoded_len < 0 ||
154 (size_t) encoded_len > length)
155 return false;
156
157 val = utf8_encoded_to_unichar(p);
158 if (val < 0 ||
159 unichar_is_control(val) ||
160 (!newline && val == '\n'))
161 return false;
162
163 length -= encoded_len;
164 p += encoded_len;
165 }
166
167 return true;
168 }
169
170 const char *utf8_is_valid(const char *str) {
171 const uint8_t *p;
172
173 assert(str);
174
175 for (p = (const uint8_t*) str; *p; ) {
176 int len;
177
178 len = utf8_encoded_valid_unichar((const char *)p);
179 if (len < 0)
180 return NULL;
181
182 p += len;
183 }
184
185 return str;
186 }
187
188 char *utf8_escape_invalid(const char *str) {
189 char *p, *s;
190
191 assert(str);
192
193 p = s = malloc(strlen(str) * 4 + 1);
194 if (!p)
195 return NULL;
196
197 while (*str) {
198 int len;
199
200 len = utf8_encoded_valid_unichar(str);
201 if (len > 0) {
202 s = mempcpy(s, str, len);
203 str += len;
204 } else {
205 s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
206 str += 1;
207 }
208 }
209
210 *s = '\0';
211
212 return p;
213 }
214
215 char *utf8_escape_non_printable(const char *str) {
216 char *p, *s;
217
218 assert(str);
219
220 p = s = malloc(strlen(str) * 4 + 1);
221 if (!p)
222 return NULL;
223
224 while (*str) {
225 int len;
226
227 len = utf8_encoded_valid_unichar(str);
228 if (len > 0) {
229 if (utf8_is_printable(str, len)) {
230 s = mempcpy(s, str, len);
231 str += len;
232 } else {
233 while (len > 0) {
234 *(s++) = '\\';
235 *(s++) = 'x';
236 *(s++) = hexchar((int) *str >> 4);
237 *(s++) = hexchar((int) *str);
238
239 str += 1;
240 len --;
241 }
242 }
243 } else {
244 s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
245 str += 1;
246 }
247 }
248
249 *s = '\0';
250
251 return p;
252 }
253
254 char *ascii_is_valid(const char *str) {
255 const char *p;
256
257 assert(str);
258
259 for (p = str; *p; p++)
260 if ((unsigned char) *p >= 128)
261 return NULL;
262
263 return (char*) str;
264 }
265
266 /**
267 * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
268 * @out_utf8: output buffer of at least 4 bytes or NULL
269 * @g: UCS-4 character to encode
270 *
271 * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
272 * The length of the character is returned. It is not zero-terminated! If the
273 * output buffer is NULL, only the length is returned.
274 *
275 * Returns: The length in bytes that the UTF-8 representation does or would
276 * occupy.
277 */
278 size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
279
280 if (g < (1 << 7)) {
281 if (out_utf8)
282 out_utf8[0] = g & 0x7f;
283 return 1;
284 } else if (g < (1 << 11)) {
285 if (out_utf8) {
286 out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
287 out_utf8[1] = 0x80 | (g & 0x3f);
288 }
289 return 2;
290 } else if (g < (1 << 16)) {
291 if (out_utf8) {
292 out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
293 out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
294 out_utf8[2] = 0x80 | (g & 0x3f);
295 }
296 return 3;
297 } else if (g < (1 << 21)) {
298 if (out_utf8) {
299 out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
300 out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
301 out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
302 out_utf8[3] = 0x80 | (g & 0x3f);
303 }
304 return 4;
305 }
306
307 return 0;
308 }
309
310 char *utf16_to_utf8(const void *s, size_t length) {
311 const uint8_t *f;
312 char *r, *t;
313
314 r = new(char, (length * 4 + 1) / 2 + 1);
315 if (!r)
316 return NULL;
317
318 f = s;
319 t = r;
320
321 while (f < (const uint8_t*) s + length) {
322 uint16_t w1, w2;
323
324 /* see RFC 2781 section 2.2 */
325
326 w1 = f[1] << 8 | f[0];
327 f += 2;
328
329 if (!utf16_is_surrogate(w1)) {
330 t += utf8_encode_unichar(t, w1);
331
332 continue;
333 }
334
335 if (utf16_is_trailing_surrogate(w1))
336 continue;
337 else if (f >= (const uint8_t*) s + length)
338 break;
339
340 w2 = f[1] << 8 | f[0];
341 f += 2;
342
343 if (!utf16_is_trailing_surrogate(w2)) {
344 f -= 2;
345 continue;
346 }
347
348 t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
349 }
350
351 *t = 0;
352 return r;
353 }
354
355 /* expected size used to encode one unicode char */
356 static int utf8_unichar_to_encoded_len(int unichar) {
357
358 if (unichar < 0x80)
359 return 1;
360 if (unichar < 0x800)
361 return 2;
362 if (unichar < 0x10000)
363 return 3;
364 if (unichar < 0x200000)
365 return 4;
366 if (unichar < 0x4000000)
367 return 5;
368
369 return 6;
370 }
371
372 /* validate one encoded unicode char and return its length */
373 int utf8_encoded_valid_unichar(const char *str) {
374 int len, unichar, i;
375
376 assert(str);
377
378 len = utf8_encoded_expected_len(str);
379 if (len == 0)
380 return -EINVAL;
381
382 /* ascii is valid */
383 if (len == 1)
384 return 1;
385
386 /* check if expected encoded chars are available */
387 for (i = 0; i < len; i++)
388 if ((str[i] & 0x80) != 0x80)
389 return -EINVAL;
390
391 unichar = utf8_encoded_to_unichar(str);
392
393 /* check if encoded length matches encoded value */
394 if (utf8_unichar_to_encoded_len(unichar) != len)
395 return -EINVAL;
396
397 /* check if value has valid range */
398 if (!unichar_is_valid(unichar))
399 return -EINVAL;
400
401 return len;
402 }