New module 'mbsnrtowcs'.
[gnulib.git] / lib / mbsnrtowcs.c
1 /* Convert string to wide string.
2    Copyright (C) 2008 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2008.
4
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.
9
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.
14
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/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <wchar.h>
22
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdlib.h>
26
27 #include "minmax.h"
28 #include "strnlen1.h"
29
30
31 extern mbstate_t _gl_mbsrtowcs_state;
32
33 size_t
34 mbsnrtowcs (wchar_t *dest, const char **srcp, size_t srclen, size_t len, mbstate_t *ps)
35 {
36   if (ps == NULL)
37     ps = &_gl_mbsrtowcs_state;
38   {
39     const char *src = *srcp;
40
41     if (dest != NULL)
42       {
43         wchar_t *destptr = dest;
44
45         for (; srclen > 0 && len > 0; destptr++, len--)
46           {
47             size_t src_avail;
48             size_t ret;
49
50             /* An optimized variant of
51                src_avail = strnlen1 (src, MIN (srclen, MB_LEN_MAX));  */
52             if (srclen == 1 || src[0] == '\0')
53               src_avail = 1;
54             else if (srclen == 2 || src[1] == '\0')
55               src_avail = 2;
56             else if (srclen == 3 || src[2] == '\0')
57               src_avail = 3;
58             else if (MB_LEN_MAX <= 4 || srclen == 4 || src[3] == '\0')
59               src_avail = 4;
60             else
61               src_avail = 4 + strnlen1 (src + 4, MIN (srclen, MB_LEN_MAX) - 4);
62
63             /* Parse the next multibyte character.  */
64             ret = mbrtowc (destptr, src, src_avail, ps);
65
66             if (ret == (size_t)(-2))
67               /* Encountered a multibyte character that extends past a '\0' byte
68                  or that is longer than MB_LEN_MAX bytes.  Cannot happen.  */
69               abort ();
70
71             if (ret == (size_t)(-1))
72               goto bad_input;
73             if (ret == 0)
74               {
75                 src = NULL;
76                 /* Here mbsinit (ps).  */
77                 break;
78               }
79             src += ret;
80             srclen -= ret;
81           }
82
83         *srcp = src;
84         return destptr - dest;
85       }
86     else
87       {
88         /* Ignore dest and len, don't store *srcp at the end, and
89            don't clobber *ps.  */
90         mbstate_t state = *ps;
91         size_t totalcount = 0;
92
93         for (; srclen > 0; totalcount++)
94           {
95             size_t src_avail;
96             size_t ret;
97
98             /* An optimized variant of
99                src_avail = strnlen1 (src, MIN (srclen, MB_LEN_MAX));  */
100             if (srclen == 1 || src[0] == '\0')
101               src_avail = 1;
102             else if (srclen == 2 || src[1] == '\0')
103               src_avail = 2;
104             else if (srclen == 3 || src[2] == '\0')
105               src_avail = 3;
106             else if (MB_LEN_MAX <= 4 || srclen == 4 || src[3] == '\0')
107               src_avail = 4;
108             else
109               src_avail = 4 + strnlen1 (src + 4, MIN (srclen, MB_LEN_MAX) - 4);
110
111             /* Parse the next multibyte character.  */
112             ret = mbrtowc (NULL, src, src_avail, &state);
113
114             if (ret == (size_t)(-2))
115               /* Encountered a multibyte character that extends past a '\0' byte
116                  or that is longer than MB_LEN_MAX bytes.  Cannot happen.  */
117               abort ();
118
119             if (ret == (size_t)(-1))
120               goto bad_input2;
121             if (ret == 0)
122               {
123                 /* Here mbsinit (&state).  */
124                 break;
125               }
126             src += ret;
127             srclen -= ret;
128           }
129
130         return totalcount;
131       }
132
133    bad_input:
134     *srcp = src;
135    bad_input2:
136     errno = EILSEQ;
137     return (size_t)(-1);
138   }
139 }