(str_cd_iconv): Use the first algorithm if iconv is not from GNU libiconv or
[gnulib.git] / lib / striconv.c
1 /* Charset conversion.
2    Copyright (C) 2001-2007 Free Software Foundation, Inc.
3    Written by Bruno Haible and Simon Josefsson.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "striconv.h"
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #if HAVE_ICONV
29 # include <iconv.h>
30 /* Get MB_LEN_MAX, CHAR_BIT.  */
31 # include <limits.h>
32 #endif
33
34 #include "strdup.h"
35 #include "c-strcase.h"
36
37 #ifndef SIZE_MAX
38 # define SIZE_MAX ((size_t) -1)
39 #endif
40
41
42 #if HAVE_ICONV
43
44 int
45 mem_cd_iconv (const char *src, size_t srclen, iconv_t cd,
46               char **resultp, size_t *lengthp)
47 {
48 # define tmpbufsize 4096
49   size_t length;
50   char *result;
51
52   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
53 # if defined _LIBICONV_VERSION \
54      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
55   /* Set to the initial state.  */
56   iconv (cd, NULL, NULL, NULL, NULL);
57 # endif
58
59   /* Determine the length we need.  */
60   {
61     size_t count = 0;
62     /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
63        libiconv's UCS-4-INTERNAL encoding.  */
64     union { unsigned int align; char buf[tmpbufsize]; } tmp;
65 # define tmpbuf tmp.buf
66     const char *inptr = src;
67     size_t insize = srclen;
68
69     while (insize > 0)
70       {
71         char *outptr = tmpbuf;
72         size_t outsize = tmpbufsize;
73         size_t res = iconv (cd,
74                             (ICONV_CONST char **) &inptr, &insize,
75                             &outptr, &outsize);
76
77         if (res == (size_t)(-1))
78           {
79             if (errno == E2BIG)
80               ;
81             else if (errno == EINVAL)
82               break;
83             else
84               return -1;
85           }
86 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
87         /* Irix iconv() inserts a NUL byte if it cannot convert.
88            NetBSD iconv() inserts a question mark if it cannot convert.
89            Only GNU libiconv and GNU libc are known to prefer to fail rather
90            than doing a lossy conversion.  */
91         else if (res > 0)
92           {
93             errno = EILSEQ;
94             return -1;
95           }
96 # endif
97         count += outptr - tmpbuf;
98       }
99     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
100 # if defined _LIBICONV_VERSION \
101      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
102     {
103       char *outptr = tmpbuf;
104       size_t outsize = tmpbufsize;
105       size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
106
107       if (res == (size_t)(-1))
108         return -1;
109       count += outptr - tmpbuf;
110     }
111 # endif
112     length = count;
113 # undef tmpbuf
114   }
115
116   if (length == 0)
117     {
118       *lengthp = 0;
119       return 0;
120     }
121   result =
122     (char *) (*resultp != NULL ? realloc (*resultp, length) : malloc (length));
123   if (result == NULL)
124     {
125       errno = ENOMEM;
126       return -1;
127     }
128   *resultp = result;
129   *lengthp = length;
130
131   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
132 # if defined _LIBICONV_VERSION \
133      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
134   /* Return to the initial state.  */
135   iconv (cd, NULL, NULL, NULL, NULL);
136 # endif
137
138   /* Do the conversion for real.  */
139   {
140     const char *inptr = src;
141     size_t insize = srclen;
142     char *outptr = result;
143     size_t outsize = length;
144
145     while (insize > 0)
146       {
147         size_t res = iconv (cd,
148                             (ICONV_CONST char **) &inptr, &insize,
149                             &outptr, &outsize);
150
151         if (res == (size_t)(-1))
152           {
153             if (errno == EINVAL)
154               break;
155             else
156               return -1;
157           }
158 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
159         /* Irix iconv() inserts a NUL byte if it cannot convert.
160            NetBSD iconv() inserts a question mark if it cannot convert.
161            Only GNU libiconv and GNU libc are known to prefer to fail rather
162            than doing a lossy conversion.  */
163         else if (res > 0)
164           {
165             errno = EILSEQ;
166             return -1;
167           }
168 # endif
169       }
170     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
171 # if defined _LIBICONV_VERSION \
172      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
173     {
174       size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
175
176       if (res == (size_t)(-1))
177         return -1;
178     }
179 # endif
180     if (outsize != 0)
181       abort ();
182   }
183
184   return 0;
185 # undef tmpbufsize
186 }
187
188 char *
189 str_cd_iconv (const char *src, iconv_t cd)
190 {
191   /* For most encodings, a trailing NUL byte in the input will be converted
192      to a trailing NUL byte in the output.  But not for UTF-7.  So that this
193      function is usable for UTF-7, we have to exclude the NUL byte from the
194      conversion and add it by hand afterwards.  */
195 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
196   /* Irix iconv() inserts a NUL byte if it cannot convert.
197      NetBSD iconv() inserts a question mark if it cannot convert.
198      Only GNU libiconv and GNU libc are known to prefer to fail rather
199      than doing a lossy conversion.  For other iconv() implementations,
200      we have to look at the number of irreversible conversions returned;
201      but this information is lost when iconv() returns for an E2BIG reason.
202      Therefore we cannot use the second, faster algorithm.  */
203
204   char *result = NULL;
205   size_t length;
206   int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length);
207   char *final_result;
208
209   if (retval < 0)
210     {
211       if (result != NULL)
212         {
213           int saved_errno = errno;
214           free (result);
215           errno = saved_errno;
216         }
217       return NULL;
218     }
219
220   /* Add the terminating NUL byte.  */
221   final_result =
222     (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
223   if (final_result == NULL)
224     {
225       if (result != NULL)
226         free (result);
227       errno = ENOMEM;
228       return NULL;
229     }
230   final_result[length] = '\0';
231
232   return final_result;
233
234 # else
235   /* This algorithm is likely faster than the one above.  But it may produce
236      iconv() returns for an E2BIG reason, when the output size guess is too
237      small.  Therefore it can only be used when we don't need the number of
238      irreversible conversions performed.  */
239   char *result;
240   size_t result_size;
241   size_t length;
242   const char *inptr = src;
243   size_t inbytes_remaining = strlen (src);
244
245   /* Make a guess for the worst-case output size, in order to avoid a
246      realloc.  It's OK if the guess is wrong as long as it is not zero and
247      doesn't lead to an integer overflow.  */
248   result_size = inbytes_remaining;
249   {
250     size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
251     if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
252       result_size *= MB_LEN_MAX;
253   }
254   result_size += 1; /* for the terminating NUL */
255
256   result = (char *) malloc (result_size);
257   if (result == NULL)
258     {
259       errno = ENOMEM;
260       return NULL;
261     }
262
263   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
264 # if defined _LIBICONV_VERSION \
265      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
266   /* Set to the initial state.  */
267   iconv (cd, NULL, NULL, NULL, NULL);
268 # endif
269
270   /* Do the conversion.  */
271   {
272     char *outptr = result;
273     size_t outbytes_remaining = result_size - 1;
274
275     for (;;)
276       {
277         /* Here inptr + inbytes_remaining = src + strlen (src),
278                 outptr + outbytes_remaining = result + result_size - 1.  */
279         size_t res = iconv (cd,
280                             (ICONV_CONST char **) &inptr, &inbytes_remaining,
281                             &outptr, &outbytes_remaining);
282
283         if (res == (size_t)(-1))
284           {
285             if (errno == EINVAL)
286               break;
287             else if (errno == E2BIG)
288               {
289                 size_t used = outptr - result;
290                 size_t newsize = result_size * 2;
291                 char *newresult;
292
293                 if (!(newsize > result_size))
294                   {
295                     errno = ENOMEM;
296                     goto failed;
297                   }
298                 newresult = (char *) realloc (result, newsize);
299                 if (newresult == NULL)
300                   {
301                     errno = ENOMEM;
302                     goto failed;
303                   }
304                 result = newresult;
305                 result_size = newsize;
306                 outptr = result + used;
307                 outbytes_remaining = result_size - 1 - used;
308               }
309             else
310               goto failed;
311           }
312         else
313           break;
314       }
315     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
316 # if defined _LIBICONV_VERSION \
317      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
318     for (;;)
319       {
320         /* Here outptr + outbytes_remaining = result + result_size - 1.  */
321         size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
322
323         if (res == (size_t)(-1))
324           {
325             if (errno == E2BIG)
326               {
327                 size_t used = outptr - result;
328                 size_t newsize = result_size * 2;
329                 char *newresult;
330
331                 if (!(newsize > result_size))
332                   {
333                     errno = ENOMEM;
334                     goto failed;
335                   }
336                 newresult = (char *) realloc (result, newsize);
337                 if (newresult == NULL)
338                   {
339                     errno = ENOMEM;
340                     goto failed;
341                   }
342                 result = newresult;
343                 result_size = newsize;
344                 outptr = result + used;
345                 outbytes_remaining = result_size - 1 - used;
346               }
347             else
348               goto failed;
349           }
350         else
351           break;
352       }
353 # endif
354
355     /* Add the terminating NUL byte.  */
356     *outptr++ = '\0';
357
358     length = outptr - result;
359   }
360
361   /* Give away unused memory.  */
362   if (length < result_size)
363     {
364       char *smaller_result = (char *) realloc (result, length);
365
366       if (smaller_result != NULL)
367         result = smaller_result;
368     }
369
370   return result;
371
372  failed:
373   {
374     int saved_errno = errno;
375     free (result);
376     errno = saved_errno;
377     return NULL;
378   }
379
380 # endif
381 }
382
383 #endif
384
385 char *
386 str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
387 {
388   if (c_strcasecmp (from_codeset, to_codeset) == 0)
389     return strdup (src);
390   else
391     {
392 #if HAVE_ICONV
393       iconv_t cd;
394       char *result;
395
396       /* Avoid glibc-2.1 bug with EUC-KR.  */
397 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
398       if (c_strcasecmp (from_codeset, "EUC-KR") == 0
399           || c_strcasecmp (to_codeset, "EUC-KR") == 0)
400         {
401           errno = EINVAL;
402           return NULL;
403         }
404 # endif
405       cd = iconv_open (to_codeset, from_codeset);
406       if (cd == (iconv_t) -1)
407         return NULL;
408
409       result = str_cd_iconv (src, cd);
410
411       if (result == NULL)
412         {
413           /* Close cd, but preserve the errno from str_cd_iconv.  */
414           int saved_errno = errno;
415           iconv_close (cd);
416           errno = saved_errno;
417         }
418       else
419         {
420           if (iconv_close (cd) < 0)
421             {
422               /* Return NULL, but free the allocated memory, and while doing
423                  that, preserve the errno from iconv_close.  */
424               int saved_errno = errno;
425               free (result);
426               errno = saved_errno;
427               return NULL;
428             }
429         }
430       return result;
431 #else
432       /* This is a different error code than if iconv_open existed but didn't
433          support from_codeset and to_codeset, so that the caller can emit
434          an error message such as
435            "iconv() is not supported. Installing GNU libiconv and
436             then reinstalling this package would fix this."  */
437       errno = ENOSYS;
438       return NULL;
439 #endif
440     }
441 }