Line data Source code
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 62 : bool unichar_is_valid(uint32_t ch) {
56 :
57 62 : if (ch >= 0x110000) /* End of unicode space */
58 0 : return false;
59 62 : if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
60 0 : return false;
61 62 : if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
62 0 : return false;
63 62 : if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
64 0 : return false;
65 :
66 62 : return true;
67 : }
68 :
69 64 : 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 73 : return (ch < ' ' && ch != '\t' && ch != '\n') ||
78 9 : (0x7F <= ch && ch <= 0x9F);
79 : }
80 :
81 : /* count of characters used to encode one unicode char */
82 20859 : static int utf8_encoded_expected_len(const char *str) {
83 : unsigned char c;
84 :
85 20859 : assert(str);
86 :
87 20859 : c = (unsigned char) str[0];
88 20859 : if (c < 0x80)
89 20528 : return 1;
90 331 : if ((c & 0xe0) == 0xc0)
91 72 : return 2;
92 259 : if ((c & 0xf0) == 0xe0)
93 210 : return 3;
94 49 : if ((c & 0xf8) == 0xf0)
95 40 : return 4;
96 9 : if ((c & 0xfc) == 0xf8)
97 0 : return 5;
98 9 : if ((c & 0xfe) == 0xfc)
99 0 : return 6;
100 :
101 9 : return 0;
102 : }
103 :
104 : /* decode one unicode char */
105 367 : int utf8_encoded_to_unichar(const char *str) {
106 : int unichar, len, i;
107 :
108 367 : assert(str);
109 :
110 367 : len = utf8_encoded_expected_len(str);
111 :
112 367 : switch (len) {
113 : case 1:
114 116 : return (int)str[0];
115 : case 2:
116 48 : unichar = str[0] & 0x1f;
117 48 : break;
118 : case 3:
119 163 : unichar = (int)str[0] & 0x0f;
120 163 : break;
121 : case 4:
122 40 : unichar = (int)str[0] & 0x07;
123 40 : break;
124 : case 5:
125 0 : unichar = (int)str[0] & 0x03;
126 0 : break;
127 : case 6:
128 0 : unichar = (int)str[0] & 0x01;
129 0 : break;
130 : default:
131 0 : return -EINVAL;
132 : }
133 :
134 741 : for (i = 1; i < len; i++) {
135 494 : if (((int)str[i] & 0xc0) != 0x80)
136 4 : return -EINVAL;
137 490 : unichar <<= 6;
138 490 : unichar |= (int)str[i] & 0x3f;
139 : }
140 :
141 247 : return unichar;
142 : }
143 :
144 43 : bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
145 : const char *p;
146 :
147 43 : assert(str);
148 :
149 141 : for (p = str; length;) {
150 : int encoded_len, val;
151 :
152 65 : encoded_len = utf8_encoded_valid_unichar(p);
153 129 : if (encoded_len < 0 ||
154 64 : (size_t) encoded_len > length)
155 1 : return false;
156 :
157 64 : val = utf8_encoded_to_unichar(p);
158 128 : if (val < 0 ||
159 119 : unichar_is_control(val) ||
160 55 : (!newline && val == '\n'))
161 9 : return false;
162 :
163 55 : length -= encoded_len;
164 55 : p += encoded_len;
165 : }
166 :
167 33 : return true;
168 : }
169 :
170 832 : const char *utf8_is_valid(const char *str) {
171 : const uint8_t *p;
172 :
173 832 : assert(str);
174 :
175 21780 : for (p = (const uint8_t*) str; *p; ) {
176 : int len;
177 :
178 20117 : len = utf8_encoded_valid_unichar((const char *)p);
179 20117 : if (len < 0)
180 1 : return NULL;
181 :
182 20116 : p += len;
183 : }
184 :
185 831 : return str;
186 : }
187 :
188 7 : char *utf8_escape_invalid(const char *str) {
189 : char *p, *s;
190 :
191 7 : assert(str);
192 :
193 7 : p = s = malloc(strlen(str) * 4 + 1);
194 7 : if (!p)
195 0 : return NULL;
196 :
197 91 : while (*str) {
198 : int len;
199 :
200 77 : len = utf8_encoded_valid_unichar(str);
201 77 : if (len > 0) {
202 71 : s = mempcpy(s, str, len);
203 71 : str += len;
204 : } else {
205 6 : s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
206 6 : str += 1;
207 : }
208 : }
209 :
210 7 : *s = '\0';
211 :
212 7 : return p;
213 : }
214 :
215 6 : char *utf8_escape_non_printable(const char *str) {
216 : char *p, *s;
217 :
218 6 : assert(str);
219 :
220 6 : p = s = malloc(strlen(str) * 4 + 1);
221 6 : if (!p)
222 0 : return NULL;
223 :
224 63 : while (*str) {
225 : int len;
226 :
227 51 : len = utf8_encoded_valid_unichar(str);
228 51 : if (len > 0) {
229 39 : if (utf8_is_printable(str, len)) {
230 30 : s = mempcpy(s, str, len);
231 30 : str += len;
232 : } else {
233 27 : while (len > 0) {
234 9 : *(s++) = '\\';
235 9 : *(s++) = 'x';
236 9 : *(s++) = hexchar((int) *str >> 4);
237 9 : *(s++) = hexchar((int) *str);
238 :
239 9 : str += 1;
240 9 : len --;
241 : }
242 : }
243 : } else {
244 12 : s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
245 12 : str += 1;
246 : }
247 : }
248 :
249 6 : *s = '\0';
250 :
251 6 : return p;
252 : }
253 :
254 79 : char *ascii_is_valid(const char *str) {
255 : const char *p;
256 :
257 79 : assert(str);
258 :
259 1879 : for (p = str; *p; p++)
260 1808 : if ((unsigned char) *p >= 128)
261 8 : return NULL;
262 :
263 71 : 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 13 : size_t utf8_encode_unichar(char *out_utf8, uint32_t g) {
279 :
280 13 : if (g < (1 << 7)) {
281 3 : if (out_utf8)
282 3 : out_utf8[0] = g & 0x7f;
283 3 : return 1;
284 10 : } else if (g < (1 << 11)) {
285 5 : if (out_utf8) {
286 5 : out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
287 5 : out_utf8[1] = 0x80 | (g & 0x3f);
288 : }
289 5 : return 2;
290 5 : } else if (g < (1 << 16)) {
291 1 : if (out_utf8) {
292 1 : out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
293 1 : out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
294 1 : out_utf8[2] = 0x80 | (g & 0x3f);
295 : }
296 1 : return 3;
297 4 : } else if (g < (1 << 21)) {
298 4 : if (out_utf8) {
299 4 : out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
300 4 : out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
301 4 : out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
302 4 : out_utf8[3] = 0x80 | (g & 0x3f);
303 : }
304 4 : return 4;
305 : }
306 :
307 0 : return 0;
308 : }
309 :
310 1 : char *utf16_to_utf8(const void *s, size_t length) {
311 : const uint8_t *f;
312 : char *r, *t;
313 :
314 1 : r = new(char, (length * 4 + 1) / 2 + 1);
315 1 : if (!r)
316 0 : return NULL;
317 :
318 1 : f = s;
319 1 : t = r;
320 :
321 8 : while (f < (const uint8_t*) s + length) {
322 : uint16_t w1, w2;
323 :
324 : /* see RFC 2781 section 2.2 */
325 :
326 6 : w1 = f[1] << 8 | f[0];
327 6 : f += 2;
328 :
329 6 : if (!utf16_is_surrogate(w1)) {
330 3 : t += utf8_encode_unichar(t, w1);
331 :
332 3 : continue;
333 : }
334 :
335 3 : if (utf16_is_trailing_surrogate(w1))
336 1 : continue;
337 2 : else if (f >= (const uint8_t*) s + length)
338 0 : break;
339 :
340 2 : w2 = f[1] << 8 | f[0];
341 2 : f += 2;
342 :
343 2 : if (!utf16_is_trailing_surrogate(w2)) {
344 1 : f -= 2;
345 1 : continue;
346 : }
347 :
348 1 : t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
349 : }
350 :
351 1 : *t = 0;
352 1 : return r;
353 : }
354 :
355 : /* expected size used to encode one unicode char */
356 62 : static int utf8_unichar_to_encoded_len(int unichar) {
357 :
358 62 : if (unichar < 0x80)
359 4 : return 1;
360 58 : if (unichar < 0x800)
361 24 : return 2;
362 34 : if (unichar < 0x10000)
363 34 : return 3;
364 0 : if (unichar < 0x200000)
365 0 : return 4;
366 0 : if (unichar < 0x4000000)
367 0 : return 5;
368 :
369 0 : return 6;
370 : }
371 :
372 : /* validate one encoded unicode char and return its length */
373 20492 : int utf8_encoded_valid_unichar(const char *str) {
374 : int len, unichar, i;
375 :
376 20492 : assert(str);
377 :
378 20492 : len = utf8_encoded_expected_len(str);
379 20492 : if (len == 0)
380 9 : return -EINVAL;
381 :
382 : /* ascii is valid */
383 20483 : if (len == 1)
384 20412 : return 1;
385 :
386 : /* check if expected encoded chars are available */
387 251 : for (i = 0; i < len; i++)
388 189 : if ((str[i] & 0x80) != 0x80)
389 9 : return -EINVAL;
390 :
391 62 : unichar = utf8_encoded_to_unichar(str);
392 :
393 : /* check if encoded length matches encoded value */
394 62 : if (utf8_unichar_to_encoded_len(unichar) != len)
395 4 : return -EINVAL;
396 :
397 : /* check if value has valid range */
398 58 : if (!unichar_is_valid(unichar))
399 0 : return -EINVAL;
400 :
401 58 : return len;
402 : }
|