Provide a robust <wchar.h>. Further simplifications are now
[gnulib.git] / lib / iconvme.c
1 /* Recode strings between character sets, using iconv.
2    Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19
20 /* Get prototype. */
21 #include "iconvme.h"
22
23 /* Get malloc. */
24 #include <stdlib.h>
25
26 /* Get strcmp. */
27 #include <string.h>
28
29 /* Get errno. */
30 #include <errno.h>
31
32 #ifdef _LIBC
33 # define HAVE_ICONV 1
34 #else
35 /* Get strdup. */
36 # include "strdup.h"
37 #endif
38
39 #if HAVE_ICONV
40 /* Get iconv etc. */
41 # include <iconv.h>
42 /* Get MB_LEN_MAX, CHAR_BIT.  */
43 # include <limits.h>
44 #endif
45
46 #ifndef SIZE_MAX
47 # define SIZE_MAX ((size_t) -1)
48 #endif
49
50 /* Convert a zero-terminated string STR from the FROM_CODSET code set
51    to the TO_CODESET code set.  The returned string is allocated using
52    malloc, and must be dellocated by the caller using free.  On
53    failure, NULL is returned and errno holds the error reason.  Note
54    that if TO_CODESET uses \0 for anything but to terminate the
55    string, the caller of this function may have difficulties finding
56    out the length of the output string.  */
57 char *
58 iconv_string (const char *str, const char *from_codeset,
59               const char *to_codeset)
60 {
61   char *dest = NULL;
62 #if HAVE_ICONV
63   iconv_t cd;
64 #endif
65
66   if (strcmp (to_codeset, from_codeset) == 0)
67     return strdup (str);
68
69 #if HAVE_ICONV
70   cd = iconv_open (to_codeset, from_codeset);
71   if (cd == (iconv_t) -1)
72     return NULL;
73
74   dest = iconv_alloc (cd, str);
75
76   if (dest == NULL)
77     {
78       int saved_errno = errno;
79       iconv_close (cd);
80       errno = saved_errno;
81     }
82   else
83     {
84       if (iconv_close (cd) < 0)
85         {
86           int saved_errno2 = errno;
87           /* If we didn't have a real error before, make sure we restore
88              the iconv_close error below. */
89           free (dest);
90           dest = NULL;
91           errno = saved_errno2;
92         }
93     }
94 #else
95   errno = ENOSYS;
96 #endif
97
98   return dest;
99 }
100
101 /* Convert a zero-terminated string STR using iconv descriptor CD.
102    The returned string is allocated using malloc, and must be
103    dellocated by the caller using free.  On failure, NULL is returned
104    and errno holds the error reason.  Note that if the target
105    character set uses \0 for anything but to terminate the string,
106    the caller of this function may have difficulties finding
107    out the length of the output string.  */
108 #if HAVE_ICONV
109 char *
110 iconv_alloc (iconv_t cd, const char *str)
111 {
112   char *dest;
113   char *p = (char *) str;
114   char *outp;
115   size_t inbytes_remaining = strlen (p);
116   /* Guess the maximum length the output string can have.  */
117   size_t outbuf_size = inbytes_remaining + 1;
118   size_t outbytes_remaining;
119   size_t err;
120   int have_error = 0;
121
122   /* Use a worst-case output size guess, so long as that wouldn't be
123      too large for comfort.  It's OK if the guess is wrong so long as
124      it's nonzero.  */
125   size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
126   if (outbuf_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
127     outbuf_size *= MB_LEN_MAX;
128   outbytes_remaining = outbuf_size - 1;
129
130   outp = dest = (char *) malloc (outbuf_size);
131   if (dest == NULL)
132     {
133       errno = ENOMEM;
134       return NULL;
135     }
136
137   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
138 # if defined _LIBICONV_VERSION \
139     || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
140   /* Set to the initial state.  */
141   iconv (cd, NULL, NULL, NULL, NULL);
142 # endif
143
144 again:
145   err = iconv (cd, &p, &inbytes_remaining, &outp, &outbytes_remaining);
146
147   if (err == (size_t) -1)
148     {
149       switch (errno)
150         {
151         case EINVAL:
152           /* Incomplete text, do not report an error */
153           break;
154
155         case E2BIG:
156           {
157             size_t used = outp - dest;
158             size_t newsize = outbuf_size * 2;
159             char *newdest;
160
161             if (newsize <= outbuf_size)
162               {
163                 errno = ENOMEM;
164                 have_error = 1;
165                 goto out;
166               }
167             newdest = (char *) realloc (dest, newsize);
168             if (newdest == NULL)
169               {
170                 errno = ENOMEM;
171                 have_error = 1;
172                 goto out;
173               }
174             dest = newdest;
175             outbuf_size = newsize;
176
177             outp = dest + used;
178             outbytes_remaining = outbuf_size - used - 1;        /* -1 for NUL */
179
180             goto again;
181           }
182           break;
183
184         case EILSEQ:
185           have_error = 1;
186           break;
187
188         default:
189           have_error = 1;
190           break;
191         }
192     }
193 # if !defined _LIBICONV_VERSION && (defined sgi || defined __sgi)
194   /* Irix iconv() inserts a NUL byte if it cannot convert.  */
195   else if (err > 0)
196     {
197       errno = EILSEQ;
198       have_error = 1;
199       goto out;
200     }
201 # endif
202
203 again2:
204   err = iconv (cd, NULL, NULL, &outp, &outbytes_remaining);
205
206   if (err == (size_t) -1)
207     {
208       switch (errno)
209         {
210         case E2BIG:
211           {
212             size_t used = outp - dest;
213             size_t newsize = outbuf_size * 2;
214             char *newdest;
215
216             if (newsize <= outbuf_size)
217               {
218                 errno = ENOMEM;
219                 have_error = 1;
220                 goto out;
221               }
222             newdest = (char *) realloc (dest, newsize);
223             if (newdest == NULL)
224               {
225                 errno = ENOMEM;
226                 have_error = 1;
227                 goto out;
228               }
229             dest = newdest;
230             outbuf_size = newsize;
231
232             outp = dest + used;
233             outbytes_remaining = outbuf_size - used - 1;        /* -1 for NUL */
234
235             goto again2;
236           }
237           break;
238
239         default:
240           have_error = 1;
241           break;
242         }
243     }
244 # if !defined _LIBICONV_VERSION && (defined sgi || defined __sgi)
245   /* Irix iconv() inserts a NUL byte if it cannot convert.  */
246   else if (err > 0)
247     {
248       errno = EILSEQ;
249       have_error = 1;
250       goto out;
251     }
252 # endif
253
254   *outp++ = '\0';
255
256   /* Give away unused memory.  */
257   if (outp - dest < outbuf_size)
258     {
259       char *newdest = (char *) realloc (dest, outp - dest);
260
261       if (newdest != NULL)
262         dest = newdest;
263     }
264
265 out:
266   if (have_error)
267     {
268       int save_errno = errno;
269       free (dest);
270       errno = save_errno;
271       dest = NULL;
272     }
273
274   return dest;
275 }
276 #endif