22022f39c4e0752e88d0257c8bab22c5e6c03d05
[debian/tar] / gnu / mbrtowc.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Convert multibyte character to wide character.
4    Copyright (C) 1999-2002, 2005-2013 Free Software Foundation, Inc.
5    Written by Bruno Haible <bruno@clisp.org>, 2008.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20 #include <config.h>
21
22 /* Specification.  */
23 #include <wchar.h>
24
25 #if GNULIB_defined_mbstate_t
26 /* Implement mbrtowc() on top of mbtowc().  */
27
28 # include <errno.h>
29 # include <stdlib.h>
30
31 # include "localcharset.h"
32 # include "streq.h"
33 # include "verify.h"
34
35
36 verify (sizeof (mbstate_t) >= 4);
37
38 static char internal_state[4];
39
40 size_t
41 mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
42 {
43   char *pstate = (char *)ps;
44
45   if (s == NULL)
46     {
47       pwc = NULL;
48       s = "";
49       n = 1;
50     }
51
52   if (n == 0)
53     return (size_t)(-2);
54
55   /* Here n > 0.  */
56
57   if (pstate == NULL)
58     pstate = internal_state;
59
60   {
61     size_t nstate = pstate[0];
62     char buf[4];
63     const char *p;
64     size_t m;
65
66     switch (nstate)
67       {
68       case 0:
69         p = s;
70         m = n;
71         break;
72       case 3:
73         buf[2] = pstate[3];
74         /*FALLTHROUGH*/
75       case 2:
76         buf[1] = pstate[2];
77         /*FALLTHROUGH*/
78       case 1:
79         buf[0] = pstate[1];
80         p = buf;
81         m = nstate;
82         buf[m++] = s[0];
83         if (n >= 2 && m < 4)
84           {
85             buf[m++] = s[1];
86             if (n >= 3 && m < 4)
87               buf[m++] = s[2];
88           }
89         break;
90       default:
91         errno = EINVAL;
92         return (size_t)(-1);
93       }
94
95     /* Here m > 0.  */
96
97 # if __GLIBC__ || defined __UCLIBC__
98     /* Work around bug <http://sourceware.org/bugzilla/show_bug.cgi?id=9674> */
99     mbtowc (NULL, NULL, 0);
100 # endif
101     {
102       int res = mbtowc (pwc, p, m);
103
104       if (res >= 0)
105         {
106           if (pwc != NULL && ((*pwc == 0) != (res == 0)))
107             abort ();
108           if (nstate >= (res > 0 ? res : 1))
109             abort ();
110           res -= nstate;
111           pstate[0] = 0;
112           return res;
113         }
114
115       /* mbtowc does not distinguish between invalid and incomplete multibyte
116          sequences.  But mbrtowc needs to make this distinction.
117          There are two possible approaches:
118            - Use iconv() and its return value.
119            - Use built-in knowledge about the possible encodings.
120          Given the low quality of implementation of iconv() on the systems that
121          lack mbrtowc(), we use the second approach.
122          The possible encodings are:
123            - 8-bit encodings,
124            - EUC-JP, EUC-KR, GB2312, EUC-TW, BIG5, GB18030, SJIS,
125            - UTF-8.
126          Use specialized code for each.  */
127       if (m >= 4 || m >= MB_CUR_MAX)
128         goto invalid;
129       /* Here MB_CUR_MAX > 1 and 0 < m < 4.  */
130       {
131         const char *encoding = locale_charset ();
132
133         if (STREQ_OPT (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
134           {
135             /* Cf. unistr/u8-mblen.c.  */
136             unsigned char c = (unsigned char) p[0];
137
138             if (c >= 0xc2)
139               {
140                 if (c < 0xe0)
141                   {
142                     if (m == 1)
143                       goto incomplete;
144                   }
145                 else if (c < 0xf0)
146                   {
147                     if (m == 1)
148                       goto incomplete;
149                     if (m == 2)
150                       {
151                         unsigned char c2 = (unsigned char) p[1];
152
153                         if ((c2 ^ 0x80) < 0x40
154                             && (c >= 0xe1 || c2 >= 0xa0)
155                             && (c != 0xed || c2 < 0xa0))
156                           goto incomplete;
157                       }
158                   }
159                 else if (c <= 0xf4)
160                   {
161                     if (m == 1)
162                       goto incomplete;
163                     else /* m == 2 || m == 3 */
164                       {
165                         unsigned char c2 = (unsigned char) p[1];
166
167                         if ((c2 ^ 0x80) < 0x40
168                             && (c >= 0xf1 || c2 >= 0x90)
169                             && (c < 0xf4 || (c == 0xf4 && c2 < 0x90)))
170                           {
171                             if (m == 2)
172                               goto incomplete;
173                             else /* m == 3 */
174                               {
175                                 unsigned char c3 = (unsigned char) p[2];
176
177                                 if ((c3 ^ 0x80) < 0x40)
178                                   goto incomplete;
179                               }
180                           }
181                       }
182                   }
183               }
184             goto invalid;
185           }
186
187         /* As a reference for this code, you can use the GNU libiconv
188            implementation.  Look for uses of the RET_TOOFEW macro.  */
189
190         if (STREQ_OPT (encoding,
191                        "EUC-JP", 'E', 'U', 'C', '-', 'J', 'P', 0, 0, 0))
192           {
193             if (m == 1)
194               {
195                 unsigned char c = (unsigned char) p[0];
196
197                 if ((c >= 0xa1 && c < 0xff) || c == 0x8e || c == 0x8f)
198                   goto incomplete;
199               }
200             if (m == 2)
201               {
202                 unsigned char c = (unsigned char) p[0];
203
204                 if (c == 0x8f)
205                   {
206                     unsigned char c2 = (unsigned char) p[1];
207
208                     if (c2 >= 0xa1 && c2 < 0xff)
209                       goto incomplete;
210                   }
211               }
212             goto invalid;
213           }
214         if (STREQ_OPT (encoding,
215                        "EUC-KR", 'E', 'U', 'C', '-', 'K', 'R', 0, 0, 0)
216             || STREQ_OPT (encoding,
217                           "GB2312", 'G', 'B', '2', '3', '1', '2', 0, 0, 0)
218             || STREQ_OPT (encoding,
219                           "BIG5", 'B', 'I', 'G', '5', 0, 0, 0, 0, 0))
220           {
221             if (m == 1)
222               {
223                 unsigned char c = (unsigned char) p[0];
224
225                 if (c >= 0xa1 && c < 0xff)
226                   goto incomplete;
227               }
228             goto invalid;
229           }
230         if (STREQ_OPT (encoding,
231                        "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0))
232           {
233             if (m == 1)
234               {
235                 unsigned char c = (unsigned char) p[0];
236
237                 if ((c >= 0xa1 && c < 0xff) || c == 0x8e)
238                   goto incomplete;
239               }
240             else /* m == 2 || m == 3 */
241               {
242                 unsigned char c = (unsigned char) p[0];
243
244                 if (c == 0x8e)
245                   goto incomplete;
246               }
247             goto invalid;
248           }
249         if (STREQ_OPT (encoding,
250                        "GB18030", 'G', 'B', '1', '8', '0', '3', '0', 0, 0))
251           {
252             if (m == 1)
253               {
254                 unsigned char c = (unsigned char) p[0];
255
256                 if ((c >= 0x90 && c <= 0xe3) || (c >= 0xf8 && c <= 0xfe))
257                   goto incomplete;
258               }
259             else /* m == 2 || m == 3 */
260               {
261                 unsigned char c = (unsigned char) p[0];
262
263                 if (c >= 0x90 && c <= 0xe3)
264                   {
265                     unsigned char c2 = (unsigned char) p[1];
266
267                     if (c2 >= 0x30 && c2 <= 0x39)
268                       {
269                         if (m == 2)
270                           goto incomplete;
271                         else /* m == 3 */
272                           {
273                             unsigned char c3 = (unsigned char) p[2];
274
275                             if (c3 >= 0x81 && c3 <= 0xfe)
276                               goto incomplete;
277                           }
278                       }
279                   }
280               }
281             goto invalid;
282           }
283         if (STREQ_OPT (encoding, "SJIS", 'S', 'J', 'I', 'S', 0, 0, 0, 0, 0))
284           {
285             if (m == 1)
286               {
287                 unsigned char c = (unsigned char) p[0];
288
289                 if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xea)
290                     || (c >= 0xf0 && c <= 0xf9))
291                   goto incomplete;
292               }
293             goto invalid;
294           }
295
296         /* An unknown multibyte encoding.  */
297         goto incomplete;
298       }
299
300      incomplete:
301       {
302         size_t k = nstate;
303         /* Here 0 <= k < m < 4.  */
304         pstate[++k] = s[0];
305         if (k < m)
306           {
307             pstate[++k] = s[1];
308             if (k < m)
309               pstate[++k] = s[2];
310           }
311         if (k != m)
312           abort ();
313       }
314       pstate[0] = m;
315       return (size_t)(-2);
316
317      invalid:
318       errno = EILSEQ;
319       /* The conversion state is undefined, says POSIX.  */
320       return (size_t)(-1);
321     }
322   }
323 }
324
325 #else
326 /* Override the system's mbrtowc() function.  */
327
328 # undef mbrtowc
329
330 size_t
331 rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
332 {
333 # if MBRTOWC_NULL_ARG2_BUG || MBRTOWC_RETVAL_BUG
334   if (s == NULL)
335     {
336       pwc = NULL;
337       s = "";
338       n = 1;
339     }
340 # endif
341
342 # if MBRTOWC_RETVAL_BUG
343   {
344     static mbstate_t internal_state;
345
346     /* Override mbrtowc's internal state.  We cannot call mbsinit() on the
347        hidden internal state, but we can call it on our variable.  */
348     if (ps == NULL)
349       ps = &internal_state;
350
351     if (!mbsinit (ps))
352       {
353         /* Parse the rest of the multibyte character byte for byte.  */
354         size_t count = 0;
355         for (; n > 0; s++, n--)
356           {
357             wchar_t wc;
358             size_t ret = mbrtowc (&wc, s, 1, ps);
359
360             if (ret == (size_t)(-1))
361               return (size_t)(-1);
362             count++;
363             if (ret != (size_t)(-2))
364               {
365                 /* The multibyte character has been completed.  */
366                 if (pwc != NULL)
367                   *pwc = wc;
368                 return (wc == 0 ? 0 : count);
369               }
370           }
371         return (size_t)(-2);
372       }
373   }
374 # endif
375
376 # if MBRTOWC_NUL_RETVAL_BUG
377   {
378     wchar_t wc;
379     size_t ret = mbrtowc (&wc, s, n, ps);
380
381     if (ret != (size_t)(-1) && ret != (size_t)(-2))
382       {
383         if (pwc != NULL)
384           *pwc = wc;
385         if (wc == 0)
386           ret = 0;
387       }
388     return ret;
389   }
390 # else
391   {
392 #   if MBRTOWC_NULL_ARG1_BUG
393     wchar_t dummy;
394
395     if (pwc == NULL)
396       pwc = &dummy;
397 #   endif
398
399     return mbrtowc (pwc, s, n, ps);
400   }
401 # endif
402 }
403
404 #endif