7abf727b16f1079b84275040469031462ebb35e1
[platform/upstream/bash.git] / lib / glob / xmbsrtowcs.c
1 /* xmbsrtowcs.c -- replacement function for mbsrtowcs */
2
3 /* Copyright (C) 2002-2010 Free Software Foundation, Inc.
4
5    This file is part of GNU Bash, the Bourne Again SHell.
6
7    Bash 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    Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /* Ask for GNU extensions to get extern declaration for mbsnrtowcs if
22    available via glibc. */
23 #ifndef _GNU_SOURCE
24 #  define _GNU_SOURCE 1
25 #endif
26
27 #include <config.h>
28
29 #include <bashansi.h>
30
31 /* <wchar.h>, <wctype.h> and <stdlib.h> are included in "shmbutil.h".
32    If <wchar.h>, <wctype.h>, mbsrtowcs(), exist, HANDLE_MULTIBYTE
33    is defined as 1. */
34 #include <shmbutil.h>
35
36 #if HANDLE_MULTIBYTE
37
38 #ifndef FREE
39 #  define FREE(x)       do { if (x) free (x); } while (0)
40 #endif
41
42 #if ! HAVE_STRCHRNUL
43 extern char *strchrnul __P((const char *, int));
44 #endif
45
46 /* On some locales (ex. ja_JP.sjis), mbsrtowc doesn't convert 0x5c to U<0x5c>.
47    So, this function is made for converting 0x5c to U<0x5c>. */
48
49 static mbstate_t local_state;
50 static int local_state_use = 0;
51
52 size_t
53 xmbsrtowcs (dest, src, len, pstate)
54     wchar_t *dest;
55     const char **src;
56     size_t len;
57     mbstate_t *pstate;
58 {
59   mbstate_t *ps;
60   size_t mblength, wclength, n;
61
62   ps = pstate;
63   if (pstate == NULL)
64     {
65       if (!local_state_use)
66         {
67           memset (&local_state, '\0', sizeof(mbstate_t));
68           local_state_use = 1;
69         }
70       ps = &local_state;
71     }
72
73   n = strlen (*src);
74
75   if (dest == NULL)
76     {
77       wchar_t *wsbuf;
78       const char *mbs;
79       mbstate_t psbuf;
80
81       /* It doesn't matter if malloc fails here, since mbsrtowcs should do
82          the right thing with a NULL first argument. */
83       wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t));
84       mbs = *src;
85       psbuf = *ps;
86
87       wclength = mbsrtowcs (wsbuf, &mbs, n, &psbuf);
88
89       if (wsbuf)
90         free (wsbuf);
91       return wclength;
92     }
93       
94   for (wclength = 0; wclength < len; wclength++, dest++)
95     {
96       if (mbsinit(ps))
97         {
98           if (**src == '\0')
99             {
100               *dest = L'\0';
101               *src = NULL;
102               return (wclength);
103             }
104           else if (**src == '\\')
105             {
106               *dest = L'\\';
107               mblength = 1;
108             }
109           else
110             mblength = mbrtowc(dest, *src, n, ps);
111         }
112       else
113         mblength = mbrtowc(dest, *src, n, ps);
114
115       /* Cannot convert multibyte character to wide character. */
116       if (mblength == (size_t)-1 || mblength == (size_t)-2)
117         return (size_t)-1;
118
119       *src += mblength;
120       n -= mblength;
121
122       /* The multibyte string  has  been  completely  converted,
123          including  the terminating '\0'. */
124       if (*dest == L'\0')
125         {
126           *src = NULL;
127           break;
128         }
129     }
130
131     return (wclength);
132 }
133
134 #if HAVE_MBSNRTOWCS
135 /* Convert a multibyte string to a wide character string. Memory for the
136    new wide character string is obtained with malloc.
137
138    Fast multiple-character version of xdupmbstowcs used when the indices are
139    not required and mbsnrtowcs is available. */
140
141 static size_t
142 xdupmbstowcs2 (destp, src)
143     wchar_t **destp;    /* Store the pointer to the wide character string */
144     const char *src;    /* Multibyte character string */
145 {
146   const char *p;        /* Conversion start position of src */
147   wchar_t *wsbuf;       /* Buffer for wide characters. */
148   size_t wsbuf_size;    /* Size of WSBUF */
149   size_t wcnum;         /* Number of wide characters in WSBUF */
150   mbstate_t state;      /* Conversion State */
151   size_t wcslength;     /* Number of wide characters produced by the conversion. */
152   const char *end_or_backslash;
153   size_t nms;   /* Number of multibyte characters to convert at one time. */
154   mbstate_t tmp_state;
155   const char *tmp_p;
156
157   memset (&state, '\0', sizeof(mbstate_t));
158
159   wsbuf_size = 0;
160   wsbuf = NULL;
161
162   p = src;
163   wcnum = 0;
164   do
165     {
166       end_or_backslash = strchrnul(p, '\\');
167       nms = (end_or_backslash - p);
168       if (*end_or_backslash == '\0')
169         nms++;
170
171       /* Compute the number of produced wide-characters. */
172       tmp_p = p;
173       tmp_state = state;
174       wcslength = mbsnrtowcs(NULL, &tmp_p, nms, 0, &tmp_state);
175
176       /* Conversion failed. */
177       if (wcslength == (size_t)-1)
178         {
179           free (wsbuf);
180           *destp = NULL;
181           return (size_t)-1;
182         }
183
184       /* Resize the buffer if it is not large enough. */
185       if (wsbuf_size < wcnum+wcslength+1)       /* 1 for the L'\0' or the potential L'\\' */
186         {
187           wchar_t *wstmp;
188
189           wsbuf_size = wcnum+wcslength+1;       /* 1 for the L'\0' or the potential L'\\' */
190
191           wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
192           if (wstmp == NULL)
193             {
194               free (wsbuf);
195               *destp = NULL;
196               return (size_t)-1;
197             }
198           wsbuf = wstmp;
199         }
200
201       /* Perform the conversion. This is assumed to return 'wcslength'.
202        * It may set 'p' to NULL. */
203       mbsnrtowcs(wsbuf+wcnum, &p, nms, wsbuf_size-wcnum, &state);
204
205       wcnum += wcslength;
206
207       if (mbsinit (&state) && (p != NULL) && (*p == '\\'))
208         {
209           wsbuf[wcnum++] = L'\\';
210           p++;
211         }
212     }
213   while (p != NULL);
214
215   *destp = wsbuf;
216
217   /* Return the length of the wide character string, not including `\0'. */
218   return wcnum;
219 }
220 #endif /* HAVE_MBSNRTOWCS */
221
222 /* Convert a multibyte string to a wide character string. Memory for the
223    new wide character string is obtained with malloc.
224
225    The return value is the length of the wide character string. Returns a
226    pointer to the wide character string in DESTP. If INDICESP is not NULL,
227    INDICESP stores the pointer to the pointer array. Each pointer is to
228    the first byte of each multibyte character. Memory for the pointer array
229    is obtained with malloc, too.
230    If conversion is failed, the return value is (size_t)-1 and the values
231    of DESTP and INDICESP are NULL. */
232
233 #define WSBUF_INC 32
234
235 size_t
236 xdupmbstowcs (destp, indicesp, src)
237     wchar_t **destp;    /* Store the pointer to the wide character string */
238     char ***indicesp;   /* Store the pointer to the pointer array. */
239     const char *src;    /* Multibyte character string */
240 {
241   const char *p;        /* Conversion start position of src */
242   wchar_t wc;           /* Created wide character by conversion */
243   wchar_t *wsbuf;       /* Buffer for wide characters. */
244   char **indices;       /* Buffer for indices. */
245   size_t wsbuf_size;    /* Size of WSBUF */
246   size_t wcnum;         /* Number of wide characters in WSBUF */
247   mbstate_t state;      /* Conversion State */
248
249   /* In case SRC or DESP is NULL, conversion doesn't take place. */
250   if (src == NULL || destp == NULL)
251     {
252       if (destp)
253         *destp = NULL;
254       return (size_t)-1;
255     }
256
257 #if HAVE_MBSNRTOWCS
258   if (indicesp == NULL)
259     return (xdupmbstowcs2 (destp, src));
260 #endif
261
262   memset (&state, '\0', sizeof(mbstate_t));
263   wsbuf_size = WSBUF_INC;
264
265   wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t));
266   if (wsbuf == NULL)
267     {
268       *destp = NULL;
269       return (size_t)-1;
270     }
271
272   indices = NULL;
273   if (indicesp)
274     {
275       indices = (char **) malloc (wsbuf_size * sizeof(char *));
276       if (indices == NULL)
277         {
278           free (wsbuf);
279           *destp = NULL;
280           return (size_t)-1;
281         }
282     }
283
284   p = src;
285   wcnum = 0;
286   do
287     {
288       size_t mblength;  /* Byte length of one multibyte character. */
289
290       if (mbsinit (&state))
291         {
292           if (*p == '\0')
293             {
294               wc = L'\0';
295               mblength = 1;
296             }
297           else if (*p == '\\')
298             {
299               wc = L'\\';
300               mblength = 1;
301             }
302           else
303             mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
304         }
305       else
306         mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
307
308       /* Conversion failed. */
309       if (MB_INVALIDCH (mblength))
310         {
311           free (wsbuf);
312           FREE (indices);
313           *destp = NULL;
314           return (size_t)-1;
315         }
316
317       ++wcnum;
318
319       /* Resize buffers when they are not large enough. */
320       if (wsbuf_size < wcnum)
321         {
322           wchar_t *wstmp;
323           char **idxtmp;
324
325           wsbuf_size += WSBUF_INC;
326
327           wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
328           if (wstmp == NULL)
329             {
330               free (wsbuf);
331               FREE (indices);
332               *destp = NULL;
333               return (size_t)-1;
334             }
335           wsbuf = wstmp;
336
337           if (indicesp)
338             {
339               idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char **));
340               if (idxtmp == NULL)
341                 {
342                   free (wsbuf);
343                   free (indices);
344                   *destp = NULL;
345                   return (size_t)-1;
346                 }
347               indices = idxtmp;
348             }
349         }
350
351       wsbuf[wcnum - 1] = wc;
352       if (indices)
353         indices[wcnum - 1] = (char *)p;
354       p += mblength;
355     }
356   while (MB_NULLWCH (wc) == 0);
357
358   /* Return the length of the wide character string, not including `\0'. */
359   *destp = wsbuf;
360   if (indicesp != NULL)
361     *indicesp = indices;
362
363   return (wcnum - 1);
364 }
365
366 #endif /* HANDLE_MULTIBYTE */