Indentation.
[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 PROBABLY_SLOWER
196
197   char *result = NULL;
198   size_t length;
199   int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length);
200   char *final_result;
201
202   if (retval < 0)
203     {
204       if (result != NULL)
205         {
206           int saved_errno = errno;
207           free (result);
208           errno = saved_errno;
209         }
210       return NULL;
211     }
212
213   /* Add the terminating NUL byte.  */
214   final_result =
215     (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
216   if (final_result == NULL)
217     {
218       if (result != NULL)
219         free (result);
220       errno = ENOMEM;
221       return NULL;
222     }
223   final_result[length] = '\0';
224
225   return final_result;
226
227 # else
228
229   char *result;
230   size_t result_size;
231   size_t length;
232   const char *inptr = src;
233   size_t inbytes_remaining = strlen (src);
234
235   /* Make a guess for the worst-case output size, in order to avoid a
236      realloc.  It's OK if the guess is wrong as long as it is not zero and
237      doesn't lead to an integer overflow.  */
238   result_size = inbytes_remaining;
239   {
240     size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
241     if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
242       result_size *= MB_LEN_MAX;
243   }
244   result_size += 1; /* for the terminating NUL */
245
246   result = (char *) malloc (result_size);
247   if (result == NULL)
248     {
249       errno = ENOMEM;
250       return NULL;
251     }
252
253   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
254 # if defined _LIBICONV_VERSION \
255      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
256   /* Set to the initial state.  */
257   iconv (cd, NULL, NULL, NULL, NULL);
258 # endif
259
260   /* Do the conversion.  */
261   {
262     char *outptr = result;
263     size_t outbytes_remaining = result_size - 1;
264
265     for (;;)
266       {
267         /* Here inptr + inbytes_remaining = src + strlen (src),
268                 outptr + outbytes_remaining = result + result_size - 1.  */
269         size_t res = iconv (cd,
270                             (ICONV_CONST char **) &inptr, &inbytes_remaining,
271                             &outptr, &outbytes_remaining);
272
273         if (res == (size_t)(-1))
274           {
275             if (errno == EINVAL)
276               break;
277             else if (errno == E2BIG)
278               {
279                 size_t used = outptr - result;
280                 size_t newsize = result_size * 2;
281                 char *newresult;
282
283                 if (!(newsize > result_size))
284                   {
285                     errno = ENOMEM;
286                     goto failed;
287                   }
288                 newresult = (char *) realloc (result, newsize);
289                 if (newresult == NULL)
290                   {
291                     errno = ENOMEM;
292                     goto failed;
293                   }
294                 result = newresult;
295                 result_size = newsize;
296                 outptr = result + used;
297                 outbytes_remaining = result_size - 1 - used;
298               }
299             else
300               goto failed;
301           }
302 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
303         /* Irix iconv() inserts a NUL byte if it cannot convert.
304            NetBSD iconv() inserts a question mark if it cannot convert.
305            Only GNU libiconv and GNU libc are known to prefer to fail rather
306            than doing a lossy conversion.  */
307         else if (res > 0)
308           {
309             errno = EILSEQ;
310             goto failed;
311           }
312 # endif
313         else
314           break;
315       }
316     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
317 # if defined _LIBICONV_VERSION \
318      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
319     for (;;)
320       {
321         /* Here outptr + outbytes_remaining = result + result_size - 1.  */
322         size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
323
324         if (res == (size_t)(-1))
325           {
326             if (errno == E2BIG)
327               {
328                 size_t used = outptr - result;
329                 size_t newsize = result_size * 2;
330                 char *newresult;
331
332                 if (!(newsize > result_size))
333                   {
334                     errno = ENOMEM;
335                     goto failed;
336                   }
337                 newresult = (char *) realloc (result, newsize);
338                 if (newresult == NULL)
339                   {
340                     errno = ENOMEM;
341                     goto failed;
342                   }
343                 result = newresult;
344                 result_size = newsize;
345                 outptr = result + used;
346                 outbytes_remaining = result_size - 1 - used;
347               }
348             else
349               goto failed;
350           }
351         else
352           break;
353       }
354 # endif
355
356     /* Add the terminating NUL byte.  */
357     *outptr++ = '\0';
358
359     length = outptr - result;
360   }
361
362   /* Give away unused memory.  */
363   if (length < result_size)
364     {
365       char *smaller_result = (char *) realloc (result, length);
366
367       if (smaller_result != NULL)
368         result = smaller_result;
369     }
370
371   return result;
372
373  failed:
374   {
375     int saved_errno = errno;
376     free (result);
377     errno = saved_errno;
378     return NULL;
379   }
380
381 # endif
382 }
383
384 #endif
385
386 char *
387 str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
388 {
389   if (c_strcasecmp (from_codeset, to_codeset) == 0)
390     return strdup (src);
391   else
392     {
393 #if HAVE_ICONV
394       iconv_t cd;
395       char *result;
396
397       /* Avoid glibc-2.1 bug with EUC-KR.  */
398 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
399       if (c_strcasecmp (from_codeset, "EUC-KR") == 0
400           || c_strcasecmp (to_codeset, "EUC-KR") == 0)
401         {
402           errno = EINVAL;
403           return NULL;
404         }
405 # endif
406       cd = iconv_open (to_codeset, from_codeset);
407       if (cd == (iconv_t) -1)
408         return NULL;
409
410       result = str_cd_iconv (src, cd);
411
412       if (result == NULL)
413         {
414           /* Close cd, but preserve the errno from str_cd_iconv.  */
415           int saved_errno = errno;
416           iconv_close (cd);
417           errno = saved_errno;
418         }
419       else
420         {
421           if (iconv_close (cd) < 0)
422             {
423               /* Return NULL, but free the allocated memory, and while doing
424                  that, preserve the errno from iconv_close.  */
425               int saved_errno = errno;
426               free (result);
427               errno = saved_errno;
428               return NULL;
429             }
430         }
431       return result;
432 #else
433       /* This is a different error code than if iconv_open existed but didn't
434          support from_codeset and to_codeset, so that the caller can emit
435          an error message such as
436            "iconv() is not supported. Installing GNU libiconv and
437             then reinstalling this package would fix this."  */
438       errno = ENOSYS;
439       return NULL;
440 #endif
441     }
442 }