195b7b453SJohn Marino /* Charset conversion.
2*09d4459fSDaniel Fojt Copyright (C) 2001-2007, 2010-2020 Free Software Foundation, Inc.
395b7b453SJohn Marino Written by Bruno Haible and Simon Josefsson.
495b7b453SJohn Marino
595b7b453SJohn Marino This program is free software; you can redistribute it and/or modify
695b7b453SJohn Marino it under the terms of the GNU General Public License as published by
795b7b453SJohn Marino the Free Software Foundation; either version 3, or (at your option)
895b7b453SJohn Marino any later version.
995b7b453SJohn Marino
1095b7b453SJohn Marino This program is distributed in the hope that it will be useful,
1195b7b453SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
1295b7b453SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1395b7b453SJohn Marino GNU General Public License for more details.
1495b7b453SJohn Marino
1595b7b453SJohn Marino You should have received a copy of the GNU General Public License
16*09d4459fSDaniel Fojt along with this program; if not, see <https://www.gnu.org/licenses/>. */
1795b7b453SJohn Marino
1895b7b453SJohn Marino #include <config.h>
1995b7b453SJohn Marino
2095b7b453SJohn Marino /* Specification. */
2195b7b453SJohn Marino #include "striconv.h"
2295b7b453SJohn Marino
2395b7b453SJohn Marino #include <errno.h>
2495b7b453SJohn Marino #include <stdlib.h>
2595b7b453SJohn Marino #include <string.h>
2695b7b453SJohn Marino
2795b7b453SJohn Marino #if HAVE_ICONV
2895b7b453SJohn Marino # include <iconv.h>
2995b7b453SJohn Marino /* Get MB_LEN_MAX, CHAR_BIT. */
3095b7b453SJohn Marino # include <limits.h>
3195b7b453SJohn Marino #endif
3295b7b453SJohn Marino
3395b7b453SJohn Marino #include "c-strcase.h"
3495b7b453SJohn Marino
3595b7b453SJohn Marino #ifndef SIZE_MAX
3695b7b453SJohn Marino # define SIZE_MAX ((size_t) -1)
3795b7b453SJohn Marino #endif
3895b7b453SJohn Marino
3995b7b453SJohn Marino
4095b7b453SJohn Marino #if HAVE_ICONV
4195b7b453SJohn Marino
4295b7b453SJohn Marino int
mem_cd_iconv(const char * src,size_t srclen,iconv_t cd,char ** resultp,size_t * lengthp)4395b7b453SJohn Marino mem_cd_iconv (const char *src, size_t srclen, iconv_t cd,
4495b7b453SJohn Marino char **resultp, size_t *lengthp)
4595b7b453SJohn Marino {
4695b7b453SJohn Marino # define tmpbufsize 4096
4795b7b453SJohn Marino size_t length;
4895b7b453SJohn Marino char *result;
4995b7b453SJohn Marino
5095b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */
5195b7b453SJohn Marino # if defined _LIBICONV_VERSION \
52200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
53200fbe8dSJohn Marino || defined __sun)
5495b7b453SJohn Marino /* Set to the initial state. */
5595b7b453SJohn Marino iconv (cd, NULL, NULL, NULL, NULL);
5695b7b453SJohn Marino # endif
5795b7b453SJohn Marino
5895b7b453SJohn Marino /* Determine the length we need. */
5995b7b453SJohn Marino {
6095b7b453SJohn Marino size_t count = 0;
6195b7b453SJohn Marino /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
6295b7b453SJohn Marino libiconv's UCS-4-INTERNAL encoding. */
6395b7b453SJohn Marino union { unsigned int align; char buf[tmpbufsize]; } tmp;
6495b7b453SJohn Marino # define tmpbuf tmp.buf
6595b7b453SJohn Marino const char *inptr = src;
6695b7b453SJohn Marino size_t insize = srclen;
6795b7b453SJohn Marino
6895b7b453SJohn Marino while (insize > 0)
6995b7b453SJohn Marino {
7095b7b453SJohn Marino char *outptr = tmpbuf;
7195b7b453SJohn Marino size_t outsize = tmpbufsize;
7295b7b453SJohn Marino size_t res = iconv (cd,
7395b7b453SJohn Marino (ICONV_CONST char **) &inptr, &insize,
7495b7b453SJohn Marino &outptr, &outsize);
7595b7b453SJohn Marino
7695b7b453SJohn Marino if (res == (size_t)(-1))
7795b7b453SJohn Marino {
7895b7b453SJohn Marino if (errno == E2BIG)
7995b7b453SJohn Marino ;
8095b7b453SJohn Marino else if (errno == EINVAL)
8195b7b453SJohn Marino break;
8295b7b453SJohn Marino else
8395b7b453SJohn Marino return -1;
8495b7b453SJohn Marino }
85200fbe8dSJohn Marino # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__)
8695b7b453SJohn Marino /* Irix iconv() inserts a NUL byte if it cannot convert.
8795b7b453SJohn Marino NetBSD iconv() inserts a question mark if it cannot convert.
8895b7b453SJohn Marino Only GNU libiconv and GNU libc are known to prefer to fail rather
8995b7b453SJohn Marino than doing a lossy conversion. */
9095b7b453SJohn Marino else if (res > 0)
9195b7b453SJohn Marino {
9295b7b453SJohn Marino errno = EILSEQ;
9395b7b453SJohn Marino return -1;
9495b7b453SJohn Marino }
9595b7b453SJohn Marino # endif
9695b7b453SJohn Marino count += outptr - tmpbuf;
9795b7b453SJohn Marino }
9895b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */
9995b7b453SJohn Marino # if defined _LIBICONV_VERSION \
100200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
101200fbe8dSJohn Marino || defined __sun)
10295b7b453SJohn Marino {
10395b7b453SJohn Marino char *outptr = tmpbuf;
10495b7b453SJohn Marino size_t outsize = tmpbufsize;
10595b7b453SJohn Marino size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
10695b7b453SJohn Marino
10795b7b453SJohn Marino if (res == (size_t)(-1))
10895b7b453SJohn Marino return -1;
10995b7b453SJohn Marino count += outptr - tmpbuf;
11095b7b453SJohn Marino }
11195b7b453SJohn Marino # endif
11295b7b453SJohn Marino length = count;
11395b7b453SJohn Marino # undef tmpbuf
11495b7b453SJohn Marino }
11595b7b453SJohn Marino
11695b7b453SJohn Marino if (length == 0)
11795b7b453SJohn Marino {
11895b7b453SJohn Marino *lengthp = 0;
11995b7b453SJohn Marino return 0;
12095b7b453SJohn Marino }
12195b7b453SJohn Marino if (*resultp != NULL && *lengthp >= length)
12295b7b453SJohn Marino result = *resultp;
12395b7b453SJohn Marino else
12495b7b453SJohn Marino {
12595b7b453SJohn Marino result = (char *) malloc (length);
12695b7b453SJohn Marino if (result == NULL)
12795b7b453SJohn Marino {
12895b7b453SJohn Marino errno = ENOMEM;
12995b7b453SJohn Marino return -1;
13095b7b453SJohn Marino }
13195b7b453SJohn Marino }
13295b7b453SJohn Marino
13395b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */
13495b7b453SJohn Marino # if defined _LIBICONV_VERSION \
135200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
136200fbe8dSJohn Marino || defined __sun)
13795b7b453SJohn Marino /* Return to the initial state. */
13895b7b453SJohn Marino iconv (cd, NULL, NULL, NULL, NULL);
13995b7b453SJohn Marino # endif
14095b7b453SJohn Marino
14195b7b453SJohn Marino /* Do the conversion for real. */
14295b7b453SJohn Marino {
14395b7b453SJohn Marino const char *inptr = src;
14495b7b453SJohn Marino size_t insize = srclen;
14595b7b453SJohn Marino char *outptr = result;
14695b7b453SJohn Marino size_t outsize = length;
14795b7b453SJohn Marino
14895b7b453SJohn Marino while (insize > 0)
14995b7b453SJohn Marino {
15095b7b453SJohn Marino size_t res = iconv (cd,
15195b7b453SJohn Marino (ICONV_CONST char **) &inptr, &insize,
15295b7b453SJohn Marino &outptr, &outsize);
15395b7b453SJohn Marino
15495b7b453SJohn Marino if (res == (size_t)(-1))
15595b7b453SJohn Marino {
15695b7b453SJohn Marino if (errno == EINVAL)
15795b7b453SJohn Marino break;
15895b7b453SJohn Marino else
15995b7b453SJohn Marino goto fail;
16095b7b453SJohn Marino }
161200fbe8dSJohn Marino # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__)
16295b7b453SJohn Marino /* Irix iconv() inserts a NUL byte if it cannot convert.
16395b7b453SJohn Marino NetBSD iconv() inserts a question mark if it cannot convert.
16495b7b453SJohn Marino Only GNU libiconv and GNU libc are known to prefer to fail rather
16595b7b453SJohn Marino than doing a lossy conversion. */
16695b7b453SJohn Marino else if (res > 0)
16795b7b453SJohn Marino {
16895b7b453SJohn Marino errno = EILSEQ;
16995b7b453SJohn Marino goto fail;
17095b7b453SJohn Marino }
17195b7b453SJohn Marino # endif
17295b7b453SJohn Marino }
17395b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */
17495b7b453SJohn Marino # if defined _LIBICONV_VERSION \
175200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
176200fbe8dSJohn Marino || defined __sun)
17795b7b453SJohn Marino {
17895b7b453SJohn Marino size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
17995b7b453SJohn Marino
18095b7b453SJohn Marino if (res == (size_t)(-1))
18195b7b453SJohn Marino goto fail;
18295b7b453SJohn Marino }
18395b7b453SJohn Marino # endif
18495b7b453SJohn Marino if (outsize != 0)
18595b7b453SJohn Marino abort ();
18695b7b453SJohn Marino }
18795b7b453SJohn Marino
18895b7b453SJohn Marino *resultp = result;
18995b7b453SJohn Marino *lengthp = length;
19095b7b453SJohn Marino
19195b7b453SJohn Marino return 0;
19295b7b453SJohn Marino
19395b7b453SJohn Marino fail:
19495b7b453SJohn Marino {
19595b7b453SJohn Marino if (result != *resultp)
19695b7b453SJohn Marino {
19795b7b453SJohn Marino int saved_errno = errno;
19895b7b453SJohn Marino free (result);
19995b7b453SJohn Marino errno = saved_errno;
20095b7b453SJohn Marino }
20195b7b453SJohn Marino return -1;
20295b7b453SJohn Marino }
20395b7b453SJohn Marino # undef tmpbufsize
20495b7b453SJohn Marino }
20595b7b453SJohn Marino
20695b7b453SJohn Marino char *
str_cd_iconv(const char * src,iconv_t cd)20795b7b453SJohn Marino str_cd_iconv (const char *src, iconv_t cd)
20895b7b453SJohn Marino {
20995b7b453SJohn Marino /* For most encodings, a trailing NUL byte in the input will be converted
21095b7b453SJohn Marino to a trailing NUL byte in the output. But not for UTF-7. So that this
21195b7b453SJohn Marino function is usable for UTF-7, we have to exclude the NUL byte from the
21295b7b453SJohn Marino conversion and add it by hand afterwards. */
213200fbe8dSJohn Marino # if !defined _LIBICONV_VERSION && !(defined __GLIBC__ && !defined __UCLIBC__)
21495b7b453SJohn Marino /* Irix iconv() inserts a NUL byte if it cannot convert.
21595b7b453SJohn Marino NetBSD iconv() inserts a question mark if it cannot convert.
21695b7b453SJohn Marino Only GNU libiconv and GNU libc are known to prefer to fail rather
21795b7b453SJohn Marino than doing a lossy conversion. For other iconv() implementations,
21895b7b453SJohn Marino we have to look at the number of irreversible conversions returned;
21995b7b453SJohn Marino but this information is lost when iconv() returns for an E2BIG reason.
22095b7b453SJohn Marino Therefore we cannot use the second, faster algorithm. */
22195b7b453SJohn Marino
22295b7b453SJohn Marino char *result = NULL;
22395b7b453SJohn Marino size_t length = 0;
22495b7b453SJohn Marino int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length);
22595b7b453SJohn Marino char *final_result;
22695b7b453SJohn Marino
22795b7b453SJohn Marino if (retval < 0)
22895b7b453SJohn Marino {
22995b7b453SJohn Marino if (result != NULL)
23095b7b453SJohn Marino abort ();
23195b7b453SJohn Marino return NULL;
23295b7b453SJohn Marino }
23395b7b453SJohn Marino
23495b7b453SJohn Marino /* Add the terminating NUL byte. */
23595b7b453SJohn Marino final_result =
23695b7b453SJohn Marino (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
23795b7b453SJohn Marino if (final_result == NULL)
23895b7b453SJohn Marino {
23995b7b453SJohn Marino free (result);
24095b7b453SJohn Marino errno = ENOMEM;
24195b7b453SJohn Marino return NULL;
24295b7b453SJohn Marino }
24395b7b453SJohn Marino final_result[length] = '\0';
24495b7b453SJohn Marino
24595b7b453SJohn Marino return final_result;
24695b7b453SJohn Marino
24795b7b453SJohn Marino # else
24895b7b453SJohn Marino /* This algorithm is likely faster than the one above. But it may produce
24995b7b453SJohn Marino iconv() returns for an E2BIG reason, when the output size guess is too
25095b7b453SJohn Marino small. Therefore it can only be used when we don't need the number of
25195b7b453SJohn Marino irreversible conversions performed. */
25295b7b453SJohn Marino char *result;
25395b7b453SJohn Marino size_t result_size;
25495b7b453SJohn Marino size_t length;
25595b7b453SJohn Marino const char *inptr = src;
25695b7b453SJohn Marino size_t inbytes_remaining = strlen (src);
25795b7b453SJohn Marino
25895b7b453SJohn Marino /* Make a guess for the worst-case output size, in order to avoid a
25995b7b453SJohn Marino realloc. It's OK if the guess is wrong as long as it is not zero and
26095b7b453SJohn Marino doesn't lead to an integer overflow. */
26195b7b453SJohn Marino result_size = inbytes_remaining;
26295b7b453SJohn Marino {
26395b7b453SJohn Marino size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
26495b7b453SJohn Marino if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
26595b7b453SJohn Marino result_size *= MB_LEN_MAX;
26695b7b453SJohn Marino }
26795b7b453SJohn Marino result_size += 1; /* for the terminating NUL */
26895b7b453SJohn Marino
26995b7b453SJohn Marino result = (char *) malloc (result_size);
27095b7b453SJohn Marino if (result == NULL)
27195b7b453SJohn Marino {
27295b7b453SJohn Marino errno = ENOMEM;
27395b7b453SJohn Marino return NULL;
27495b7b453SJohn Marino }
27595b7b453SJohn Marino
27695b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */
27795b7b453SJohn Marino # if defined _LIBICONV_VERSION \
278200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
279200fbe8dSJohn Marino || defined __sun)
28095b7b453SJohn Marino /* Set to the initial state. */
28195b7b453SJohn Marino iconv (cd, NULL, NULL, NULL, NULL);
28295b7b453SJohn Marino # endif
28395b7b453SJohn Marino
28495b7b453SJohn Marino /* Do the conversion. */
28595b7b453SJohn Marino {
28695b7b453SJohn Marino char *outptr = result;
28795b7b453SJohn Marino size_t outbytes_remaining = result_size - 1;
28895b7b453SJohn Marino
28995b7b453SJohn Marino for (;;)
29095b7b453SJohn Marino {
29195b7b453SJohn Marino /* Here inptr + inbytes_remaining = src + strlen (src),
29295b7b453SJohn Marino outptr + outbytes_remaining = result + result_size - 1. */
29395b7b453SJohn Marino size_t res = iconv (cd,
29495b7b453SJohn Marino (ICONV_CONST char **) &inptr, &inbytes_remaining,
29595b7b453SJohn Marino &outptr, &outbytes_remaining);
29695b7b453SJohn Marino
29795b7b453SJohn Marino if (res == (size_t)(-1))
29895b7b453SJohn Marino {
29995b7b453SJohn Marino if (errno == EINVAL)
30095b7b453SJohn Marino break;
30195b7b453SJohn Marino else if (errno == E2BIG)
30295b7b453SJohn Marino {
30395b7b453SJohn Marino size_t used = outptr - result;
30495b7b453SJohn Marino size_t newsize = result_size * 2;
30595b7b453SJohn Marino char *newresult;
30695b7b453SJohn Marino
30795b7b453SJohn Marino if (!(newsize > result_size))
30895b7b453SJohn Marino {
30995b7b453SJohn Marino errno = ENOMEM;
31095b7b453SJohn Marino goto failed;
31195b7b453SJohn Marino }
31295b7b453SJohn Marino newresult = (char *) realloc (result, newsize);
31395b7b453SJohn Marino if (newresult == NULL)
31495b7b453SJohn Marino {
31595b7b453SJohn Marino errno = ENOMEM;
31695b7b453SJohn Marino goto failed;
31795b7b453SJohn Marino }
31895b7b453SJohn Marino result = newresult;
31995b7b453SJohn Marino result_size = newsize;
32095b7b453SJohn Marino outptr = result + used;
32195b7b453SJohn Marino outbytes_remaining = result_size - 1 - used;
32295b7b453SJohn Marino }
32395b7b453SJohn Marino else
32495b7b453SJohn Marino goto failed;
32595b7b453SJohn Marino }
32695b7b453SJohn Marino else
32795b7b453SJohn Marino break;
32895b7b453SJohn Marino }
32995b7b453SJohn Marino /* Avoid glibc-2.1 bug and Solaris 2.7 bug. */
33095b7b453SJohn Marino # if defined _LIBICONV_VERSION \
331200fbe8dSJohn Marino || !(((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
332200fbe8dSJohn Marino || defined __sun)
33395b7b453SJohn Marino for (;;)
33495b7b453SJohn Marino {
33595b7b453SJohn Marino /* Here outptr + outbytes_remaining = result + result_size - 1. */
33695b7b453SJohn Marino size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
33795b7b453SJohn Marino
33895b7b453SJohn Marino if (res == (size_t)(-1))
33995b7b453SJohn Marino {
34095b7b453SJohn Marino if (errno == E2BIG)
34195b7b453SJohn Marino {
34295b7b453SJohn Marino size_t used = outptr - result;
34395b7b453SJohn Marino size_t newsize = result_size * 2;
34495b7b453SJohn Marino char *newresult;
34595b7b453SJohn Marino
34695b7b453SJohn Marino if (!(newsize > result_size))
34795b7b453SJohn Marino {
34895b7b453SJohn Marino errno = ENOMEM;
34995b7b453SJohn Marino goto failed;
35095b7b453SJohn Marino }
35195b7b453SJohn Marino newresult = (char *) realloc (result, newsize);
35295b7b453SJohn Marino if (newresult == NULL)
35395b7b453SJohn Marino {
35495b7b453SJohn Marino errno = ENOMEM;
35595b7b453SJohn Marino goto failed;
35695b7b453SJohn Marino }
35795b7b453SJohn Marino result = newresult;
35895b7b453SJohn Marino result_size = newsize;
35995b7b453SJohn Marino outptr = result + used;
36095b7b453SJohn Marino outbytes_remaining = result_size - 1 - used;
36195b7b453SJohn Marino }
36295b7b453SJohn Marino else
36395b7b453SJohn Marino goto failed;
36495b7b453SJohn Marino }
36595b7b453SJohn Marino else
36695b7b453SJohn Marino break;
36795b7b453SJohn Marino }
36895b7b453SJohn Marino # endif
36995b7b453SJohn Marino
37095b7b453SJohn Marino /* Add the terminating NUL byte. */
37195b7b453SJohn Marino *outptr++ = '\0';
37295b7b453SJohn Marino
37395b7b453SJohn Marino length = outptr - result;
37495b7b453SJohn Marino }
37595b7b453SJohn Marino
37695b7b453SJohn Marino /* Give away unused memory. */
37795b7b453SJohn Marino if (length < result_size)
37895b7b453SJohn Marino {
37995b7b453SJohn Marino char *smaller_result = (char *) realloc (result, length);
38095b7b453SJohn Marino
38195b7b453SJohn Marino if (smaller_result != NULL)
38295b7b453SJohn Marino result = smaller_result;
38395b7b453SJohn Marino }
38495b7b453SJohn Marino
38595b7b453SJohn Marino return result;
38695b7b453SJohn Marino
38795b7b453SJohn Marino failed:
38895b7b453SJohn Marino {
38995b7b453SJohn Marino int saved_errno = errno;
39095b7b453SJohn Marino free (result);
39195b7b453SJohn Marino errno = saved_errno;
39295b7b453SJohn Marino return NULL;
39395b7b453SJohn Marino }
39495b7b453SJohn Marino
39595b7b453SJohn Marino # endif
39695b7b453SJohn Marino }
39795b7b453SJohn Marino
39895b7b453SJohn Marino #endif
39995b7b453SJohn Marino
40095b7b453SJohn Marino char *
str_iconv(const char * src,const char * from_codeset,const char * to_codeset)40195b7b453SJohn Marino str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
40295b7b453SJohn Marino {
40395b7b453SJohn Marino if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
40495b7b453SJohn Marino {
40595b7b453SJohn Marino char *result = strdup (src);
40695b7b453SJohn Marino
40795b7b453SJohn Marino if (result == NULL)
40895b7b453SJohn Marino errno = ENOMEM;
40995b7b453SJohn Marino return result;
41095b7b453SJohn Marino }
41195b7b453SJohn Marino else
41295b7b453SJohn Marino {
41395b7b453SJohn Marino #if HAVE_ICONV
41495b7b453SJohn Marino iconv_t cd;
41595b7b453SJohn Marino char *result;
41695b7b453SJohn Marino
41795b7b453SJohn Marino /* Avoid glibc-2.1 bug with EUC-KR. */
418200fbe8dSJohn Marino # if ((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) && !defined __UCLIBC__) \
419200fbe8dSJohn Marino && !defined _LIBICONV_VERSION
42095b7b453SJohn Marino if (c_strcasecmp (from_codeset, "EUC-KR") == 0
42195b7b453SJohn Marino || c_strcasecmp (to_codeset, "EUC-KR") == 0)
42295b7b453SJohn Marino {
42395b7b453SJohn Marino errno = EINVAL;
42495b7b453SJohn Marino return NULL;
42595b7b453SJohn Marino }
42695b7b453SJohn Marino # endif
42795b7b453SJohn Marino cd = iconv_open (to_codeset, from_codeset);
42895b7b453SJohn Marino if (cd == (iconv_t) -1)
42995b7b453SJohn Marino return NULL;
43095b7b453SJohn Marino
43195b7b453SJohn Marino result = str_cd_iconv (src, cd);
43295b7b453SJohn Marino
43395b7b453SJohn Marino if (result == NULL)
43495b7b453SJohn Marino {
43595b7b453SJohn Marino /* Close cd, but preserve the errno from str_cd_iconv. */
43695b7b453SJohn Marino int saved_errno = errno;
43795b7b453SJohn Marino iconv_close (cd);
43895b7b453SJohn Marino errno = saved_errno;
43995b7b453SJohn Marino }
44095b7b453SJohn Marino else
44195b7b453SJohn Marino {
44295b7b453SJohn Marino if (iconv_close (cd) < 0)
44395b7b453SJohn Marino {
44495b7b453SJohn Marino /* Return NULL, but free the allocated memory, and while doing
44595b7b453SJohn Marino that, preserve the errno from iconv_close. */
44695b7b453SJohn Marino int saved_errno = errno;
44795b7b453SJohn Marino free (result);
44895b7b453SJohn Marino errno = saved_errno;
44995b7b453SJohn Marino return NULL;
45095b7b453SJohn Marino }
45195b7b453SJohn Marino }
45295b7b453SJohn Marino return result;
45395b7b453SJohn Marino #else
45495b7b453SJohn Marino /* This is a different error code than if iconv_open existed but didn't
45595b7b453SJohn Marino support from_codeset and to_codeset, so that the caller can emit
45695b7b453SJohn Marino an error message such as
45795b7b453SJohn Marino "iconv() is not supported. Installing GNU libiconv and
45895b7b453SJohn Marino then reinstalling this package would fix this." */
45995b7b453SJohn Marino errno = ENOSYS;
46095b7b453SJohn Marino return NULL;
46195b7b453SJohn Marino #endif
46295b7b453SJohn Marino }
46395b7b453SJohn Marino }
464