Code

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