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