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 7959 : bool unichar_is_valid(char32_t ch) {
39 :
40 7959 : if (ch >= 0x110000) /* End of unicode space */
41 0 : return false;
42 7959 : if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
43 0 : return false;
44 7959 : if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
45 0 : return false;
46 7959 : if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
47 0 : return false;
48 :
49 7959 : return true;
50 : }
51 :
52 372 : 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 381 : return (ch < ' ' && !IN_SET(ch, '\t', '\n')) ||
61 9 : (0x7F <= ch && ch <= 0x9F);
62 : }
63 :
64 : /* count of characters used to encode one unicode char */
65 326601 : static size_t utf8_encoded_expected_len(uint8_t c) {
66 326601 : if (c < 0x80)
67 271826 : return 1;
68 54775 : if ((c & 0xe0) == 0xc0)
69 23974 : return 2;
70 30801 : if ((c & 0xf0) == 0xe0)
71 30611 : return 3;
72 190 : if ((c & 0xf8) == 0xf0)
73 161 : return 4;
74 29 : if ((c & 0xfc) == 0xf8)
75 0 : return 5;
76 29 : if ((c & 0xfe) == 0xfc)
77 0 : return 6;
78 :
79 29 : return 0;
80 : }
81 :
82 : /* decode one unicode char */
83 62441 : int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
84 : char32_t unichar;
85 : size_t len, i;
86 :
87 62441 : assert(str);
88 :
89 62441 : len = utf8_encoded_expected_len(str[0]);
90 :
91 62441 : switch (len) {
92 16822 : case 1:
93 16822 : *ret_unichar = (char32_t)str[0];
94 16822 : return 0;
95 20366 : case 2:
96 20366 : unichar = str[0] & 0x1f;
97 20366 : break;
98 25100 : case 3:
99 25100 : unichar = (char32_t)str[0] & 0x0f;
100 25100 : break;
101 153 : case 4:
102 153 : unichar = (char32_t)str[0] & 0x07;
103 153 : 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 116636 : for (i = 1; i < len; i++) {
115 71023 : if (((char32_t)str[i] & 0xc0) != 0x80)
116 6 : return -EINVAL;
117 :
118 71017 : unichar <<= 6;
119 71017 : unichar |= (char32_t)str[i] & 0x3f;
120 : }
121 :
122 45613 : *ret_unichar = unichar;
123 :
124 45613 : return 0;
125 : }
126 :
127 351 : bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
128 : const char *p;
129 :
130 351 : assert(str);
131 :
132 647 : for (p = str; length > 0;) {
133 : int encoded_len, r;
134 : char32_t val;
135 :
136 373 : encoded_len = utf8_encoded_valid_unichar(p, length);
137 373 : if (encoded_len < 0)
138 77 : return false;
139 372 : assert(encoded_len > 0 && (size_t) encoded_len <= length);
140 :
141 372 : r = utf8_encoded_to_unichar(p, &val);
142 744 : if (r < 0 ||
143 372 : unichar_is_control(val) ||
144 296 : (!newline && val == '\n'))
145 76 : return false;
146 :
147 296 : length -= encoded_len;
148 296 : p += encoded_len;
149 : }
150 :
151 274 : return true;
152 : }
153 :
154 13116 : char *utf8_is_valid(const char *str) {
155 : const char *p;
156 :
157 13116 : assert(str);
158 :
159 13116 : p = str;
160 269627 : while (*p) {
161 : int len;
162 :
163 257629 : len = utf8_encoded_valid_unichar(p, (size_t) -1);
164 257629 : if (len < 0)
165 1118 : return NULL;
166 :
167 256511 : p += len;
168 : }
169 :
170 11998 : return (char*) str;
171 : }
172 :
173 5 : char *utf8_escape_invalid(const char *str) {
174 : char *p, *s;
175 :
176 5 : assert(str);
177 :
178 5 : p = s = malloc(strlen(str) * 4 + 1);
179 5 : if (!p)
180 0 : return NULL;
181 :
182 45 : while (*str) {
183 : int len;
184 :
185 40 : len = utf8_encoded_valid_unichar(str, (size_t) -1);
186 40 : if (len > 0) {
187 33 : s = mempcpy(s, str, len);
188 33 : str += len;
189 : } else {
190 7 : s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER);
191 7 : str += 1;
192 : }
193 : }
194 :
195 5 : *s = '\0';
196 5 : (void) str_realloc(&p);
197 5 : return p;
198 : }
199 :
200 40774 : static int utf8_char_console_width(const char *str) {
201 : char32_t c;
202 : int r;
203 :
204 40774 : r = utf8_encoded_to_unichar(str, &c);
205 40774 : if (r < 0)
206 1 : return r;
207 :
208 : /* TODO: we should detect combining characters */
209 :
210 40773 : return unichar_iswide(c) ? 2 : 1;
211 : }
212 :
213 76 : char *utf8_escape_non_printable_full(const char *str, size_t console_width) {
214 : char *p, *s, *prev_s;
215 76 : size_t n = 0; /* estimated print width */
216 :
217 76 : assert(str);
218 :
219 76 : if (console_width == 0)
220 5 : return strdup("");
221 :
222 71 : p = s = prev_s = malloc(strlen(str) * 4 + 1);
223 71 : if (!p)
224 0 : return NULL;
225 :
226 360 : for (;;) {
227 : int len;
228 431 : char *saved_s = s;
229 :
230 431 : if (!*str) /* done! */
231 34 : goto finish;
232 :
233 397 : len = utf8_encoded_valid_unichar(str, (size_t) -1);
234 397 : if (len > 0) {
235 347 : if (utf8_is_printable(str, len)) {
236 : int w;
237 :
238 271 : w = utf8_char_console_width(str);
239 271 : assert(w >= 0);
240 271 : if (n + w > console_width)
241 17 : goto truncation;
242 :
243 254 : s = mempcpy(s, str, len);
244 254 : str += len;
245 254 : n += w;
246 :
247 : } else {
248 133 : for (; len > 0; len--) {
249 76 : if (n + 4 > console_width)
250 19 : goto truncation;
251 :
252 57 : *(s++) = '\\';
253 57 : *(s++) = 'x';
254 57 : *(s++) = hexchar((int) *str >> 4);
255 57 : *(s++) = hexchar((int) *str);
256 :
257 57 : str += 1;
258 57 : n += 4;
259 : }
260 : }
261 : } else {
262 50 : if (n + 1 > console_width)
263 1 : goto truncation;
264 :
265 49 : s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
266 49 : str += 1;
267 49 : n += 1;
268 : }
269 :
270 360 : prev_s = saved_s;
271 : }
272 :
273 37 : truncation:
274 : /* Try to go back one if we don't have enough space for the ellipsis */
275 37 : if (n + 1 >= console_width)
276 27 : s = prev_s;
277 :
278 37 : s = mempcpy(s, "…", strlen("…"));
279 :
280 71 : finish:
281 71 : *s = '\0';
282 71 : (void) str_realloc(&p);
283 71 : return p;
284 : }
285 :
286 57 : 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 57 : assert(str);
293 :
294 1087 : for (p = str; *p; p++)
295 1032 : if ((unsigned char) *p >= 128)
296 2 : return NULL;
297 :
298 55 : return (char*) str;
299 : }
300 :
301 3266 : 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 3266 : assert(str);
308 :
309 13684 : for (i = 0; i < len; i++)
310 12654 : if ((unsigned char) str[i] >= 128 || str[i] == 0)
311 2236 : return NULL;
312 :
313 1030 : 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 137 : size_t utf8_encode_unichar(char *out_utf8, char32_t g) {
329 :
330 137 : if (g < (1 << 7)) {
331 72 : if (out_utf8)
332 72 : out_utf8[0] = g & 0x7f;
333 72 : return 1;
334 65 : } else if (g < (1 << 11)) {
335 14 : if (out_utf8) {
336 14 : out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f);
337 14 : out_utf8[1] = 0x80 | (g & 0x3f);
338 : }
339 14 : return 2;
340 51 : } else if (g < (1 << 16)) {
341 4 : if (out_utf8) {
342 4 : out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f);
343 4 : out_utf8[1] = 0x80 | ((g >> 6) & 0x3f);
344 4 : out_utf8[2] = 0x80 | (g & 0x3f);
345 : }
346 4 : return 3;
347 47 : } else if (g < (1 << 21)) {
348 47 : if (out_utf8) {
349 47 : out_utf8[0] = 0xf0 | ((g >> 18) & 0x07);
350 47 : out_utf8[1] = 0x80 | ((g >> 12) & 0x3f);
351 47 : out_utf8[2] = 0x80 | ((g >> 6) & 0x3f);
352 47 : out_utf8[3] = 0x80 | (g & 0x3f);
353 : }
354 47 : return 4;
355 : }
356 :
357 0 : return 0;
358 : }
359 :
360 7 : char *utf16_to_utf8(const char16_t *s, size_t length /* bytes! */) {
361 : const uint8_t *f;
362 : char *r, *t;
363 :
364 7 : 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 7 : if (length * 2 < length)
369 0 : return NULL; /* overflow */
370 :
371 7 : r = new(char, length * 2 + 1);
372 7 : if (!r)
373 0 : return NULL;
374 :
375 7 : f = (const uint8_t*) s;
376 7 : t = r;
377 :
378 45 : while (f + 1 < (const uint8_t*) s + length) {
379 : char16_t w1, w2;
380 :
381 : /* see RFC 2781 section 2.2 */
382 :
383 38 : w1 = f[1] << 8 | f[0];
384 38 : f += 2;
385 :
386 38 : if (!utf16_is_surrogate(w1)) {
387 31 : t += utf8_encode_unichar(t, w1);
388 31 : continue;
389 : }
390 :
391 7 : if (utf16_is_trailing_surrogate(w1))
392 1 : continue; /* spurious trailing surrogate, ignore */
393 :
394 6 : if (f + 1 >= (const uint8_t*) s + length)
395 0 : break;
396 :
397 6 : w2 = f[1] << 8 | f[0];
398 6 : f += 2;
399 :
400 6 : if (!utf16_is_trailing_surrogate(w2)) {
401 1 : f -= 2;
402 1 : continue; /* surrogate missing its trailing surrogate, ignore */
403 : }
404 :
405 5 : t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2));
406 : }
407 :
408 7 : *t = 0;
409 7 : return r;
410 : }
411 :
412 16 : size_t utf16_encode_unichar(char16_t *out, char32_t c) {
413 :
414 : /* Note that this encodes as little-endian. */
415 :
416 16 : switch (c) {
417 :
418 12 : case 0 ... 0xd7ffU:
419 : case 0xe000U ... 0xffffU:
420 12 : out[0] = htole16(c);
421 12 : return 1;
422 :
423 4 : case 0x10000U ... 0x10ffffU:
424 4 : c -= 0x10000U;
425 4 : out[0] = htole16((c >> 10) + 0xd800U);
426 4 : out[1] = htole16((c & 0x3ffU) + 0xdc00U);
427 4 : return 2;
428 :
429 0 : default: /* A surrogate (invalid) */
430 0 : return 0;
431 : }
432 : }
433 :
434 6 : 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 6 : assert(s);
440 :
441 6 : n = new(char16_t, length + 1);
442 6 : if (!n)
443 0 : return NULL;
444 :
445 6 : p = n;
446 :
447 38 : for (i = 0; i < length;) {
448 : char32_t unichar;
449 : size_t e;
450 :
451 32 : e = utf8_encoded_expected_len(s[i]);
452 32 : if (e <= 1) /* Invalid and single byte characters are copied as they are */
453 16 : goto copy;
454 :
455 16 : if (i + e > length) /* sequence longer than input buffer, then copy as-is */
456 0 : goto copy;
457 :
458 16 : r = utf8_encoded_to_unichar(s + i, &unichar);
459 16 : if (r < 0) /* sequence invalid, then copy as-is */
460 0 : goto copy;
461 :
462 16 : p += utf16_encode_unichar(p, unichar);
463 16 : i += e;
464 16 : continue;
465 :
466 16 : copy:
467 16 : *(p++) = htole16(s[i++]);
468 : }
469 :
470 6 : *p = 0;
471 6 : return n;
472 : }
473 :
474 6 : size_t char16_strlen(const char16_t *s) {
475 6 : size_t n = 0;
476 :
477 6 : assert(s);
478 :
479 42 : while (*s != 0)
480 36 : n++, s++;
481 :
482 6 : return n;
483 : }
484 :
485 : /* expected size used to encode one unicode char */
486 7955 : static int utf8_unichar_to_encoded_len(char32_t unichar) {
487 :
488 7955 : if (unichar < 0x80)
489 0 : return 1;
490 7955 : if (unichar < 0x800)
491 3283 : return 2;
492 4672 : if (unichar < 0x10000)
493 4669 : return 3;
494 3 : if (unichar < 0x200000)
495 3 : 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 264128 : 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 264128 : assert(str);
509 264128 : 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 264128 : len = utf8_encoded_expected_len(str[0]);
514 264128 : if (len == 0)
515 29 : return -EINVAL;
516 :
517 : /* Do we have a truncated multi-byte character? */
518 264099 : if (len > length)
519 6 : return -EINVAL;
520 :
521 : /* ascii is valid */
522 264093 : if (len == 1)
523 254988 : return 1;
524 :
525 : /* check if expected encoded chars are available */
526 31278 : for (i = 0; i < len; i++)
527 23318 : if ((str[i] & 0x80) != 0x80)
528 1145 : return -EINVAL;
529 :
530 7960 : r = utf8_encoded_to_unichar(str, &unichar);
531 7960 : if (r < 0)
532 5 : return r;
533 :
534 : /* check if encoded length matches encoded value */
535 7955 : if (utf8_unichar_to_encoded_len(unichar) != (int) len)
536 0 : return -EINVAL;
537 :
538 : /* check if value has valid range */
539 7955 : if (!unichar_is_valid(unichar))
540 0 : return -EINVAL;
541 :
542 7955 : return (int) len;
543 : }
544 :
545 6 : size_t utf8_n_codepoints(const char *str) {
546 6 : 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 34 : while (*str != 0) {
551 : int k;
552 :
553 29 : k = utf8_encoded_valid_unichar(str, (size_t) -1);
554 29 : if (k < 0)
555 1 : return (size_t) -1;
556 :
557 28 : str += k;
558 28 : n++;
559 : }
560 :
561 5 : return n;
562 : }
563 :
564 9150 : size_t utf8_console_width(const char *str) {
565 9150 : 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 49652 : while (*str) {
571 : int w;
572 :
573 40503 : w = utf8_char_console_width(str);
574 40503 : if (w < 0)
575 1 : return (size_t) -1;
576 :
577 40502 : n += w;
578 40502 : str = utf8_next_char(str);
579 : }
580 :
581 9149 : return n;
582 : }
|