chiark / gitweb /
Prep v236 : Add missing SPDX-License-Identifier (2/9) src/basic
[elogind.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 }