Imported from ../bash-4.0-rc1.tar.gz.
[platform/upstream/bash.git] / lib / glob / xmbsrtowcs.c
1 /* xmbsrtowcs.c -- replacement function for mbsrtowcs */
2
3 /* Copyright (C) 2002-2004 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 #include <config.h>
22
23 #include <bashansi.h>
24
25 /* <wchar.h>, <wctype.h> and <stdlib.h> are included in "shmbutil.h".
26    If <wchar.h>, <wctype.h>, mbsrtowcs(), exist, HANDLE_MULTIBYTE
27    is defined as 1. */
28 #include <shmbutil.h>
29
30 #if HANDLE_MULTIBYTE
31
32 #ifndef FREE
33 #  define FREE(x)       do { if (x) free (x); } while (0)
34 #endif
35 /* On some locales (ex. ja_JP.sjis), mbsrtowc doesn't convert 0x5c to U<0x5c>.
36    So, this function is made for converting 0x5c to U<0x5c>. */
37
38 static mbstate_t local_state;
39 static int local_state_use = 0;
40
41 size_t
42 xmbsrtowcs (dest, src, len, pstate)
43     wchar_t *dest;
44     const char **src;
45     size_t len;
46     mbstate_t *pstate;
47 {
48   mbstate_t *ps;
49   size_t mblength, wclength, n;
50
51   ps = pstate;
52   if (pstate == NULL)
53     {
54       if (!local_state_use)
55         {
56           memset (&local_state, '\0', sizeof(mbstate_t));
57           local_state_use = 1;
58         }
59       ps = &local_state;
60     }
61
62   n = strlen (*src);
63
64   if (dest == NULL)
65     {
66       wchar_t *wsbuf;
67       const char *mbs;
68       mbstate_t psbuf;
69
70       /* It doesn't matter if malloc fails here, since mbsrtowcs should do
71          the right thing with a NULL first argument. */
72       wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t));
73       mbs = *src;
74       psbuf = *ps;
75
76       wclength = mbsrtowcs (wsbuf, &mbs, n, &psbuf);
77
78       if (wsbuf)
79         free (wsbuf);
80       return wclength;
81     }
82       
83   for (wclength = 0; wclength < len; wclength++, dest++)
84     {
85       if (mbsinit(ps))
86         {
87           if (**src == '\0')
88             {
89               *dest = L'\0';
90               *src = NULL;
91               return (wclength);
92             }
93           else if (**src == '\\')
94             {
95               *dest = L'\\';
96               mblength = 1;
97             }
98           else
99             mblength = mbrtowc(dest, *src, n, ps);
100         }
101       else
102         mblength = mbrtowc(dest, *src, n, ps);
103
104       /* Cannot convert multibyte character to wide character. */
105       if (mblength == (size_t)-1 || mblength == (size_t)-2)
106         return (size_t)-1;
107
108       *src += mblength;
109       n -= mblength;
110
111       /* The multibyte string  has  been  completely  converted,
112          including  the terminating '\0'. */
113       if (*dest == L'\0')
114         {
115           *src = NULL;
116           break;
117         }
118     }
119
120     return (wclength);
121 }
122
123 /* Convert a multibyte string to a wide character string. Memory for the
124    new wide character string is obtained with malloc.
125
126    The return value is the length of the wide character string. Returns a
127    pointer to the wide character string in DESTP. If INDICESP is not NULL,
128    INDICESP stores the pointer to the pointer array. Each pointer is to
129    the first byte of each multibyte character. Memory for the pointer array
130    is obtained with malloc, too.
131    If conversion is failed, the return value is (size_t)-1 and the values
132    of DESTP and INDICESP are NULL. */
133
134 #define WSBUF_INC 32
135
136 size_t
137 xdupmbstowcs (destp, indicesp, src)
138     wchar_t **destp;    /* Store the pointer to the wide character string */
139     char ***indicesp;   /* Store the pointer to the pointer array. */
140     const char *src;    /* Multibyte character string */
141 {
142   const char *p;        /* Conversion start position of src */
143   wchar_t wc;           /* Created wide character by conversion */
144   wchar_t *wsbuf;       /* Buffer for wide characters. */
145   char **indices;       /* Buffer for indices. */
146   size_t wsbuf_size;    /* Size of WSBUF */
147   size_t wcnum;         /* Number of wide characters in WSBUF */
148   mbstate_t state;      /* Conversion State */
149
150   /* In case SRC or DESP is NULL, conversion doesn't take place. */
151   if (src == NULL || destp == NULL)
152     {
153       if (destp)
154         *destp = NULL;
155       return (size_t)-1;
156     }
157
158   memset (&state, '\0', sizeof(mbstate_t));
159   wsbuf_size = WSBUF_INC;
160
161   wsbuf = (wchar_t *) malloc (wsbuf_size * sizeof(wchar_t));
162   if (wsbuf == NULL)
163     {
164       *destp = NULL;
165       return (size_t)-1;
166     }
167
168   indices = NULL;
169   if (indicesp)
170     {
171       indices = (char **) malloc (wsbuf_size * sizeof(char *));
172       if (indices == NULL)
173         {
174           free (wsbuf);
175           *destp = NULL;
176           return (size_t)-1;
177         }
178     }
179
180   p = src;
181   wcnum = 0;
182   do
183     {
184       size_t mblength;  /* Byte length of one multibyte character. */
185
186       if (mbsinit (&state))
187         {
188           if (*p == '\0')
189             {
190               wc = L'\0';
191               mblength = 1;
192             }
193           else if (*p == '\\')
194             {
195               wc = L'\\';
196               mblength = 1;
197             }
198           else
199             mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
200         }
201       else
202         mblength = mbrtowc(&wc, p, MB_LEN_MAX, &state);
203
204       /* Conversion failed. */
205       if (MB_INVALIDCH (mblength))
206         {
207           free (wsbuf);
208           FREE (indices);
209           *destp = NULL;
210           return (size_t)-1;
211         }
212
213       ++wcnum;
214
215       /* Resize buffers when they are not large enough. */
216       if (wsbuf_size < wcnum)
217         {
218           wchar_t *wstmp;
219           char **idxtmp;
220
221           wsbuf_size += WSBUF_INC;
222
223           wstmp = (wchar_t *) realloc (wsbuf, wsbuf_size * sizeof (wchar_t));
224           if (wstmp == NULL)
225             {
226               free (wsbuf);
227               FREE (indices);
228               *destp = NULL;
229               return (size_t)-1;
230             }
231           wsbuf = wstmp;
232
233           if (indicesp)
234             {
235               idxtmp = (char **) realloc (indices, wsbuf_size * sizeof (char **));
236               if (idxtmp == NULL)
237                 {
238                   free (wsbuf);
239                   free (indices);
240                   *destp = NULL;
241                   return (size_t)-1;
242                 }
243               indices = idxtmp;
244             }
245         }
246
247       wsbuf[wcnum - 1] = wc;
248       if (indices)
249         indices[wcnum - 1] = (char *)p;
250       p += mblength;
251     }
252   while (MB_NULLWCH (wc) == 0);
253
254   /* Return the length of the wide character string, not including `\0'. */
255   *destp = wsbuf;
256   if (indicesp != NULL)
257     *indicesp = indices;
258
259   return (wcnum - 1);
260 }
261
262 #endif /* HANDLE_MULTIBYTE */