chiark / gitweb /
core: move status line ellipsation to 50% of the line
[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;
84
85         assert(str);
86
87         c = (unsigned char) str[0];
88         if (c < 0x80)
89                 return 1;
90         if ((c & 0xe0) == 0xc0)
91                 return 2;
92         if ((c & 0xf0) == 0xe0)
93                 return 3;
94         if ((c & 0xf8) == 0xf0)
95                 return 4;
96         if ((c & 0xfc) == 0xf8)
97                 return 5;
98         if ((c & 0xfe) == 0xfc)
99                 return 6;
100
101         return 0;
102 }
103
104 /* decode one unicode char */
105 int utf8_encoded_to_unichar(const char *str) {
106         int unichar, len, i;
107
108         assert(str);
109
110         len = utf8_encoded_expected_len(str);
111
112         switch (len) {
113         case 1:
114                 return (int)str[0];
115         case 2:
116                 unichar = str[0] & 0x1f;
117                 break;
118         case 3:
119                 unichar = (int)str[0] & 0x0f;
120                 break;
121         case 4:
122                 unichar = (int)str[0] & 0x07;
123                 break;
124         case 5:
125                 unichar = (int)str[0] & 0x03;
126                 break;
127         case 6:
128                 unichar = (int)str[0] & 0x01;
129                 break;
130         default:
131                 return -EINVAL;
132         }
133
134         for (i = 1; i < len; i++) {
135                 if (((int)str[i] & 0xc0) != 0x80)
136                         return -EINVAL;
137                 unichar <<= 6;
138                 unichar |= (int)str[i] & 0x3f;
139         }
140
141         return unichar;
142 }
143
144 bool utf8_is_printable_newline(const char* str, size_t length, bool newline) {
145         const uint8_t *p;
146
147         assert(str);
148
149         for (p = (const uint8_t*) str; length;) {
150                 int encoded_len, val;
151
152                 encoded_len = utf8_encoded_valid_unichar((const char *) p);
153                 val = utf8_encoded_to_unichar((const char*) p);
154
155                 if (encoded_len < 0 ||
156                     val < 0 ||
157                     is_unicode_control(val) ||
158                     (!newline && val == '\n'))
159                         return false;
160
161                 length -= encoded_len;
162                 p += encoded_len;
163         }
164
165         return true;
166 }
167
168 const char *utf8_is_valid(const char *str) {
169         const uint8_t *p;
170
171         assert(str);
172
173         for (p = (const uint8_t*) str; *p; ) {
174                 int len;
175
176                 len = utf8_encoded_valid_unichar((const char *)p);
177                 if (len < 0)
178                         return NULL;
179
180                 p += len;
181         }
182
183         return str;
184 }
185
186 char *utf8_escape_invalid(const char *str) {
187         char *p, *s;
188
189         assert(str);
190
191         p = s = malloc(strlen(str) * 4 + 1);
192         if (!p)
193                 return NULL;
194
195         while (*str) {
196                 int len;
197
198                 len = utf8_encoded_valid_unichar(str);
199                 if (len > 0) {
200                         s = mempcpy(s, str, len);
201                         str += len;
202                 } else {
203                         s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER));
204                         str += 1;
205                 }
206         }
207
208         *s = '\0';
209
210         return p;
211 }
212
213 char *ascii_is_valid(const char *str) {
214         const char *p;
215
216         assert(str);
217
218         for (p = str; *p; p++)
219                 if ((unsigned char) *p >= 128)
220                         return NULL;
221
222         return (char*) str;
223 }
224
225 char *utf16_to_utf8(const void *s, size_t length) {
226         char *r;
227         const uint8_t *f;
228         uint8_t *t;
229
230         r = new(char, (length*3+1)/2 + 1);
231         if (!r)
232                 return NULL;
233
234         t = (uint8_t*) r;
235
236         for (f = s; f < (const uint8_t*) s + length; f += 2) {
237                 uint16_t c;
238
239                 c = (f[1] << 8) | f[0];
240
241                 if (c == 0) {
242                         *t = 0;
243                         return r;
244                 } else if (c < 0x80) {
245                         *(t++) = (uint8_t) c;
246                 } else if (c < 0x800) {
247                         *(t++) = (uint8_t) (0xc0 | (c >> 6));
248                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
249                 } else {
250                         *(t++) = (uint8_t) (0xe0 | (c >> 12));
251                         *(t++) = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
252                         *(t++) = (uint8_t) (0x80 | (c & 0x3f));
253                 }
254         }
255
256         *t = 0;
257
258         return r;
259 }
260
261 /* expected size used to encode one unicode char */
262 static int utf8_unichar_to_encoded_len(int unichar) {
263
264         if (unichar < 0x80)
265                 return 1;
266         if (unichar < 0x800)
267                 return 2;
268         if (unichar < 0x10000)
269                 return 3;
270         if (unichar < 0x200000)
271                 return 4;
272         if (unichar < 0x4000000)
273                 return 5;
274
275         return 6;
276 }
277
278 /* validate one encoded unicode char and return its length */
279 int utf8_encoded_valid_unichar(const char *str) {
280         int len, unichar, i;
281
282         assert(str);
283
284         len = utf8_encoded_expected_len(str);
285         if (len == 0)
286                 return -EINVAL;
287
288         /* ascii is valid */
289         if (len == 1)
290                 return 1;
291
292         /* check if expected encoded chars are available */
293         for (i = 0; i < len; i++)
294                 if ((str[i] & 0x80) != 0x80)
295                         return -EINVAL;
296
297         unichar = utf8_encoded_to_unichar(str);
298
299         /* check if encoded length matches encoded value */
300         if (utf8_unichar_to_encoded_len(unichar) != len)
301                 return -EINVAL;
302
303         /* check if value has valid range */
304         if (!is_unicode_valid(unichar))
305                 return -EINVAL;
306
307         return len;
308 }