chiark / gitweb /
licence: remove references to old FSF address
[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 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 /* This file is 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 <stdlib.h>
47 #include <inttypes.h>
48 #include <string.h>
49 #include <stdbool.h>
50
51 #include "utf8.h"
52
53 #define FILTER_CHAR '_'
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 inline bool is_continuation_char(uint8_t ch) {
70         if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
71                 return false;
72         return true;
73 }
74
75 static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
76         *u_ch <<= 6;
77         *u_ch |= ch & 0x3f;
78 }
79
80 static bool is_unicode_control(uint32_t ch) {
81
82         /*
83           0 to ' '-1 is the C0 range.
84           DEL=0x7F, and DEL+1 to 0x9F is C1 range.
85           '\t' is in C0 range, but more or less harmless and commonly used.
86         */
87
88         return (ch < ' ' && ch != '\t') ||
89                 (0x7F <= ch && ch <= 0x9F);
90 }
91
92 char* utf8_is_printable_n(const char* str, size_t length) {
93         uint32_t val = 0;
94         uint32_t min = 0;
95         const uint8_t *p;
96
97         assert(str);
98
99         for (p = (const uint8_t*) str; length; p++, length--) {
100                 if (*p < 128) {
101                         val = *p;
102                 } else {
103                         if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
104                                 min = 128;
105                                 val = (uint32_t) (*p & 0x1e);
106                                 goto ONE_REMAINING;
107                         } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
108                                 min = (1 << 11);
109                                 val = (uint32_t) (*p & 0x0f);
110                                 goto TWO_REMAINING;
111                         } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
112                                 min = (1 << 16);
113                                 val = (uint32_t) (*p & 0x07);
114                         } else
115                                 goto error;
116
117                         p++;
118                         length--;
119                         if (!length || !is_continuation_char(*p))
120                                 goto error;
121                         merge_continuation_char(&val, *p);
122
123                 TWO_REMAINING:
124                         p++;
125                         length--;
126                         if (!is_continuation_char(*p))
127                                 goto error;
128                         merge_continuation_char(&val, *p);
129
130                 ONE_REMAINING:
131                         p++;
132                         length--;
133                         if (!is_continuation_char(*p))
134                                 goto error;
135                         merge_continuation_char(&val, *p);
136
137                         if (val < min)
138                                 goto error;
139                 }
140
141                 if (is_unicode_control(val))
142                         goto error;
143         }
144
145         return (char*) str;
146
147 error:
148         return NULL;
149 }
150
151 static char* utf8_validate(const char *str, char *output) {
152         uint32_t val = 0;
153         uint32_t min = 0;
154         const uint8_t *p, *last;
155         int size;
156         uint8_t *o;
157
158         assert(str);
159
160         o = (uint8_t*) output;
161         for (p = (const uint8_t*) str; *p; p++) {
162                 if (*p < 128) {
163                         if (o)
164                                 *o = *p;
165                 } else {
166                         last = p;
167
168                         if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
169                                 size = 2;
170                                 min = 128;
171                                 val = (uint32_t) (*p & 0x1e);
172                                 goto ONE_REMAINING;
173                         } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
174                                 size = 3;
175                                 min = (1 << 11);
176                                 val = (uint32_t) (*p & 0x0f);
177                                 goto TWO_REMAINING;
178                         } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
179                                 size = 4;
180                                 min = (1 << 16);
181                                 val = (uint32_t) (*p & 0x07);
182                         } else
183                                 goto error;
184
185                         p++;
186                         if (!is_continuation_char(*p))
187                                 goto error;
188                         merge_continuation_char(&val, *p);
189
190                 TWO_REMAINING:
191                         p++;
192                         if (!is_continuation_char(*p))
193                                 goto error;
194                         merge_continuation_char(&val, *p);
195
196                 ONE_REMAINING:
197                         p++;
198                         if (!is_continuation_char(*p))
199                                 goto error;
200                         merge_continuation_char(&val, *p);
201
202                         if (val < min)
203                                 goto error;
204
205                         if (!is_unicode_valid(val))
206                                 goto error;
207
208                         if (o) {
209                                 memcpy(o, last, (size_t) size);
210                                 o += size;
211                         }
212
213                         continue;
214
215                 error:
216                         if (o) {
217                                 *o = FILTER_CHAR;
218                                 p = last; /* We retry at the next character */
219                         } else
220                                 goto failure;
221                 }
222
223                 if (o)
224                         o++;
225         }
226
227         if (o) {
228                 *o = '\0';
229                 return output;
230         }
231
232         return (char*) str;
233
234 failure:
235         return NULL;
236 }
237
238 char* utf8_is_valid (const char *str) {
239         return utf8_validate(str, NULL);
240 }
241
242 char* utf8_filter (const char *str) {
243         char *new_str;
244
245         assert(str);
246
247         new_str = malloc(strlen(str) + 1);
248         if (!new_str)
249                 return NULL;
250
251         return utf8_validate(str, new_str);
252 }
253
254 char *ascii_is_valid(const char *str) {
255         const char *p;
256
257         assert(str);
258
259         for (p = str; *p; p++)
260                 if ((unsigned char) *p >= 128)
261                         return NULL;
262
263         return (char*) str;
264 }
265
266 char *ascii_filter(const char *str) {
267         const char *s;
268         char *r, *d;
269         size_t l;
270
271         assert(str);
272
273         l = strlen(str);
274         r = malloc(l + 1);
275         if (!r)
276                 return NULL;
277
278         for (s = str, d = r; *s; s++)
279                 if ((unsigned char) *s < 128)
280                         *(d++) = *s;
281
282         *d = 0;
283
284         return r;
285 }