]> git.cworth.org Git - tar/blob - gnu/mbrtowc.c
upstream: Fix extraction of device nodes.
[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-2010 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 (pstate == NULL)
46     pstate = internal_state;
47
48   if (s == NULL)
49     {
50       pwc = NULL;
51       s = "";
52       n = 1;
53     }
54
55   if (n == 0)
56     return (size_t)(-2);
57
58   /* Here n > 0.  */
59   {
60     size_t nstate = pstate[0];
61     char buf[4];
62     const char *p;
63     size_t m;
64
65     switch (nstate)
66       {
67       case 0:
68         p = s;
69         m = n;
70         break;
71       case 3:
72         buf[2] = pstate[3];
73         /*FALLTHROUGH*/
74       case 2:
75         buf[1] = pstate[2];
76         /*FALLTHROUGH*/
77       case 1:
78         buf[0] = pstate[1];
79         p = buf;
80         m = nstate;
81         buf[m++] = s[0];
82         if (n >= 2 && m < 4)
83           {
84             buf[m++] = s[1];
85             if (n >= 3 && m < 4)
86               buf[m++] = s[2];
87           }
88         break;
89       default:
90         errno = EINVAL;
91         return (size_t)(-1);
92       }
93
94     /* Here m > 0.  */
95
96 # if __GLIBC__
97     /* Work around bug <http://sourceware.org/bugzilla/show_bug.cgi?id=9674> */
98     mbtowc (NULL, NULL, 0);
99 # endif
100     {
101       int res = mbtowc (pwc, p, m);
102
103       if (res >= 0)
104         {
105           if (pwc != NULL && ((*pwc == 0) != (res == 0)))
106             abort ();
107           if (nstate >= (res > 0 ? res : 1))
108             abort ();
109           res -= nstate;
110           pstate[0] = 0;
111           return res;
112         }
113
114       /* mbtowc does not distinguish between invalid and incomplete multibyte
115          sequences.  But mbrtowc needs to make this distinction.
116          There are two possible approaches:
117            - Use iconv() and its return value.
118            - Use built-in knowledge about the possible encodings.
119          Given the low quality of implementation of iconv() on the systems that
120          lack mbrtowc(), we use the second approach.
121          The possible encodings are:
122            - 8-bit encodings,
123            - EUC-JP, EUC-KR, GB2312, EUC-TW, BIG5, GB18030, SJIS,
124            - UTF-8.
125          Use specialized code for each.  */
126       if (m >= 4 || m >= MB_CUR_MAX)
127         goto invalid;
128       /* Here MB_CUR_MAX > 1 and 0 < m < 4.  */
129       {
130         const char *encoding = locale_charset ();
131
132         if (STREQ (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
133           {
134             /* Cf. unistr/u8-mblen.c.  */
135             unsigned char c = (unsigned char) p[0];
136
137             if (c >= 0xc2)
138               {
139                 if (c < 0xe0)
140                   {
141                     if (m == 1)
142                       goto incomplete;
143                   }
144                 else if (c < 0xf0)
145                   {
146                     if (m == 1)
147                       goto incomplete;
148                     if (m == 2)
149                       {
150                         unsigned char c2 = (unsigned char) p[1];
151
152                         if ((c2 ^ 0x80) < 0x40
153                             && (c >= 0xe1 || c2 >= 0xa0)
154                             && (c != 0xed || c2 < 0xa0))
155                           goto incomplete;
156                       }
157                   }
158                 else if (c <= 0xf4)
159                   {
160                     if (m == 1)
161                       goto incomplete;
162                     else /* m == 2 || m == 3 */
163                       {
164                         unsigned char c2 = (unsigned char) p[1];
165
166                         if ((c2 ^ 0x80) < 0x40
167                             && (c >= 0xf1 || c2 >= 0x90)
168                             && (c < 0xf4 || (c == 0xf4 && c2 < 0x90)))
169                           {
170                             if (m == 2)
171                               goto incomplete;
172                             else /* m == 3 */
173                               {
174                                 unsigned char c3 = (unsigned char) p[2];
175
176                                 if ((c3 ^ 0x80) < 0x40)
177                                   goto incomplete;
178                               }
179                           }
180                       }
181                   }
182               }
183             goto invalid;
184           }
185
186         /* As a reference for this code, you can use the GNU libiconv
187            implementation.  Look for uses of the RET_TOOFEW macro.  */
188
189         if (STREQ (encoding, "EUC-JP", 'E', 'U', 'C', '-', 'J', 'P', 0, 0, 0))
190           {
191             if (m == 1)
192               {
193                 unsigned char c = (unsigned char) p[0];
194
195                 if ((c >= 0xa1 && c < 0xff) || c == 0x8e || c == 0x8f)
196                   goto incomplete;
197               }
198             if (m == 2)
199               {
200                 unsigned char c = (unsigned char) p[0];
201
202                 if (c == 0x8f)
203                   {
204                     unsigned char c2 = (unsigned char) p[1];
205
206                     if (c2 >= 0xa1 && c2 < 0xff)
207                       goto incomplete;
208                   }
209               }
210             goto invalid;
211           }
212         if (STREQ (encoding, "EUC-KR", 'E', 'U', 'C', '-', 'K', 'R', 0, 0, 0)
213             || STREQ (encoding, "GB2312", 'G', 'B', '2', '3', '1', '2', 0, 0, 0)
214             || STREQ (encoding, "BIG5", 'B', 'I', 'G', '5', 0, 0, 0, 0, 0))
215           {
216             if (m == 1)
217               {
218                 unsigned char c = (unsigned char) p[0];
219
220                 if (c >= 0xa1 && c < 0xff)
221                   goto incomplete;
222               }
223             goto invalid;
224           }
225         if (STREQ (encoding, "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0))
226           {
227             if (m == 1)
228               {
229                 unsigned char c = (unsigned char) p[0];
230
231                 if ((c >= 0xa1 && c < 0xff) || c == 0x8e)
232                   goto incomplete;
233               }
234             else /* m == 2 || m == 3 */
235               {
236                 unsigned char c = (unsigned char) p[0];
237
238                 if (c == 0x8e)
239                   goto incomplete;
240               }
241             goto invalid;
242           }
243         if (STREQ (encoding, "GB18030", 'G', 'B', '1', '8', '0', '3', '0', 0, 0))
244           {
245             if (m == 1)
246               {
247                 unsigned char c = (unsigned char) p[0];
248
249                 if ((c >= 0x90 && c <= 0xe3) || (c >= 0xf8 && c <= 0xfe))
250                   goto incomplete;
251               }
252             else /* m == 2 || m == 3 */
253               {
254                 unsigned char c = (unsigned char) p[0];
255
256                 if (c >= 0x90 && c <= 0xe3)
257                   {
258                     unsigned char c2 = (unsigned char) p[1];
259
260                     if (c2 >= 0x30 && c2 <= 0x39)
261                       {
262                         if (m == 2)
263                           goto incomplete;
264                         else /* m == 3 */
265                           {
266                             unsigned char c3 = (unsigned char) p[2];
267
268                             if (c3 >= 0x81 && c3 <= 0xfe)
269                               goto incomplete;
270                           }
271                       }
272                   }
273               }
274             goto invalid;
275           }
276         if (STREQ (encoding, "SJIS", 'S', 'J', 'I', 'S', 0, 0, 0, 0, 0))
277           {
278             if (m == 1)
279               {
280                 unsigned char c = (unsigned char) p[0];
281
282                 if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xea)
283                     || (c >= 0xf0 && c <= 0xf9))
284                   goto incomplete;
285               }
286             goto invalid;
287           }
288
289         /* An unknown multibyte encoding.  */
290         goto incomplete;
291       }
292
293      incomplete:
294       {
295         size_t k = nstate;
296         /* Here 0 <= k < m < 4.  */
297         pstate[++k] = s[0];
298         if (k < m)
299           {
300             pstate[++k] = s[1];
301             if (k < m)
302               pstate[++k] = s[2];
303           }
304         if (k != m)
305           abort ();
306       }
307       pstate[0] = m;
308       return (size_t)(-2);
309
310      invalid:
311       errno = EILSEQ;
312       /* The conversion state is undefined, says POSIX.  */
313       return (size_t)(-1);
314     }
315   }
316 }
317
318 #else
319 /* Override the system's mbrtowc() function.  */
320
321 # undef mbrtowc
322
323 size_t
324 rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
325 {
326 # if MBRTOWC_NULL_ARG_BUG || MBRTOWC_RETVAL_BUG
327   if (s == NULL)
328     {
329       pwc = NULL;
330       s = "";
331       n = 1;
332     }
333 # endif
334
335 # if MBRTOWC_RETVAL_BUG
336   {
337     static mbstate_t internal_state;
338
339     /* Override mbrtowc's internal state.  We can not call mbsinit() on the
340        hidden internal state, but we can call it on our variable.  */
341     if (ps == NULL)
342       ps = &internal_state;
343
344     if (!mbsinit (ps))
345       {
346         /* Parse the rest of the multibyte character byte for byte.  */
347         size_t count = 0;
348         for (; n > 0; s++, n--)
349           {
350             wchar_t wc;
351             size_t ret = mbrtowc (&wc, s, 1, ps);
352
353             if (ret == (size_t)(-1))
354               return (size_t)(-1);
355             count++;
356             if (ret != (size_t)(-2))
357               {
358                 /* The multibyte character has been completed.  */
359                 if (pwc != NULL)
360                   *pwc = wc;
361                 return (wc == 0 ? 0 : count);
362               }
363           }
364         return (size_t)(-2);
365       }
366   }
367 # endif
368
369 # if MBRTOWC_NUL_RETVAL_BUG
370   {
371     wchar_t wc;
372     size_t ret = mbrtowc (&wc, s, n, ps);
373
374     if (ret != (size_t)(-1) && ret != (size_t)(-2))
375       {
376         if (pwc != NULL)
377           *pwc = wc;
378         if (wc == 0)
379           ret = 0;
380       }
381     return ret;
382   }
383 # else
384   return mbrtowc (pwc, s, n, ps);
385 # endif
386 }
387
388 #endif