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