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