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