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