Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / glob / xmbsrtowcs.c
1 /* xmbsrtowcs.c -- replacement function for mbsrtowcs */
2
3 /* Copyright (C) 2002-2013 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       if (indicesp)
287         *indicesp = NULL;
288       return (size_t)-1;
289     }
290
291 #if HAVE_MBSNRTOWCS
292   if (indicesp == NULL)
293     return (xdupmbstowcs2 (destp, src));
294 #endif
295
296   memset (&state, '\0', sizeof(mbstate_t));
297   wsbuf_size = WSBUF_INC;
298
299   wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t));
300   if (wsbuf == NULL)
301     {
302       *destp = NULL;
303       if (indicesp)
304         *indicesp = NULL;
305       return (size_t)-1;
306     }
307
308   indices = NULL;
309   if (indicesp)
310     {
311       indices = (char **) malloc (wsbuf_size * sizeof(char *));
312       if (indices == NULL)
313         {
314           free (wsbuf);
315           *destp = NULL;
316           *indicesp = NULL;
317           return (size_t)-1;
318         }
319     }
320
321   p = src;
322   wcnum = 0;
323   do
324     {
325       size_t mblength;  /* Byte length of one multibyte character. */
326
327       if (mbsinit (&state))
328         {
329           if (*p == '\0')
330             {
331               wc = L'\0';
332               mblength = 1;
333             }
334           else if (*p == '\\')
335             {
336               wc = L'\\';
337               mblength = 1;
338             }
339           else
340             mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
341         }
342       else
343         mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
344
345       /* Conversion failed. */
346       if (MB_INVALIDCH (mblength))
347         {
348           free (wsbuf);
349           FREE (indices);
350           *destp = NULL;
351           if (indicesp)
352             *indicesp = NULL;
353           return (size_t)-1;
354         }
355
356       ++wcnum;
357
358       /* Resize buffers when they are not large enough. */
359       if (wsbuf_size < wcnum)
360         {
361           wchar_t *wstmp;
362           char **idxtmp;
363
364           wsbuf_size += WSBUF_INC;
365
366           wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
367           if (wstmp == NULL)
368             {
369               free (wsbuf);
370               FREE (indices);
371               *destp = NULL;
372               if (indicesp)
373                 *indicesp = NULL;
374               return (size_t)-1;
375             }
376           wsbuf = wstmp;
377
378           if (indicesp)
379             {
380               idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char *));
381               if (idxtmp == NULL)
382                 {
383                   free (wsbuf);
384                   free (indices);
385                   *destp = NULL;
386                   if (indicesp)
387                     *indicesp = NULL;
388                   return (size_t)-1;
389                 }
390               indices = idxtmp;
391             }
392         }
393
394       wsbuf[wcnum - 1] = wc;
395       if (indices)
396         indices[wcnum - 1] = (char *)p;
397       p += mblength;
398     }
399   while (MB_NULLWCH (wc) == 0);
400
401   /* Return the length of the wide character string, not including `\0'. */
402   *destp = wsbuf;
403   if (indicesp != NULL)
404     *indicesp = indices;
405
406   return (wcnum - 1);
407 }
408
409 #endif /* HANDLE_MULTIBYTE */