]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - lib/libc/locale/utf8.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / lib / libc / locale / utf8.c
1 /*-
2  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
3  * Copyright (c) 2002-2004 Tim J. Robbins
4  * All rights reserved.
5  *
6  * Copyright (c) 2011 The FreeBSD Foundation
7  * All rights reserved.
8  * Portions of this software were developed by David Chisnall
9  * under sponsorship from the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/param.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <errno.h>
37 #include <limits.h>
38 #include <runetype.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <wchar.h>
42 #include "mblocal.h"
43
44 extern int __mb_sb_limit;
45
46 static size_t   _UTF8_mbrtowc(wchar_t * __restrict, const char * __restrict,
47                     size_t, mbstate_t * __restrict);
48 static int      _UTF8_mbsinit(const mbstate_t *);
49 static size_t   _UTF8_mbsnrtowcs(wchar_t * __restrict,
50                     const char ** __restrict, size_t, size_t,
51                     mbstate_t * __restrict);
52 static size_t   _UTF8_wcrtomb(char * __restrict, wchar_t,
53                     mbstate_t * __restrict);
54 static size_t   _UTF8_wcsnrtombs(char * __restrict, const wchar_t ** __restrict,
55                     size_t, size_t, mbstate_t * __restrict);
56
57 typedef struct {
58         wchar_t ch;
59         int     want;
60         wchar_t lbound;
61 } _UTF8State;
62
63 int
64 _UTF8_init(struct xlocale_ctype *l, _RuneLocale *rl)
65 {
66
67         l->__mbrtowc = _UTF8_mbrtowc;
68         l->__wcrtomb = _UTF8_wcrtomb;
69         l->__mbsinit = _UTF8_mbsinit;
70         l->__mbsnrtowcs = _UTF8_mbsnrtowcs;
71         l->__wcsnrtombs = _UTF8_wcsnrtombs;
72         l->runes = rl;
73         l->__mb_cur_max = 6;
74         /*
75          * UCS-4 encoding used as the internal representation, so
76          * slots 0x0080-0x00FF are occuped and must be excluded
77          * from the single byte ctype by setting the limit.
78          */
79         l->__mb_sb_limit = 128;
80
81         return (0);
82 }
83
84 static int
85 _UTF8_mbsinit(const mbstate_t *ps)
86 {
87
88         return (ps == NULL || ((const _UTF8State *)ps)->want == 0);
89 }
90
91 static size_t
92 _UTF8_mbrtowc(wchar_t * __restrict pwc, const char * __restrict s, size_t n,
93     mbstate_t * __restrict ps)
94 {
95         _UTF8State *us;
96         int ch, i, mask, want;
97         wchar_t lbound, wch;
98
99         us = (_UTF8State *)ps;
100
101         if (us->want < 0 || us->want > 6) {
102                 errno = EINVAL;
103                 return ((size_t)-1);
104         }
105
106         if (s == NULL) {
107                 s = "";
108                 n = 1;
109                 pwc = NULL;
110         }
111
112         if (n == 0)
113                 /* Incomplete multibyte sequence */
114                 return ((size_t)-2);
115
116         if (us->want == 0) {
117                 /*
118                  * Determine the number of octets that make up this character
119                  * from the first octet, and a mask that extracts the
120                  * interesting bits of the first octet. We already know
121                  * the character is at least two bytes long.
122                  *
123                  * We also specify a lower bound for the character code to
124                  * detect redundant, non-"shortest form" encodings. For
125                  * example, the sequence C0 80 is _not_ a legal representation
126                  * of the null character. This enforces a 1-to-1 mapping
127                  * between character codes and their multibyte representations.
128                  */
129                 ch = (unsigned char)*s;
130                 if ((ch & 0x80) == 0) {
131                         /* Fast path for plain ASCII characters. */
132                         if (pwc != NULL)
133                                 *pwc = ch;
134                         return (ch != '\0' ? 1 : 0);
135                 }
136                 if ((ch & 0xe0) == 0xc0) {
137                         mask = 0x1f;
138                         want = 2;
139                         lbound = 0x80;
140                 } else if ((ch & 0xf0) == 0xe0) {
141                         mask = 0x0f;
142                         want = 3;
143                         lbound = 0x800;
144                 } else if ((ch & 0xf8) == 0xf0) {
145                         mask = 0x07;
146                         want = 4;
147                         lbound = 0x10000;
148                 } else {
149                         /*
150                          * Malformed input; input is not UTF-8.
151                          */
152                         errno = EILSEQ;
153                         return ((size_t)-1);
154                 }
155         } else {
156                 want = us->want;
157                 lbound = us->lbound;
158         }
159
160         /*
161          * Decode the octet sequence representing the character in chunks
162          * of 6 bits, most significant first.
163          */
164         if (us->want == 0)
165                 wch = (unsigned char)*s++ & mask;
166         else
167                 wch = us->ch;
168         for (i = (us->want == 0) ? 1 : 0; i < MIN(want, n); i++) {
169                 if ((*s & 0xc0) != 0x80) {
170                         /*
171                          * Malformed input; bad characters in the middle
172                          * of a character.
173                          */
174                         errno = EILSEQ;
175                         return ((size_t)-1);
176                 }
177                 wch <<= 6;
178                 wch |= *s++ & 0x3f;
179         }
180         if (i < want) {
181                 /* Incomplete multibyte sequence. */
182                 us->want = want - i;
183                 us->lbound = lbound;
184                 us->ch = wch;
185                 return ((size_t)-2);
186         }
187         if (wch < lbound) {
188                 /*
189                  * Malformed input; redundant encoding.
190                  */
191                 errno = EILSEQ;
192                 return ((size_t)-1);
193         }
194         if ((wch >= 0xd800 && wch <= 0xdfff) || wch > 0x10ffff) {
195                 /*
196                  * Malformed input; invalid code points.
197                  */
198                 errno = EILSEQ;
199                 return ((size_t)-1);
200         }
201         if (pwc != NULL)
202                 *pwc = wch;
203         us->want = 0;
204         return (wch == L'\0' ? 0 : want);
205 }
206
207 static size_t
208 _UTF8_mbsnrtowcs(wchar_t * __restrict dst, const char ** __restrict src,
209     size_t nms, size_t len, mbstate_t * __restrict ps)
210 {
211         _UTF8State *us;
212         const char *s;
213         size_t nchr;
214         wchar_t wc;
215         size_t nb;
216
217         us = (_UTF8State *)ps;
218
219         s = *src;
220         nchr = 0;
221
222         if (dst == NULL) {
223                 /*
224                  * The fast path in the loop below is not safe if an ASCII
225                  * character appears as anything but the first byte of a
226                  * multibyte sequence. Check now to avoid doing it in the loop.
227                  */
228                 if (nms > 0 && us->want > 0 && (signed char)*s > 0) {
229                         errno = EILSEQ;
230                         return ((size_t)-1);
231                 }
232                 for (;;) {
233                         if (nms > 0 && (signed char)*s > 0)
234                                 /*
235                                  * Fast path for plain ASCII characters
236                                  * excluding NUL.
237                                  */
238                                 nb = 1;
239                         else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps)) ==
240                             (size_t)-1)
241                                 /* Invalid sequence - mbrtowc() sets errno. */
242                                 return ((size_t)-1);
243                         else if (nb == 0 || nb == (size_t)-2)
244                                 return (nchr);
245                         s += nb;
246                         nms -= nb;
247                         nchr++;
248                 }
249                 /*NOTREACHED*/
250         }
251
252         /*
253          * The fast path in the loop below is not safe if an ASCII
254          * character appears as anything but the first byte of a
255          * multibyte sequence. Check now to avoid doing it in the loop.
256          */
257         if (nms > 0 && len > 0 && us->want > 0 && (signed char)*s > 0) {
258                 errno = EILSEQ;
259                 return ((size_t)-1);
260         }
261         while (len-- > 0) {
262                 if (nms > 0 && (signed char)*s > 0) {
263                         /*
264                          * Fast path for plain ASCII characters
265                          * excluding NUL.
266                          */
267                         *dst = (wchar_t)*s;
268                         nb = 1;
269                 } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps)) ==
270                     (size_t)-1) {
271                         *src = s;
272                         return ((size_t)-1);
273                 } else if (nb == (size_t)-2) {
274                         *src = s + nms;
275                         return (nchr);
276                 } else if (nb == 0) {
277                         *src = NULL;
278                         return (nchr);
279                 }
280                 s += nb;
281                 nms -= nb;
282                 nchr++;
283                 dst++;
284         }
285         *src = s;
286         return (nchr);
287 }
288
289 static size_t
290 _UTF8_wcrtomb(char * __restrict s, wchar_t wc, mbstate_t * __restrict ps)
291 {
292         _UTF8State *us;
293         unsigned char lead;
294         int i, len;
295
296         us = (_UTF8State *)ps;
297
298         if (us->want != 0) {
299                 errno = EINVAL;
300                 return ((size_t)-1);
301         }
302
303         if (s == NULL)
304                 /* Reset to initial shift state (no-op) */
305                 return (1);
306
307         /*
308          * Determine the number of octets needed to represent this character.
309          * We always output the shortest sequence possible. Also specify the
310          * first few bits of the first octet, which contains the information
311          * about the sequence length.
312          */
313         if ((wc & ~0x7f) == 0) {
314                 /* Fast path for plain ASCII characters. */
315                 *s = (char)wc;
316                 return (1);
317         } else if ((wc & ~0x7ff) == 0) {
318                 lead = 0xc0;
319                 len = 2;
320         } else if ((wc & ~0xffff) == 0) {
321                 if (wc >= 0xd800 && wc <= 0xdfff) {
322                         errno = EILSEQ;
323                         return ((size_t)-1);
324                 }
325                 lead = 0xe0;
326                 len = 3;
327         } else if (wc >= 0 && wc <= 0x10ffff) {
328                 lead = 0xf0;
329                 len = 4;
330         } else {
331                 errno = EILSEQ;
332                 return ((size_t)-1);
333         }
334
335         /*
336          * Output the octets representing the character in chunks
337          * of 6 bits, least significant last. The first octet is
338          * a special case because it contains the sequence length
339          * information.
340          */
341         for (i = len - 1; i > 0; i--) {
342                 s[i] = (wc & 0x3f) | 0x80;
343                 wc >>= 6;
344         }
345         *s = (wc & 0xff) | lead;
346
347         return (len);
348 }
349
350 static size_t
351 _UTF8_wcsnrtombs(char * __restrict dst, const wchar_t ** __restrict src,
352     size_t nwc, size_t len, mbstate_t * __restrict ps)
353 {
354         _UTF8State *us;
355         char buf[MB_LEN_MAX];
356         const wchar_t *s;
357         size_t nbytes;
358         size_t nb;
359
360         us = (_UTF8State *)ps;
361
362         if (us->want != 0) {
363                 errno = EINVAL;
364                 return ((size_t)-1);
365         }
366
367         s = *src;
368         nbytes = 0;
369
370         if (dst == NULL) {
371                 while (nwc-- > 0) {
372                         if (0 <= *s && *s < 0x80)
373                                 /* Fast path for plain ASCII characters. */
374                                 nb = 1;
375                         else if ((nb = _UTF8_wcrtomb(buf, *s, ps)) ==
376                             (size_t)-1)
377                                 /* Invalid character - wcrtomb() sets errno. */
378                                 return ((size_t)-1);
379                         if (*s == L'\0')
380                                 return (nbytes + nb - 1);
381                         s++;
382                         nbytes += nb;
383                 }
384                 return (nbytes);
385         }
386
387         while (len > 0 && nwc-- > 0) {
388                 if (0 <= *s && *s < 0x80) {
389                         /* Fast path for plain ASCII characters. */
390                         nb = 1;
391                         *dst = *s;
392                 } else if (len > (size_t)MB_CUR_MAX) {
393                         /* Enough space to translate in-place. */
394                         if ((nb = _UTF8_wcrtomb(dst, *s, ps)) == (size_t)-1) {
395                                 *src = s;
396                                 return ((size_t)-1);
397                         }
398                 } else {
399                         /*
400                          * May not be enough space; use temp. buffer.
401                          */
402                         if ((nb = _UTF8_wcrtomb(buf, *s, ps)) == (size_t)-1) {
403                                 *src = s;
404                                 return ((size_t)-1);
405                         }
406                         if (nb > (int)len)
407                                 /* MB sequence for character won't fit. */
408                                 break;
409                         memcpy(dst, buf, nb);
410                 }
411                 if (*s == L'\0') {
412                         *src = NULL;
413                         return (nbytes + nb - 1);
414                 }
415                 s++;
416                 dst += nb;
417                 len -= nb;
418                 nbytes += nb;
419         }
420         *src = s;
421         return (nbytes);
422 }