chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[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 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 }