Merge branch 'master' of https://github.com/cern-mig/nagios-plugins
[monitoring-plugins.git] / gl / mbrtowc.c
blob5c2650e9520751569df711577333e7d53ecc8c58
1 /* Convert multibyte character to wide character.
2 Copyright (C) 1999-2002, 2005-2010 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)
41 char *pstate = (char *)ps;
43 if (pstate == NULL)
44 pstate = internal_state;
46 if (s == NULL)
48 pwc = NULL;
49 s = "";
50 n = 1;
53 if (n == 0)
54 return (size_t)(-2);
56 /* Here n > 0. */
58 size_t nstate = pstate[0];
59 char buf[4];
60 const char *p;
61 size_t m;
63 switch (nstate)
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)
82 buf[m++] = s[1];
83 if (n >= 3 && m < 4)
84 buf[m++] = s[2];
86 break;
87 default:
88 errno = EINVAL;
89 return (size_t)(-1);
92 /* Here m > 0. */
94 # if __GLIBC__
95 /* Work around bug <http://sourceware.org/bugzilla/show_bug.cgi?id=9674> */
96 mbtowc (NULL, NULL, 0);
97 # endif
99 int res = mbtowc (pwc, p, m);
101 if (res >= 0)
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;
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, GB18030, 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. */
128 const char *encoding = locale_charset ();
130 if (STREQ (encoding, "UTF-8", 'U', 'T', 'F', '-', '8', 0, 0, 0, 0))
132 /* Cf. unistr/u8-mblen.c. */
133 unsigned char c = (unsigned char) p[0];
135 if (c >= 0xc2)
137 if (c < 0xe0)
139 if (m == 1)
140 goto incomplete;
142 else if (c < 0xf0)
144 if (m == 1)
145 goto incomplete;
146 if (m == 2)
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;
156 else if (c <= 0xf4)
158 if (m == 1)
159 goto incomplete;
160 else /* m == 2 || m == 3 */
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)))
168 if (m == 2)
169 goto incomplete;
170 else /* m == 3 */
172 unsigned char c3 = (unsigned char) p[2];
174 if ((c3 ^ 0x80) < 0x40)
175 goto incomplete;
181 goto invalid;
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))
189 if (m == 1)
191 unsigned char c = (unsigned char) p[0];
193 if ((c >= 0xa1 && c < 0xff) || c == 0x8e || c == 0x8f)
194 goto incomplete;
196 if (m == 2)
198 unsigned char c = (unsigned char) p[0];
200 if (c == 0x8f)
202 unsigned char c2 = (unsigned char) p[1];
204 if (c2 >= 0xa1 && c2 < 0xff)
205 goto incomplete;
208 goto invalid;
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))
214 if (m == 1)
216 unsigned char c = (unsigned char) p[0];
218 if (c >= 0xa1 && c < 0xff)
219 goto incomplete;
221 goto invalid;
223 if (STREQ (encoding, "EUC-TW", 'E', 'U', 'C', '-', 'T', 'W', 0, 0, 0))
225 if (m == 1)
227 unsigned char c = (unsigned char) p[0];
229 if ((c >= 0xa1 && c < 0xff) || c == 0x8e)
230 goto incomplete;
232 else /* m == 2 || m == 3 */
234 unsigned char c = (unsigned char) p[0];
236 if (c == 0x8e)
237 goto incomplete;
239 goto invalid;
241 if (STREQ (encoding, "GB18030", 'G', 'B', '1', '8', '0', '3', '0', 0, 0))
243 if (m == 1)
245 unsigned char c = (unsigned char) p[0];
247 if ((c >= 0x90 && c <= 0xe3) || (c >= 0xf8 && c <= 0xfe))
248 goto incomplete;
250 else /* m == 2 || m == 3 */
252 unsigned char c = (unsigned char) p[0];
254 if (c >= 0x90 && c <= 0xe3)
256 unsigned char c2 = (unsigned char) p[1];
258 if (c2 >= 0x30 && c2 <= 0x39)
260 if (m == 2)
261 goto incomplete;
262 else /* m == 3 */
264 unsigned char c3 = (unsigned char) p[2];
266 if (c3 >= 0x81 && c3 <= 0xfe)
267 goto incomplete;
272 goto invalid;
274 if (STREQ (encoding, "SJIS", 'S', 'J', 'I', 'S', 0, 0, 0, 0, 0))
276 if (m == 1)
278 unsigned char c = (unsigned char) p[0];
280 if ((c >= 0x81 && c <= 0x9f) || (c >= 0xe0 && c <= 0xea)
281 || (c >= 0xf0 && c <= 0xf9))
282 goto incomplete;
284 goto invalid;
287 /* An unknown multibyte encoding. */
288 goto incomplete;
291 incomplete:
293 size_t k = nstate;
294 /* Here 0 <= k < m < 4. */
295 pstate[++k] = s[0];
296 if (k < m)
298 pstate[++k] = s[1];
299 if (k < m)
300 pstate[++k] = s[2];
302 if (k != m)
303 abort ();
305 pstate[0] = m;
306 return (size_t)(-2);
308 invalid:
309 errno = EILSEQ;
310 /* The conversion state is undefined, says POSIX. */
311 return (size_t)(-1);
316 #else
317 /* Override the system's mbrtowc() function. */
319 # undef mbrtowc
321 size_t
322 rpl_mbrtowc (wchar_t *pwc, const char *s, size_t n, mbstate_t *ps)
324 # if MBRTOWC_NULL_ARG_BUG || MBRTOWC_RETVAL_BUG
325 if (s == NULL)
327 pwc = NULL;
328 s = "";
329 n = 1;
331 # endif
333 # if MBRTOWC_RETVAL_BUG
335 static mbstate_t internal_state;
337 /* Override mbrtowc's internal state. We can not call mbsinit() on the
338 hidden internal state, but we can call it on our variable. */
339 if (ps == NULL)
340 ps = &internal_state;
342 if (!mbsinit (ps))
344 /* Parse the rest of the multibyte character byte for byte. */
345 size_t count = 0;
346 for (; n > 0; s++, n--)
348 wchar_t wc;
349 size_t ret = mbrtowc (&wc, s, 1, ps);
351 if (ret == (size_t)(-1))
352 return (size_t)(-1);
353 count++;
354 if (ret != (size_t)(-2))
356 /* The multibyte character has been completed. */
357 if (pwc != NULL)
358 *pwc = wc;
359 return (wc == 0 ? 0 : count);
362 return (size_t)(-2);
365 # endif
367 # if MBRTOWC_NUL_RETVAL_BUG
369 wchar_t wc;
370 size_t ret = mbrtowc (&wc, s, n, ps);
372 if (ret != (size_t)(-1) && ret != (size_t)(-2))
374 if (pwc != NULL)
375 *pwc = wc;
376 if (wc == 0)
377 ret = 0;
379 return ret;
381 # else
382 return mbrtowc (pwc, s, n, ps);
383 # endif
386 #endif