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