Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : /* Parts of this file are based on the GLIB utf8 validation functions. The
4 : : * original license text follows. */
5 : :
6 : : /* gutf8.c - Operations on UTF-8 strings.
7 : : *
8 : : * Copyright (C) 1999 Tom Tromey
9 : : * Copyright (C) 2000 Red Hat, Inc.
10 : : *
11 : : * This library is free software; you can redistribute it and/or
12 : : * modify it under the terms of the GNU Library General Public
13 : : * License as published by the Free Software Foundation; either
14 : : * version 2 of the License, or (at your option) any later version.
15 : : *
16 : : * This library is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 : : * Library General Public License for more details.
20 : : *
21 : : * You should have received a copy of the GNU Library General Public
22 : : * License along with this library; if not, write to the Free Software
23 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 : : */
25 : :
26 : : #include <errno.h>
27 : : #include <stdbool.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : :
31 : : #include "alloc-util.h"
32 : : #include "gunicode.h"
33 : : #include "hexdecoct.h"
34 : : #include "macro.h"
35 : : #include "string-util.h"
36 : : #include "utf8.h"
37 : :
38 : 31836 : bool unichar_is_valid(char32_t ch) {
39 : :
40 [ - + ]: 31836 : if (ch >= 0x110000) /* End of unicode space */
41 : 0 : return false;
42 [ - + ]: 31836 : if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
43 : 0 : return false;
44 [ + + - + ]: 31836 : if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
45 : 0 : return false;
46 [ - + ]: 31836 : if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
47 : 0 : return false;
48 : :
49 : 31836 : return true;
50 : : }
51 : :
52 : 1488 : static bool unichar_is_control(char32_t ch) {
53 : :
54 : : /*
55 : : 0 to ' '-1 is the C0 range.
56 : : DEL=0x7F, and DEL+1 to 0x9F is C1 range.
57 : : '\t' is in C0 range, but more or less harmless and commonly used.
58 : : */
59 : :
60 [ + + + + : 1524 : return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
+ + + + ]
61 [ - + ]: 36 : (0x7F <= ch && ch <= 0x9F);
62 : : }
63 : :
64 : : /* count of characters used to encode one unicode char */
65 : 1306404 : static size_t utf8_encoded_expected_len(uint8_t c) {
66 [ + + ]: 1306404 : if (c < 0x80)
67 : 1087304 : return 1;
68 [ + + ]: 219100 : if ((c & 0xe0) == 0xc0)
69 : 95896 : return 2;
70 [ + + ]: 123204 : if ((c & 0xf0) == 0xe0)
71 : 122444 : return 3;
72 [ + + ]: 760 : if ((c & 0xf8) == 0xf0)
73 : 644 : return 4;
74 [ - + ]: 116 : if ((c & 0xfc) == 0xf8)
75 : 0 : return 5;
76 [ - + ]: 116 : if ((c & 0xfe) == 0xfc)
77 : 0 : return 6;
78 : :
79 : 116 : return 0;
80 : : }
81 : :
82 : : /* decode one unicode char */
83 : 249764 : int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
84 : : char32_t unichar;
85 : : size_t len, i;
86 : :
87 [ - + ]: 249764 : assert(str);
88 : :
89 : 249764 : len = utf8_encoded_expected_len(str[0]);
90 : :
91 [ + + + + : 249764 : switch (len) {
- - - ]
92 : 67288 : case 1:
93 : 67288 : *ret_unichar = (char32_t)str[0];
94 : 67288 : return 0;
95 : 81464 : case 2:
96 : 81464 : unichar = str[0] & 0x1f;
97 : 81464 : break;
98 : 100400 : case 3:
99 : 100400 : unichar = (char32_t)str[0] & 0x0f;
100 : 100400 : break;
101 : 612 : case 4:
102 : 612 : unichar = (char32_t)str[0] & 0x07;
103 : 612 : break;
104 : 0 : case 5:
105 : 0 : unichar = (char32_t)str[0] & 0x03;
106 : 0 : break;
107 : 0 : case 6:
108 : 0 : unichar = (char32_t)str[0] & 0x01;
109 : 0 : break;
110 : 0 : default:
111 : 0 : return -EINVAL;
112 : : }
113 : :
114 [ + + ]: 466544 : for (i = 1; i < len; i++) {
115 [ + + ]: 284092 : if (((char32_t)str[i] & 0xc0) != 0x80)
116 : 24 : return -EINVAL;
117 : :
118 : 284068 : unichar <<= 6;
119 : 284068 : unichar |= (char32_t)str[i] & 0x3f;
120 : : }
121 : :
122 : 182452 : *ret_unichar = unichar;
123 : :
124 : 182452 : return 0;
125 : : }
126 : :
127 : 1404 : bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
128 : : const char *p;
129 : :
130 [ - + ]: 1404 : assert(str);
131 : :
132 [ + + ]: 2588 : for (p = str; length > 0;) {
133 : : int encoded_len, r;
134 : : char32_t val;
135 : :
136 : 1492 : encoded_len = utf8_encoded_valid_unichar(p, length);
137 [ + + ]: 1492 : if (encoded_len < 0)
138 : 308 : return false;
139 [ + - - + ]: 1488 : assert(encoded_len > 0 && (size_t) encoded_len <= length);
140 : :
141 : 1488 : r = utf8_encoded_to_unichar(p, &val);
142 [ + - + + ]: 2976 : if (r < 0 ||
143 : 1488 : unichar_is_control(val) ||
144 [ - + # # ]: 1184 : (!newline && val == '\n'))
145 : 304 : return false;
146 : :
147 : 1184 : length -= encoded_len;
148 : 1184 : p += encoded_len;
149 : : }
150 : :
151 : 1096 : return true;
152 : : }
153 : :
154 : 52464 : char *utf8_is_valid(const char *str) {
155 : : const char *p;
156 : :
157 [ - + ]: 52464 : assert(str);
158 : :
159 : 52464 : p = str;
160 [ + + ]: 1078508 : while (*p) {
161 : : int len;
162 : :
163 : 1030516 : len = utf8_encoded_valid_unichar(p, (size_t) -1);
164 [ + + ]: 1030516 : if (len < 0)
165 : 4472 : return NULL;
166 : :
167 : 1026044 : p += len;
168 : : }
169 : :
170 : 47992 : return (char*) str;
171 : : }
172 : :
173 : 20 : char *utf8_escape_invalid(const char *str) {
174 : : char *p, *s;
175 : :
176 [ - + ]: 20 : assert(str);
177 : :
178 : 20 : p = s = malloc(strlen(str) * 4 + 1);
179 [ - + ]: 20 : if (!p)
180 : 0 : return NULL;
181 : :
182 [ + + ]: 180 : while (*str) {
183 : : int len;
184 : :
185 : 160 : len = utf8_encoded_valid_unichar(str, (size_t) -1);
186 [ + + ]: 160 : if (len > 0) {
187 : 132 : s = mempcpy(s, str, len);
188 : 132 : str += len;
189 : : } else {
190 : 28 : s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
191 : 28 : str += 1;
192 : : }
193 : : }
194 : :
195 : 20 : *s = '\0';
196 : 20 : (void) str_realloc(&p);
197 : 20 : return p;
198 : : }
199 : :
200 : 163096 : static int utf8_char_console_width(const char *str) {
201 : : char32_t c;
202 : : int r;
203 : :
204 : 163096 : r = utf8_encoded_to_unichar(str, &c);
205 [ + + ]: 163096 : if (r < 0)
206 : 4 : return r;
207 : :
208 : : /* TODO: we should detect combining characters */
209 : :
210 [ + + ]: 163092 : return unichar_iswide(c) ? 2 : 1;
211 : : }
212 : :
213 : 304 : char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
214 : : char *p, *s, *prev_s;
215 : 304 : size_t n = 0; /* estimated print width */
216 : :
217 [ - + ]: 304 : assert(str);
218 : :
219 [ + + ]: 304 : if (console_width == 0)
220 : 20 : return strdup("");
221 : :
222 : 284 : p = s = prev_s = malloc(strlen(str) * 4 + 1);
223 [ - + ]: 284 : if (!p)
224 : 0 : return NULL;
225 : :
226 : 1440 : for (;;) {
227 : : int len;
228 : 1724 : char *saved_s = s;
229 : :
230 [ + + ]: 1724 : if (!*str) /* done! */
231 : 136 : goto finish;
232 : :
233 : 1588 : len = utf8_encoded_valid_unichar(str, (size_t) -1);
234 [ + + ]: 1588 : if (len > 0) {
235 [ + + ]: 1388 : if (utf8_is_printable(str, len)) {
236 : : int w;
237 : :
238 : 1084 : w = utf8_char_console_width(str);
239 [ - + ]: 1084 : assert(w >= 0);
240 [ + + ]: 1084 : if (n + w > console_width)
241 : 68 : goto truncation;
242 : :
243 : 1016 : s = mempcpy(s, str, len);
244 : 1016 : str += len;
245 : 1016 : n += w;
246 : :
247 : : } else {
248 [ + + ]: 532 : for (; len > 0; len--) {
249 [ + + ]: 304 : if (n + 4 > console_width)
250 : 76 : goto truncation;
251 : :
252 : 228 : *(s++) = '\\';
253 : 228 : *(s++) = 'x';
254 : 228 : *(s++) = hexchar((int) *str >> 4);
255 : 228 : *(s++) = hexchar((int) *str);
256 : :
257 : 228 : str += 1;
258 : 228 : n += 4;
259 : : }
260 : : }
261 : : } else {
262 [ + + ]: 200 : if (n + 1 > console_width)
263 : 4 : goto truncation;
264 : :
265 : 196 : s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
266 : 196 : str += 1;
267 : 196 : n += 1;
268 : : }
269 : :
270 : 1440 : prev_s = saved_s;
271 : : }
272 : :
273 : 148 : truncation:
274 : : /* Try to go back one if we don't have enough space for the ellipsis */
275 [ + + ]: 148 : if (n + 1 >= console_width)
276 : 108 : s = prev_s;
277 : :
278 : 148 : s = mempcpy(s, "…", strlen("…"));
279 : :
280 : 284 : finish:
281 : 284 : *s = '\0';
282 : 284 : (void) str_realloc(&p);
283 : 284 : return p;
284 : : }
285 : :
286 : 228 : char *ascii_is_valid(const char *str) {
287 : : const char *p;
288 : :
289 : : /* Check whether the string consists of valid ASCII bytes,
290 : : * i.e values between 0 and 127, inclusive. */
291 : :
292 [ - + ]: 228 : assert(str);
293 : :
294 [ + + ]: 4348 : for (p = str; *p; p++)
295 [ + + ]: 4128 : if ((unsigned char) *p >= 128)
296 : 8 : return NULL;
297 : :
298 : 220 : return (char*) str;
299 : : }
300 : :
301 : 13064 : char *ascii_is_valid_n(const char *str, size_t len) {
302 : : size_t i;
303 : :
304 : : /* Very similar to ascii_is_valid(), but checks exactly len
305 : : * bytes and rejects any NULs in that range. */
306 : :
307 [ - + ]: 13064 : assert(str);
308 : :
309 [ + + ]: 54736 : for (i = 0; i < len; i++)
310 [ + + + + ]: 50616 : if ((unsigned char) str[i] >= 128 || str[i] == 0)
311 : 8944 : return NULL;
312 : :
313 : 4120 : return (char*) str;
314 : : }
315 : :
316 : : /**
317 : : * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8
318 : : * @out_utf8: output buffer of at least 4 bytes or NULL
319 : : * @g: UCS-4 character to encode
320 : : *
321 : : * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8.
322 : : * The length of the character is returned. It is not zero-terminated! If the
323 : : * output buffer is NULL, only the length is returned.
324 : : *
325 : : * Returns: The length in bytes that the UTF-8 representation does or would
326 : : * occupy.
327 : : */
328 : 548 : size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
329 : :
330 [ + + ]: 548 : if (g < (1 << 7)) {
331 [ + - ]: 288 : if (out_utf8)
332 : 288 : out_utf8[0] = g & 0x7f;
333 : 288 : return 1;
334 [ + + ]: 260 : } else if (g < (1 << 11)) {
335 [ + - ]: 56 : if (out_utf8) {
336 : 56 : out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
337 : 56 : out_utf8[1] = 0x80 | (g & 0x3f);
338 : : }
339 : 56 : return 2;
340 [ + + ]: 204 : } else if (g < (1 << 16)) {
341 [ + - ]: 16 : if (out_utf8) {
342 : 16 : out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
343 : 16 : out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
344 : 16 : out_utf8[2] = 0x80 | (g & 0x3f);
345 : : }
346 : 16 : return 3;
347 [ + - ]: 188 : } else if (g < (1 << 21)) {
348 [ + - ]: 188 : if (out_utf8) {
349 : 188 : out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
350 : 188 : out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
351 : 188 : out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
352 : 188 : out_utf8[3] = 0x80 | (g & 0x3f);
353 : : }
354 : 188 : return 4;
355 : : }
356 : :
357 : 0 : return 0;
358 : : }
359 : :
360 : 28 : char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
361 : : const uint8_t *f;
362 : : char *r, *t;
363 : :
364 [ - + ]: 28 : assert(s);
365 : :
366 : : /* Input length is in bytes, i.e. the shortest possible character takes 2 bytes. Each unicode character may
367 : : * take up to 4 bytes in UTF-8. Let's also account for a trailing NUL byte. */
368 [ - + ]: 28 : if (length * 2 < length)
369 : 0 : return NULL; /* overflow */
370 : :
371 : 28 : r = new(char, length * 2 + 1);
372 [ - + ]: 28 : if (!r)
373 : 0 : return NULL;
374 : :
375 : 28 : f = (const uint8_t*) s;
376 : 28 : t = r;
377 : :
378 [ + + ]: 180 : while (f + 1 < (const uint8_t*) s + length) {
379 : : char16_t w1, w2;
380 : :
381 : : /* see RFC 2781 section 2.2 */
382 : :
383 : 152 : w1 = f[1] << 8 | f[0];
384 : 152 : f += 2;
385 : :
386 [ + + ]: 152 : if (!utf16_is_surrogate(w1)) {
387 : 124 : t += utf8_encode_unichar(t, w1);
388 : 124 : continue;
389 : : }
390 : :
391 [ + + ]: 28 : if (utf16_is_trailing_surrogate(w1))
392 : 4 : continue; /* spurious trailing surrogate, ignore */
393 : :
394 [ - + ]: 24 : if (f + 1 >= (const uint8_t*) s + length)
395 : 0 : break;
396 : :
397 : 24 : w2 = f[1] << 8 | f[0];
398 : 24 : f += 2;
399 : :
400 [ + + ]: 24 : if (!utf16_is_trailing_surrogate(w2)) {
401 : 4 : f -= 2;
402 : 4 : continue; /* surrogate missing its trailing surrogate, ignore */
403 : : }
404 : :
405 : 20 : t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
406 : : }
407 : :
408 : 28 : *t = 0;
409 : 28 : return r;
410 : : }
411 : :
412 : 64 : size_t utf16_encode_unichar(char16_t *out, char32_t c) {
413 : :
414 : : /* Note that this encodes as little-endian. */
415 : :
416 [ + + - ]: 64 : switch (c) {
417 : :
418 : 48 : case 0 ... 0xd7ffU:
419 : : case 0xe000U ... 0xffffU:
420 : 48 : out[0] = htole16(c);
421 : 48 : return 1;
422 : :
423 : 16 : case 0x10000U ... 0x10ffffU:
424 : 16 : c -= 0x10000U;
425 : 16 : out[0] = htole16((c >> 10) + 0xd800U);
426 : 16 : out[1] = htole16((c & 0x3ffU) + 0xdc00U);
427 : 16 : return 2;
428 : :
429 : 0 : default: /* A surrogate (invalid) */
430 : 0 : return 0;
431 : : }
432 : : }
433 : :
434 : 24 : char16_t *utf8_to_utf16(const char *s, size_t length) {
435 : : char16_t *n, *p;
436 : : size_t i;
437 : : int r;
438 : :
439 [ - + ]: 24 : assert(s);
440 : :
441 : 24 : n = new(char16_t, length + 1);
442 [ - + ]: 24 : if (!n)
443 : 0 : return NULL;
444 : :
445 : 24 : p = n;
446 : :
447 [ + + ]: 152 : for (i = 0; i < length;) {
448 : : char32_t unichar;
449 : : size_t e;
450 : :
451 : 128 : e = utf8_encoded_expected_len(s[i]);
452 [ + + ]: 128 : if (e <= 1) /* Invalid and single byte characters are copied as they are */
453 : 64 : goto copy;
454 : :
455 [ - + ]: 64 : if (i + e > length) /* sequence longer than input buffer, then copy as-is */
456 : 0 : goto copy;
457 : :
458 : 64 : r = utf8_encoded_to_unichar(s + i, &unichar);
459 [ - + ]: 64 : if (r < 0) /* sequence invalid, then copy as-is */
460 : 0 : goto copy;
461 : :
462 : 64 : p += utf16_encode_unichar(p, unichar);
463 : 64 : i += e;
464 : 64 : continue;
465 : :
466 : 64 : copy:
467 : 64 : *(p++) = htole16(s[i++]);
468 : : }
469 : :
470 : 24 : *p = 0;
471 : 24 : return n;
472 : : }
473 : :
474 : 24 : size_t char16_strlen(const char16_t *s) {
475 : 24 : size_t n = 0;
476 : :
477 [ - + ]: 24 : assert(s);
478 : :
479 [ + + ]: 168 : while (*s != 0)
480 : 144 : n++, s++;
481 : :
482 : 24 : return n;
483 : : }
484 : :
485 : : /* expected size used to encode one unicode char */
486 : 31820 : static int utf8_unichar_to_encoded_len(char32_t unichar) {
487 : :
488 [ - + ]: 31820 : if (unichar < 0x80)
489 : 0 : return 1;
490 [ + + ]: 31820 : if (unichar < 0x800)
491 : 13132 : return 2;
492 [ + + ]: 18688 : if (unichar < 0x10000)
493 : 18676 : return 3;
494 [ + - ]: 12 : if (unichar < 0x200000)
495 : 12 : return 4;
496 [ # # ]: 0 : if (unichar < 0x4000000)
497 : 0 : return 5;
498 : :
499 : 0 : return 6;
500 : : }
501 : :
502 : : /* validate one encoded unicode char and return its length */
503 : 1056512 : int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) {
504 : : char32_t unichar;
505 : : size_t len, i;
506 : : int r;
507 : :
508 [ - + ]: 1056512 : assert(str);
509 [ - + ]: 1056512 : assert(length > 0);
510 : :
511 : : /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */
512 : :
513 : 1056512 : len = utf8_encoded_expected_len(str[0]);
514 [ + + ]: 1056512 : if (len == 0)
515 : 116 : return -EINVAL;
516 : :
517 : : /* Do we have a truncated multi-byte character? */
518 [ + + ]: 1056396 : if (len > length)
519 : 24 : return -EINVAL;
520 : :
521 : : /* ascii is valid */
522 [ + + ]: 1056372 : if (len == 1)
523 : 1019952 : return 1;
524 : :
525 : : /* check if expected encoded chars are available */
526 [ + + ]: 125112 : for (i = 0; i < len; i++)
527 [ + + ]: 93272 : if ((str[i] & 0x80) != 0x80)
528 : 4580 : return -EINVAL;
529 : :
530 : 31840 : r = utf8_encoded_to_unichar(str, &unichar);
531 [ + + ]: 31840 : if (r < 0)
532 : 20 : return r;
533 : :
534 : : /* check if encoded length matches encoded value */
535 [ - + ]: 31820 : if (utf8_unichar_to_encoded_len(unichar) != (int) len)
536 : 0 : return -EINVAL;
537 : :
538 : : /* check if value has valid range */
539 [ - + ]: 31820 : if (!unichar_is_valid(unichar))
540 : 0 : return -EINVAL;
541 : :
542 : 31820 : return (int) len;
543 : : }
544 : :
545 : 24 : size_t utf8_n_codepoints(const char *str) {
546 : 24 : size_t n = 0;
547 : :
548 : : /* Returns the number of UTF-8 codepoints in this string, or (size_t) -1 if the string is not valid UTF-8. */
549 : :
550 [ + + ]: 136 : while (*str != 0) {
551 : : int k;
552 : :
553 : 116 : k = utf8_encoded_valid_unichar(str, (size_t) -1);
554 [ + + ]: 116 : if (k < 0)
555 : 4 : return (size_t) -1;
556 : :
557 : 112 : str += k;
558 : 112 : n++;
559 : : }
560 : :
561 : 20 : return n;
562 : : }
563 : :
564 : 36600 : size_t utf8_console_width(const char *str) {
565 : 36600 : size_t n = 0;
566 : :
567 : : /* Returns the approximate width a string will take on screen when printed on a character cell
568 : : * terminal/console. */
569 : :
570 [ + + ]: 198608 : while (*str) {
571 : : int w;
572 : :
573 : 162012 : w = utf8_char_console_width(str);
574 [ + + ]: 162012 : if (w < 0)
575 : 4 : return (size_t) -1;
576 : :
577 : 162008 : n += w;
578 : 162008 : str = utf8_next_char(str);
579 : : }
580 : :
581 : 36596 : return n;
582 : : }
|