Bash-4.2 patch 30
[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       /* Compensate for taking single byte on wcs conversion failure above. */
220       if (wcslength == 1 && (n == 0 || n == (size_t)-1))
221         {
222           state = tmp_state;
223           p = tmp_p;
224           wsbuf[wcnum++] = *p++;
225         }
226       else
227         wcnum += wcslength;
228
229       if (mbsinit (&state) && (p != NULL) && (*p == '\\'))
230         {
231           wsbuf[wcnum++] = L'\\';
232           p++;
233         }
234     }
235   while (p != NULL);
236
237   *destp = wsbuf;
238
239   /* Return the length of the wide character string, not including `\0'. */
240   return wcnum;
241 }
242 #endif /* HAVE_MBSNRTOWCS */
243
244 /* Convert a multibyte string to a wide character string. Memory for the
245    new wide character string is obtained with malloc.
246
247    The return value is the length of the wide character string. Returns a
248    pointer to the wide character string in DESTP. If INDICESP is not NULL,
249    INDICESP stores the pointer to the pointer array. Each pointer is to
250    the first byte of each multibyte character. Memory for the pointer array
251    is obtained with malloc, too.
252    If conversion is failed, the return value is (size_t)-1 and the values
253    of DESTP and INDICESP are NULL. */
254
255 size_t
256 xdupmbstowcs (destp, indicesp, src)
257     wchar_t **destp;    /* Store the pointer to the wide character string */
258     char ***indicesp;   /* Store the pointer to the pointer array. */
259     const char *src;    /* Multibyte character string */
260 {
261   const char *p;        /* Conversion start position of src */
262   wchar_t wc;           /* Created wide character by conversion */
263   wchar_t *wsbuf;       /* Buffer for wide characters. */
264   char **indices;       /* Buffer for indices. */
265   size_t wsbuf_size;    /* Size of WSBUF */
266   size_t wcnum;         /* Number of wide characters in WSBUF */
267   mbstate_t state;      /* Conversion State */
268
269   /* In case SRC or DESP is NULL, conversion doesn't take place. */
270   if (src == NULL || destp == NULL)
271     {
272       if (destp)
273         *destp = NULL;
274       return (size_t)-1;
275     }
276
277 #if HAVE_MBSNRTOWCS
278   if (indicesp == NULL)
279     return (xdupmbstowcs2 (destp, src));
280 #endif
281
282   memset (&state, '\0', sizeof(mbstate_t));
283   wsbuf_size = WSBUF_INC;
284
285   wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t));
286   if (wsbuf == NULL)
287     {
288       *destp = NULL;
289       return (size_t)-1;
290     }
291
292   indices = NULL;
293   if (indicesp)
294     {
295       indices = (char **) malloc (wsbuf_size * sizeof(char *));
296       if (indices == NULL)
297         {
298           free (wsbuf);
299           *destp = NULL;
300           return (size_t)-1;
301         }
302     }
303
304   p = src;
305   wcnum = 0;
306   do
307     {
308       size_t mblength;  /* Byte length of one multibyte character. */
309
310       if (mbsinit (&state))
311         {
312           if (*p == '\0')
313             {
314               wc = L'\0';
315               mblength = 1;
316             }
317           else if (*p == '\\')
318             {
319               wc = L'\\';
320               mblength = 1;
321             }
322           else
323             mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
324         }
325       else
326         mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
327
328       /* Conversion failed. */
329       if (MB_INVALIDCH (mblength))
330         {
331           free (wsbuf);
332           FREE (indices);
333           *destp = NULL;
334           return (size_t)-1;
335         }
336
337       ++wcnum;
338
339       /* Resize buffers when they are not large enough. */
340       if (wsbuf_size < wcnum)
341         {
342           wchar_t *wstmp;
343           char **idxtmp;
344
345           wsbuf_size += WSBUF_INC;
346
347           wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
348           if (wstmp == NULL)
349             {
350               free (wsbuf);
351               FREE (indices);
352               *destp = NULL;
353               return (size_t)-1;
354             }
355           wsbuf = wstmp;
356
357           if (indicesp)
358             {
359               idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char **));
360               if (idxtmp == NULL)
361                 {
362                   free (wsbuf);
363                   free (indices);
364                   *destp = NULL;
365                   return (size_t)-1;
366                 }
367               indices = idxtmp;
368             }
369         }
370
371       wsbuf[wcnum - 1] = wc;
372       if (indices)
373         indices[wcnum - 1] = (char *)p;
374       p += mblength;
375     }
376   while (MB_NULLWCH (wc) == 0);
377
378   /* Return the length of the wide character string, not including `\0'. */
379   *destp = wsbuf;
380   if (indicesp != NULL)
381     *indicesp = indices;
382
383   return (wcnum - 1);
384 }
385
386 #endif /* HANDLE_MULTIBYTE */