chiark / gitweb /
import udev repository
[elogind.git] / src / 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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 Lesser 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  * Lesser General Public License for more details.
39  *
40  * You should have received a copy of the GNU Lesser General Public
41  * License along with this library; if not, write to the
42  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
43  * Boston, MA 02111-1307, 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
54 #define FILTER_CHAR '_'
55
56 static inline bool is_unicode_valid(uint32_t ch) {
57
58         if (ch >= 0x110000) /* End of unicode space */
59                 return false;
60         if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
61                 return false;
62         if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
63                 return false;
64         if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
65                 return false;
66
67         return true;
68 }
69
70 static inline bool is_continuation_char(uint8_t ch) {
71         if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
72                 return false;
73         return true;
74 }
75
76 static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
77         *u_ch <<= 6;
78         *u_ch |= ch & 0x3f;
79 }
80
81 static char* utf8_validate(const char *str, char *output) {
82         uint32_t val = 0;
83         uint32_t min = 0;
84         const uint8_t *p, *last;
85         int size;
86         uint8_t *o;
87
88         assert(str);
89
90         o = (uint8_t*) output;
91         for (p = (const uint8_t*) str; *p; p++) {
92                 if (*p < 128) {
93                         if (o)
94                                 *o = *p;
95                 } else {
96                         last = p;
97
98                         if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
99                                 size = 2;
100                                 min = 128;
101                                 val = (uint32_t) (*p & 0x1e);
102                                 goto ONE_REMAINING;
103                         } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
104                                 size = 3;
105                                 min = (1 << 11);
106                                 val = (uint32_t) (*p & 0x0f);
107                                 goto TWO_REMAINING;
108                         } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
109                                 size = 4;
110                                 min = (1 << 16);
111                                 val = (uint32_t) (*p & 0x07);
112                         } else
113                                 goto error;
114
115                         p++;
116                         if (!is_continuation_char(*p))
117                                 goto error;
118                         merge_continuation_char(&val, *p);
119
120                 TWO_REMAINING:
121                         p++;
122                         if (!is_continuation_char(*p))
123                                 goto error;
124                         merge_continuation_char(&val, *p);
125
126                 ONE_REMAINING:
127                         p++;
128                         if (!is_continuation_char(*p))
129                                 goto error;
130                         merge_continuation_char(&val, *p);
131
132                         if (val < min)
133                                 goto error;
134
135                         if (!is_unicode_valid(val))
136                                 goto error;
137
138                         if (o) {
139                                 memcpy(o, last, (size_t) size);
140                                 o += size;
141                         }
142
143                         continue;
144
145                 error:
146                         if (o) {
147                                 *o = FILTER_CHAR;
148                                 p = last; /* We retry at the next character */
149                         } else
150                                 goto failure;
151                 }
152
153                 if (o)
154                         o++;
155         }
156
157         if (o) {
158                 *o = '\0';
159                 return output;
160         }
161
162         return (char*) str;
163
164 failure:
165         return NULL;
166 }
167
168 char* utf8_is_valid (const char *str) {
169         return utf8_validate(str, NULL);
170 }
171
172 char* utf8_filter (const char *str) {
173         char *new_str;
174
175         assert(str);
176
177         new_str = malloc(strlen(str) + 1);
178         if (!new_str)
179                 return NULL;
180
181         return utf8_validate(str, new_str);
182 }
183
184 char *ascii_is_valid(const char *str) {
185         const char *p;
186
187         assert(str);
188
189         for (p = str; *p; p++)
190                 if ((unsigned char) *p >= 128)
191                         return NULL;
192
193         return (char*) str;
194 }
195
196 char *ascii_filter(const char *str) {
197         char *r, *s, *d;
198         size_t l;
199
200         assert(str);
201
202         l = strlen(str);
203         r = malloc(l + 1);
204         if (!r)
205                 return NULL;
206
207         for (s = r, d = r; *s; s++)
208                 if ((unsigned char) *s < 128)
209                         *(d++) = *s;
210
211         *d = 0;
212
213         return r;
214 }