chiark / gitweb /
a8e28accd3b20e41aad631bda2d08a371cd5ee4a
[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 static 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(const char* str, size_t length) {
140         const uint8_t *p;
141
142         assert(str);
143
144         for (p = (const uint8_t*) str; length; p++) {
145                 int encoded_len = utf8_encoded_valid_unichar((const char *)p);
146                 int32_t val = utf8_encoded_to_unichar((const char*)p);
147
148                 if (encoded_len < 0 || val < 0 || is_unicode_control(val))
149                         return false;
150
151                 length -= encoded_len;
152         }
153
154         return true;
155 }
156
157 const char *utf8_is_valid(const char *str) {
158         const uint8_t *p;
159
160         assert(str);
161
162         for (p = (const uint8_t*) str; *p; ) {
163                 int len = utf8_encoded_valid_unichar((const char *)p);
164
165                 if (len < 0)
166                         return NULL;
167
168                 p += len;
169         }
170
171         return str;
172 }
173
174 char *ascii_is_valid(const char *str) {
175         const char *p;
176
177         assert(str);
178
179         for (p = str; *p; p++)
180                 if ((unsigned char) *p >= 128)
181                         return NULL;
182
183         return (char*) str;
184 }
185
186 char *ascii_filter(const char *str) {
187         const char *s;
188         char *r, *d;
189         size_t l;
190
191         assert(str);
192
193         l = strlen(str);
194         r = malloc(l + 1);
195         if (!r)
196                 return NULL;
197
198         for (s = str, d = r; *s; s++)
199                 if ((unsigned char) *s < 128)
200                         *(d++) = *s;
201
202         *d = 0;
203
204         return r;
205 }
206
207 char *utf16_to_utf8(const void *s, size_t length) {
208         char *r;
209         const uint8_t *f;
210         uint8_t *t;
211
212         r = new(char, (length*3+1)/2 + 1);
213         if (!r)
214                 return NULL;
215
216         t = (uint8_t*) r;
217
218         for (f = s; f < (const uint8_t*) s + length; f += 2) {
219                 uint16_t c;
220
221                 c = (f[1] << 8) | f[0];
222
223                 if (c == 0) {
224                         *t = 0;
225                         return r;
226                 } else if (c < 0x80) {
227                         *(t++) = (uint8_t) c;
228                 } else if (c < 0x800) {
229                         *(t++) = (uint8_t) (0xc0 | (c >> 6));
230                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
231                 } else {
232                         *(t++) = (uint8_t) (0xe0 | (c >> 12));
233                         *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
234                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
235                 }
236         }
237
238         *t = 0;
239
240         return r;
241 }
242
243 /* expected size used to encode one unicode char */
244 static int utf8_unichar_to_encoded_len(int unichar) {
245         if (unichar < 0x80)
246                 return 1;
247         if (unichar < 0x800)
248                 return 2;
249         if (unichar < 0x10000)
250                 return 3;
251         if (unichar < 0x200000)
252                 return 4;
253         if (unichar < 0x4000000)
254                 return 5;
255         return 6;
256 }
257
258 /* validate one encoded unicode char and return its length */
259 int utf8_encoded_valid_unichar(const char *str) {
260         int len;
261         int unichar;
262         int i;
263
264         len = utf8_encoded_expected_len(str);
265         if (len == 0)
266                 return -1;
267
268         /* ascii is valid */
269         if (len == 1)
270                 return 1;
271
272         /* check if expected encoded chars are available */
273         for (i = 0; i < len; i++)
274                 if ((str[i] & 0x80) != 0x80)
275                         return -1;
276
277         unichar = utf8_encoded_to_unichar(str);
278
279         /* check if encoded length matches encoded value */
280         if (utf8_unichar_to_encoded_len(unichar) != len)
281                 return -1;
282
283         /* check if value has valid range */
284         if (!is_unicode_valid(unichar))
285                 return -1;
286
287         return len;
288 }