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