Bump versions.
[gsasl.git] / gl / striconv.c
blobea4fa2979edce1a03b042e608fa64c44eca136ae
1 /* Charset conversion.
2 Copyright (C) 2001-2007 Free Software Foundation, Inc.
3 Written by Bruno Haible and Simon Josefsson.
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.
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.
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. */
19 #include <config.h>
21 /* Specification. */
22 #include "striconv.h"
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #if HAVE_ICONV
29 # include <iconv.h>
30 /* Get MB_LEN_MAX, CHAR_BIT. */
31 # include <limits.h>
32 #endif
34 #include "c-strcase.h"
36 #ifndef SIZE_MAX
37 # define SIZE_MAX ((size_t) -1)
38 #endif
41 #if HAVE_ICONV
43 int
44 mem_cd_iconv (const char *src, size_t srclen, iconv_t cd,
45 char **resultp, size_t *lengthp)
47 # define tmpbufsize 4096
48 size_t length;
49 char *result;
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
58 /* Determine the length we need. */
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;
68 while (insize > 0)
70 char *outptr = tmpbuf;
71 size_t outsize = tmpbufsize;
72 size_t res = iconv (cd,
73 (ICONV_CONST char **) &inptr, &insize,
74 &outptr, &outsize);
76 if (res == (size_t)(-1))
78 if (errno == E2BIG)
80 else if (errno == EINVAL)
81 break;
82 else
83 return -1;
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)
92 errno = EILSEQ;
93 return -1;
95 # endif
96 count += outptr - tmpbuf;
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)
102 char *outptr = tmpbuf;
103 size_t outsize = tmpbufsize;
104 size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
106 if (res == (size_t)(-1))
107 return -1;
108 count += outptr - tmpbuf;
110 # endif
111 length = count;
112 # undef tmpbuf
115 if (length == 0)
117 *lengthp = 0;
118 return 0;
120 if (*resultp != NULL && *lengthp >= length)
121 result = *resultp;
122 else
124 result = (char *) malloc (length);
125 if (result == NULL)
127 errno = ENOMEM;
128 return -1;
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
139 /* Do the conversion for real. */
141 const char *inptr = src;
142 size_t insize = srclen;
143 char *outptr = result;
144 size_t outsize = length;
146 while (insize > 0)
148 size_t res = iconv (cd,
149 (ICONV_CONST char **) &inptr, &insize,
150 &outptr, &outsize);
152 if (res == (size_t)(-1))
154 if (errno == EINVAL)
155 break;
156 else
157 goto fail;
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)
166 errno = EILSEQ;
167 goto fail;
169 # endif
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)
175 size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
177 if (res == (size_t)(-1))
178 goto fail;
180 # endif
181 if (outsize != 0)
182 abort ();
185 *resultp = result;
186 *lengthp = length;
188 return 0;
190 fail:
192 if (result != *resultp)
194 int saved_errno = errno;
195 free (result);
196 errno = saved_errno;
198 return -1;
200 # undef tmpbufsize
203 char *
204 str_cd_iconv (const char *src, iconv_t cd)
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. */
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;
224 if (retval < 0)
226 if (result != NULL)
227 abort ();
228 return NULL;
231 /* Add the terminating NUL byte. */
232 final_result =
233 (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
234 if (final_result == NULL)
236 if (result != NULL)
237 free (result);
238 errno = ENOMEM;
239 return NULL;
241 final_result[length] = '\0';
243 return final_result;
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);
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;
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;
265 result_size += 1; /* for the terminating NUL */
267 result = (char *) malloc (result_size);
268 if (result == NULL)
270 errno = ENOMEM;
271 return NULL;
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
281 /* Do the conversion. */
283 char *outptr = result;
284 size_t outbytes_remaining = result_size - 1;
286 for (;;)
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);
294 if (res == (size_t)(-1))
296 if (errno == EINVAL)
297 break;
298 else if (errno == E2BIG)
300 size_t used = outptr - result;
301 size_t newsize = result_size * 2;
302 char *newresult;
304 if (!(newsize > result_size))
306 errno = ENOMEM;
307 goto failed;
309 newresult = (char *) realloc (result, newsize);
310 if (newresult == NULL)
312 errno = ENOMEM;
313 goto failed;
315 result = newresult;
316 result_size = newsize;
317 outptr = result + used;
318 outbytes_remaining = result_size - 1 - used;
320 else
321 goto failed;
323 else
324 break;
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 (;;)
331 /* Here outptr + outbytes_remaining = result + result_size - 1. */
332 size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
334 if (res == (size_t)(-1))
336 if (errno == E2BIG)
338 size_t used = outptr - result;
339 size_t newsize = result_size * 2;
340 char *newresult;
342 if (!(newsize > result_size))
344 errno = ENOMEM;
345 goto failed;
347 newresult = (char *) realloc (result, newsize);
348 if (newresult == NULL)
350 errno = ENOMEM;
351 goto failed;
353 result = newresult;
354 result_size = newsize;
355 outptr = result + used;
356 outbytes_remaining = result_size - 1 - used;
358 else
359 goto failed;
361 else
362 break;
364 # endif
366 /* Add the terminating NUL byte. */
367 *outptr++ = '\0';
369 length = outptr - result;
372 /* Give away unused memory. */
373 if (length < result_size)
375 char *smaller_result = (char *) realloc (result, length);
377 if (smaller_result != NULL)
378 result = smaller_result;
381 return result;
383 failed:
385 int saved_errno = errno;
386 free (result);
387 errno = saved_errno;
388 return NULL;
391 # endif
394 #endif
396 char *
397 str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
399 if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
401 char *result = strdup (src);
403 if (result == NULL)
404 errno = ENOMEM;
405 return result;
407 else
409 #if HAVE_ICONV
410 iconv_t cd;
411 char *result;
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)
418 errno = EINVAL;
419 return NULL;
421 # endif
422 cd = iconv_open (to_codeset, from_codeset);
423 if (cd == (iconv_t) -1)
424 return NULL;
426 result = str_cd_iconv (src, cd);
428 if (result == NULL)
430 /* Close cd, but preserve the errno from str_cd_iconv. */
431 int saved_errno = errno;
432 iconv_close (cd);
433 errno = saved_errno;
435 else
437 if (iconv_close (cd) < 0)
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;
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