chiark / gitweb /
use more _cleanup_ macro
[elogind.git] / src / shared / utf8.c
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 static inline bool is_unicode_valid(uint32_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 is_unicode_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         return (ch < ' ' && ch != '\t' && ch != '\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 = (unsigned char)str[0];
84
85         if (c < 0x80)
86                 return 1;
87         if ((c & 0xe0) == 0xc0)
88                 return 2;
89         if ((c & 0xf0) == 0xe0)
90                 return 3;
91         if ((c & 0xf8) == 0xf0)
92                 return 4;
93         if ((c & 0xfc) == 0xf8)
94                 return 5;
95         if ((c & 0xfe) == 0xfc)
96                 return 6;
97         return 0;
98 }
99
100 /* decode one unicode char */
101 int utf8_encoded_to_unichar(const char *str) {
102         int unichar;
103         int len;
104         int i;
105
106         len = utf8_encoded_expected_len(str);
107         switch (len) {
108         case 1:
109                 return (int)str[0];
110         case 2:
111                 unichar = str[0] & 0x1f;
112                 break;
113         case 3:
114                 unichar = (int)str[0] & 0x0f;
115                 break;
116         case 4:
117                 unichar = (int)str[0] & 0x07;
118                 break;
119         case 5:
120                 unichar = (int)str[0] & 0x03;
121                 break;
122         case 6:
123                 unichar = (int)str[0] & 0x01;
124                 break;
125         default:
126                 return -1;
127         }
128
129         for (i = 1; i < len; i++) {
130                 if (((int)str[i] & 0xc0) != 0x80)
131                         return -1;
132                 unichar <<= 6;
133                 unichar |= (int)str[i] & 0x3f;
134         }
135
136         return unichar;
137 }
138
139 bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
140         const uint8_t *p;
141
142         assert(str);
143
144         for (p = (const uint8_t*) str; length;) {
145                 int encoded_len = utf8_encoded_valid_unichar((const char *)p);
146                 int val = utf8_encoded_to_unichar((const char*)p);
147
148                 if (encoded_len < 0 || val < 0 || is_unicode_control(val) ||
149                     (!newline && val == '\n'))
150                         return false;
151
152                 length -= encoded_len;
153                 p += encoded_len;
154         }
155
156         return true;
157 }
158
159 const char *utf8_is_valid(const char *str) {
160         const uint8_t *p;
161
162         assert(str);
163
164         for (p = (const uint8_t*) str; *p; ) {
165                 int len;
166
167                 len = utf8_encoded_valid_unichar((const char *)p);
168
169                 if (len < 0)
170                         return NULL;
171
172                 p += len;
173         }
174
175         return str;
176 }
177
178 char *utf8_escape_invalid(const char *str) {
179         char *p, *s;
180
181         assert(str);
182
183         p = s = malloc(strlen(str) * 4 + 1);
184         if (!p)
185                 return NULL;
186
187         while (*str) {
188                 int len;
189
190                 len = utf8_encoded_valid_unichar(str);
191                 if (len > 0) {
192                         s = mempcpy(s, str, len);
193                         str += len;
194                 } else {
195                         s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
196                         str += 1;
197                 }
198         }
199         *s = '\0';
200
201         return p;
202 }
203
204 char *ascii_is_valid(const char *str) {
205         const char *p;
206
207         assert(str);
208
209         for (p = str; *p; p++)
210                 if ((unsigned char) *p >= 128)
211                         return NULL;
212
213         return (char*) str;
214 }
215
216 char *utf16_to_utf8(const void *s, size_t length) {
217         char *r;
218         const uint8_t *f;
219         uint8_t *t;
220
221         r = new(char, (length*3+1)/2 + 1);
222         if (!r)
223                 return NULL;
224
225         t = (uint8_t*) r;
226
227         for (f = s; f < (const uint8_t*) s + length; f += 2) {
228                 uint16_t c;
229
230                 c = (f[1] << 8) | f[0];
231
232                 if (c == 0) {
233                         *t = 0;
234                         return r;
235                 } else if (c < 0x80) {
236                         *(t++) = (uint8_t) c;
237                 } else if (c < 0x800) {
238                         *(t++) = (uint8_t) (0xc0 | (c >> 6));
239                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
240                 } else {
241                         *(t++) = (uint8_t) (0xe0 | (c >> 12));
242                         *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
243                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
244                 }
245         }
246
247         *t = 0;
248
249         return r;
250 }
251
252 /* expected size used to encode one unicode char */
253 static int utf8_unichar_to_encoded_len(int unichar) {
254         if (unichar < 0x80)
255                 return 1;
256         if (unichar < 0x800)
257                 return 2;
258         if (unichar < 0x10000)
259                 return 3;
260         if (unichar < 0x200000)
261                 return 4;
262         if (unichar < 0x4000000)
263                 return 5;
264         return 6;
265 }
266
267 /* validate one encoded unicode char and return its length */
268 int utf8_encoded_valid_unichar(const char *str) {
269         int len;
270         int unichar;
271         int i;
272
273         len = utf8_encoded_expected_len(str);
274         if (len == 0)
275                 return -1;
276
277         /* ascii is valid */
278         if (len == 1)
279                 return 1;
280
281         /* check if expected encoded chars are available */
282         for (i = 0; i < len; i++)
283                 if ((str[i] & 0x80) != 0x80)
284                         return -1;
285
286         unichar = utf8_encoded_to_unichar(str);
287
288         /* check if encoded length matches encoded value */
289         if (utf8_unichar_to_encoded_len(unichar) != len)
290                 return -1;
291
292         /* check if value has valid range */
293         if (!is_unicode_valid(unichar))
294                 return -1;
295
296         return len;
297 }