1 /* Character set conversion with error handling.
2 Copyright (C) 2001-2008 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 3 of the License, or
8 (at your option) 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, see <http://www.gnu.org/licenses/>. */
21 #include "striconveh.h"
33 #include "c-strcase.h"
34 #include "c-strcaseeq.h"
37 # define SIZE_MAX ((size_t) -1)
43 /* The caller must provide CD, CD1, CD2, not just CD, because when a conversion
44 error occurs, we may have to determine the Unicode representation of the
45 inconvertible character. */
47 /* iconv_carefully is like iconv, except that it stops as soon as it encounters
48 a conversion error, and it returns in *INCREMENTED a boolean telling whether
49 it has incremented the input pointers past the error location. */
50 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
51 /* Irix iconv() inserts a NUL byte if it cannot convert.
52 NetBSD iconv() inserts a question mark if it cannot convert.
53 Only GNU libiconv and GNU libc are known to prefer to fail rather
54 than doing a lossy conversion. */
56 iconv_carefully (iconv_t cd,
57 const char **inbuf, size_t *inbytesleft,
58 char **outbuf, size_t *outbytesleft,
61 const char *inptr = *inbuf;
62 const char *inptr_end = inptr + *inbytesleft;
63 char *outptr = *outbuf;
64 size_t outsize = *outbytesleft;
65 const char *inptr_before;
75 for (insize = 1; inptr + insize <= inptr_end; insize++)
78 (ICONV_CONST char **) &inptr, &insize,
80 if (!(res == (size_t)(-1) && errno == EINVAL))
82 /* We expect that no input bytes have been consumed so far. */
83 if (inptr != inptr_before)
90 *outbytesleft = outsize;
93 while (res == 0 && inptr < inptr_end);
96 *inbytesleft = inptr_end - inptr;
97 if (res != (size_t)(-1) && res > 0)
99 /* iconv() has already incremented INPTR. We cannot go back to a
100 previous INPTR, otherwise the state inside CD would become invalid,
101 if FROM_CODESET is a stateful encoding. So, tell the caller that
102 *INBUF has already been incremented. */
103 *incremented = (inptr > inptr_before);
109 *incremented = false;
114 # define iconv_carefully(cd, inbuf, inbytesleft, outbuf, outbytesleft, incremented) \
115 (*(incremented) = false, \
116 iconv (cd, (ICONV_CONST char **) (inbuf), inbytesleft, outbuf, outbytesleft))
119 /* iconv_carefully_1 is like iconv_carefully, except that it stops after
120 converting one character. */
122 iconv_carefully_1 (iconv_t cd,
123 const char **inbuf, size_t *inbytesleft,
124 char **outbuf, size_t *outbytesleft,
127 const char *inptr = *inbuf;
128 const char *inptr_end = inptr + *inbytesleft;
129 char *outptr = *outbuf;
130 size_t outsize = *outbytesleft;
131 const char *inptr_before = inptr;
132 size_t res = (size_t)(-1);
135 for (insize = 1; inptr + insize <= inptr_end; insize++)
138 (ICONV_CONST char **) &inptr, &insize,
140 if (!(res == (size_t)(-1) && errno == EINVAL))
142 /* We expect that no input bytes have been consumed so far. */
143 if (inptr != inptr_before)
148 *inbytesleft = inptr_end - inptr;
149 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
150 /* Irix iconv() inserts a NUL byte if it cannot convert.
151 NetBSD iconv() inserts a question mark if it cannot convert.
152 Only GNU libiconv and GNU libc are known to prefer to fail rather
153 than doing a lossy conversion. */
154 if (res != (size_t)(-1) && res > 0)
156 /* iconv() has already incremented INPTR. We cannot go back to a
157 previous INPTR, otherwise the state inside CD would become invalid,
158 if FROM_CODESET is a stateful encoding. So, tell the caller that
159 *INBUF has already been incremented. */
160 *incremented = (inptr > inptr_before);
166 if (res != (size_t)(-1))
169 *outbytesleft = outsize;
171 *incremented = false;
175 /* utf8conv_carefully is like iconv, except that
176 - it converts from UTF-8 to UTF-8,
177 - it stops as soon as it encounters a conversion error, and it returns
178 in *INCREMENTED a boolean telling whether it has incremented the input
179 pointers past the error location,
180 - if one_character_only is true, it stops after converting one
183 utf8conv_carefully (bool one_character_only,
184 const char **inbuf, size_t *inbytesleft,
185 char **outbuf, size_t *outbytesleft,
188 const char *inptr = *inbuf;
189 size_t insize = *inbytesleft;
190 char *outptr = *outbuf;
191 size_t outsize = *outbytesleft;
201 n = u8_mbtoucr (&uc, (const uint8_t *) inptr, insize);
204 errno = (n == -2 ? EINVAL : EILSEQ);
205 n = u8_mbtouc (&uc, (const uint8_t *) inptr, insize);
216 *incremented = false;
219 m = u8_uctomb ((uint8_t *) outptr, uc, outsize);
224 *incremented = false;
239 while (!one_character_only && insize > 0);
242 *inbytesleft = insize;
244 *outbytesleft = outsize;
249 mem_cd_iconveh_internal (const char *src, size_t srclen,
250 iconv_t cd, iconv_t cd1, iconv_t cd2,
251 enum iconv_ilseq_handler handler,
254 char **resultp, size_t *lengthp)
256 /* When a conversion error occurs, we cannot start using CD1 and CD2 at
257 this point: FROM_CODESET may be a stateful encoding like ISO-2022-KR.
258 Instead, we have to start afresh from the beginning of SRC. */
259 /* Use a temporary buffer, so that for small strings, a single malloc()
260 call will be sufficient. */
261 # define tmpbufsize 4096
262 /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
263 libiconv's UCS-4-INTERNAL encoding. */
264 union { unsigned int align; char buf[tmpbufsize]; } tmp;
265 # define tmpbuf tmp.buf
267 char *initial_result;
271 size_t last_length = (size_t)(-1); /* only needed if offsets != NULL */
273 if (*resultp != NULL && *lengthp >= sizeof (tmpbuf))
275 initial_result = *resultp;
276 allocated = *lengthp;
280 initial_result = tmpbuf;
281 allocated = sizeof (tmpbuf);
283 result = initial_result;
285 /* Test whether a direct conversion is possible at all. */
286 if (cd == (iconv_t)(-1))
293 for (i = 0; i < srclen; i++)
294 offsets[i] = (size_t)(-1);
296 last_length = (size_t)(-1);
300 /* First, try a direct conversion, and see whether a conversion error
303 const char *inptr = src;
304 size_t insize = srclen;
306 /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */
307 # if defined _LIBICONV_VERSION \
308 || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
309 /* Set to the initial state. */
310 iconv (cd, NULL, NULL, NULL, NULL);
315 char *outptr = result + length;
316 size_t outsize = allocated - extra_alloc - length;
323 if (length != last_length) /* ensure that offset[] be increasing */
325 offsets[inptr - src] = length;
326 last_length = length;
328 res = iconv_carefully_1 (cd,
334 /* Use iconv_carefully instead of iconv here, because:
335 - If TO_CODESET is UTF-8, we can do the error handling in this
336 loop, no need for a second loop,
337 - With iconv() implementations other than GNU libiconv and GNU
338 libc, if we use iconv() in a big swoop, checking for an E2BIG
339 return, we lose the number of irreversible conversions. */
340 res = iconv_carefully (cd,
345 length = outptr - result;
346 grow = (length + extra_alloc > allocated / 2);
347 if (res == (size_t)(-1))
351 else if (errno == EINVAL)
353 else if (errno == EILSEQ && handler != iconveh_error)
355 if (cd2 == (iconv_t)(-1))
357 /* TO_CODESET is UTF-8. */
358 /* Error handling can produce up to 1 byte of output. */
359 if (length + 1 + extra_alloc > allocated)
363 allocated = 2 * allocated;
364 if (length + 1 + extra_alloc > allocated)
366 if (result == initial_result)
367 memory = (char *) malloc (allocated);
369 memory = (char *) realloc (result, allocated);
372 if (result != initial_result)
377 if (result == initial_result)
378 memcpy (memory, initial_result, length);
382 /* The input is invalid in FROM_CODESET. Eat up one byte
383 and emit a question mark. */
391 result[length] = '?';
399 if (result != initial_result)
401 int saved_errno = errno;
414 allocated = 2 * allocated;
415 if (result == initial_result)
416 memory = (char *) malloc (allocated);
418 memory = (char *) realloc (result, allocated);
421 if (result != initial_result)
426 if (result == initial_result)
427 memcpy (memory, initial_result, length);
433 /* Now get the conversion state back to the initial state.
434 But avoid glibc-2.1 bug and Solaris 2.7 bug. */
435 #if defined _LIBICONV_VERSION \
436 || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
439 char *outptr = result + length;
440 size_t outsize = allocated - extra_alloc - length;
443 res = iconv (cd, NULL, NULL, &outptr, &outsize);
444 length = outptr - result;
445 if (res == (size_t)(-1))
451 allocated = 2 * allocated;
452 if (result == initial_result)
453 memory = (char *) malloc (allocated);
455 memory = (char *) realloc (result, allocated);
458 if (result != initial_result)
463 if (result == initial_result)
464 memcpy (memory, initial_result, length);
469 if (result != initial_result)
471 int saved_errno = errno;
483 /* The direct conversion succeeded. */
487 /* The direct conversion failed.
488 Use a conversion through UTF-8. */
493 for (i = 0; i < srclen; i++)
494 offsets[i] = (size_t)(-1);
496 last_length = (size_t)(-1);
500 const bool slowly = (offsets != NULL || handler == iconveh_error);
501 # define utf8bufsize 4096 /* may also be smaller or larger than tmpbufsize */
502 char utf8buf[utf8bufsize + 1];
504 const char *in1ptr = src;
505 size_t in1size = srclen;
506 bool do_final_flush1 = true;
507 bool do_final_flush2 = true;
509 /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug. */
510 # if defined _LIBICONV_VERSION \
511 || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
512 /* Set to the initial state. */
513 if (cd1 != (iconv_t)(-1))
514 iconv (cd1, NULL, NULL, NULL, NULL);
515 if (cd2 != (iconv_t)(-1))
516 iconv (cd2, NULL, NULL, NULL, NULL);
519 while (in1size > 0 || do_final_flush1 || utf8len > 0 || do_final_flush2)
521 char *out1ptr = utf8buf + utf8len;
522 size_t out1size = utf8bufsize - utf8len;
527 /* Conversion step 1: from FROM_CODESET to UTF-8. */
531 && length != last_length) /* ensure that offset[] be increasing */
533 offsets[in1ptr - src] = length;
534 last_length = length;
536 if (cd1 != (iconv_t)(-1))
539 res1 = iconv_carefully_1 (cd1,
544 res1 = iconv_carefully (cd1,
551 /* FROM_CODESET is UTF-8. */
552 res1 = utf8conv_carefully (slowly,
558 else if (do_final_flush1)
560 /* Now get the conversion state of CD1 back to the initial state.
561 But avoid glibc-2.1 bug and Solaris 2.7 bug. */
562 # if defined _LIBICONV_VERSION \
563 || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
564 if (cd1 != (iconv_t)(-1))
565 res1 = iconv (cd1, NULL, NULL, &out1ptr, &out1size);
569 do_final_flush1 = false;
577 if (res1 == (size_t)(-1)
578 && !(errno == E2BIG || errno == EINVAL || errno == EILSEQ))
580 if (result != initial_result)
582 int saved_errno = errno;
588 if (res1 == (size_t)(-1)
589 && errno == EILSEQ && handler != iconveh_error)
591 /* The input is invalid in FROM_CODESET. Eat up one byte and
592 emit a question mark. Room for the question mark was allocated
593 at the end of utf8buf. */
601 utf8buf[utf8len++] = '?';
604 utf8len = out1ptr - utf8buf;
608 || utf8len > utf8bufsize / 2
609 || (res1 == (size_t)(-1) && errno1 == E2BIG))
611 /* Conversion step 2: from UTF-8 to TO_CODESET. */
612 const char *in2ptr = utf8buf;
613 size_t in2size = utf8len;
616 || (in1size == 0 && !do_final_flush1 && do_final_flush2))
618 char *out2ptr = result + length;
619 size_t out2size = allocated - extra_alloc - length;
626 if (cd2 != (iconv_t)(-1))
627 res2 = iconv_carefully (cd2,
632 /* TO_CODESET is UTF-8. */
633 res2 = utf8conv_carefully (false,
638 else /* in1size == 0 && !do_final_flush1
639 && in2size == 0 && do_final_flush2 */
641 /* Now get the conversion state of CD1 back to the initial
642 state. But avoid glibc-2.1 bug and Solaris 2.7 bug. */
643 # if defined _LIBICONV_VERSION \
644 || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
645 if (cd2 != (iconv_t)(-1))
646 res2 = iconv (cd2, NULL, NULL, &out2ptr, &out2size);
650 do_final_flush2 = false;
654 length = out2ptr - result;
655 grow = (length + extra_alloc > allocated / 2);
656 if (res2 == (size_t)(-1))
660 else if (errno == EINVAL)
662 else if (errno == EILSEQ && handler != iconveh_error)
664 /* Error handling can produce up to 10 bytes of ASCII
665 output. But TO_CODESET may be UCS-2, UTF-16 or
666 UCS-4, so use CD2 here as well. */
676 if (u8_prev (&uc, (const uint8_t *) in2ptr,
677 (const uint8_t *) utf8buf)
686 n = u8_mbtouc_unsafe (&uc, (const uint8_t *) in2ptr,
692 if (handler == iconveh_escape_sequence)
694 static char hex[16] = "0123456789ABCDEF";
696 scratchbuf[scratchlen++] = '\\';
698 scratchbuf[scratchlen++] = 'u';
701 scratchbuf[scratchlen++] = 'U';
702 scratchbuf[scratchlen++] = hex[(uc>>28) & 15];
703 scratchbuf[scratchlen++] = hex[(uc>>24) & 15];
704 scratchbuf[scratchlen++] = hex[(uc>>20) & 15];
705 scratchbuf[scratchlen++] = hex[(uc>>16) & 15];
707 scratchbuf[scratchlen++] = hex[(uc>>12) & 15];
708 scratchbuf[scratchlen++] = hex[(uc>>8) & 15];
709 scratchbuf[scratchlen++] = hex[(uc>>4) & 15];
710 scratchbuf[scratchlen++] = hex[uc & 15];
720 if (cd2 != (iconv_t)(-1))
722 (ICONV_CONST char **) &inptr, &insize,
723 &out2ptr, &out2size);
726 /* TO_CODESET is UTF-8. */
727 if (out2size >= insize)
729 memcpy (out2ptr, inptr, insize);
742 length = out2ptr - result;
743 if (res == (size_t)(-1) && errno == E2BIG)
747 allocated = 2 * allocated;
748 if (length + 1 + extra_alloc > allocated)
750 if (result == initial_result)
751 memory = (char *) malloc (allocated);
753 memory = (char *) realloc (result, allocated);
756 if (result != initial_result)
761 if (result == initial_result)
762 memcpy (memory, initial_result, length);
766 out2ptr = result + length;
767 out2size = allocated - extra_alloc - length;
768 if (cd2 != (iconv_t)(-1))
770 (ICONV_CONST char **) &inptr,
772 &out2ptr, &out2size);
775 /* TO_CODESET is UTF-8. */
776 if (!(out2size >= insize))
778 memcpy (out2ptr, inptr, insize);
785 length = out2ptr - result;
787 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
788 /* Irix iconv() inserts a NUL byte if it cannot convert.
789 NetBSD iconv() inserts a question mark if it cannot
791 Only GNU libiconv and GNU libc are known to prefer
792 to fail rather than doing a lossy conversion. */
793 if (res != (size_t)(-1) && res > 0)
799 if (res == (size_t)(-1))
801 /* Failure converting the ASCII replacement. */
802 if (result != initial_result)
804 int saved_errno = errno;
813 if (result != initial_result)
815 int saved_errno = errno;
823 || (in1size == 0 && !do_final_flush1 && do_final_flush2)))
829 allocated = 2 * allocated;
830 if (result == initial_result)
831 memory = (char *) malloc (allocated);
833 memory = (char *) realloc (result, allocated);
836 if (result != initial_result)
841 if (result == initial_result)
842 memcpy (memory, initial_result, length);
847 /* Move the remaining bytes to the beginning of utf8buf. */
849 memmove (utf8buf, in2ptr, in2size);
853 if (res1 == (size_t)(-1))
855 if (errno1 == EINVAL)
857 else if (errno1 == EILSEQ)
859 if (result != initial_result)
870 /* Now the final memory allocation. */
871 if (result == tmpbuf)
873 size_t memsize = length + extra_alloc;
876 memory = (char *) malloc (memsize > 0 ? memsize : 1);
879 memcpy (memory, tmpbuf, length);
888 else if (result != *resultp && length + extra_alloc < allocated)
890 /* Shrink the allocated memory if possible. */
891 size_t memsize = length + extra_alloc;
894 memory = (char *) realloc (result, memsize > 0 ? memsize : 1);
906 mem_cd_iconveh (const char *src, size_t srclen,
907 iconv_t cd, iconv_t cd1, iconv_t cd2,
908 enum iconv_ilseq_handler handler,
910 char **resultp, size_t *lengthp)
912 return mem_cd_iconveh_internal (src, srclen, cd, cd1, cd2, handler, 0,
913 offsets, resultp, lengthp);
917 str_cd_iconveh (const char *src,
918 iconv_t cd, iconv_t cd1, iconv_t cd2,
919 enum iconv_ilseq_handler handler)
921 /* For most encodings, a trailing NUL byte in the input will be converted
922 to a trailing NUL byte in the output. But not for UTF-7. So that this
923 function is usable for UTF-7, we have to exclude the NUL byte from the
924 conversion and add it by hand afterwards. */
927 int retval = mem_cd_iconveh_internal (src, strlen (src),
928 cd, cd1, cd2, handler, 1, NULL,
935 int saved_errno = errno;
942 /* Add the terminating NUL byte. */
943 result[length] = '\0';
951 mem_iconveh (const char *src, size_t srclen,
952 const char *from_codeset, const char *to_codeset,
953 enum iconv_ilseq_handler handler,
955 char **resultp, size_t *lengthp)
959 /* Nothing to convert. */
963 else if (offsets == NULL && c_strcasecmp (from_codeset, to_codeset) == 0)
967 if (*resultp != NULL && *lengthp >= srclen)
971 result = (char *) malloc (srclen);
978 memcpy (result, src, srclen);
993 /* Avoid glibc-2.1 bug with EUC-KR. */
994 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
995 if (c_strcasecmp (from_codeset, "EUC-KR") == 0
996 || c_strcasecmp (to_codeset, "EUC-KR") == 0)
1003 cd = iconv_open (to_codeset, from_codeset);
1005 if (STRCASEEQ (from_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0))
1006 cd1 = (iconv_t)(-1);
1009 cd1 = iconv_open ("UTF-8", from_codeset);
1010 if (cd1 == (iconv_t)(-1))
1012 int saved_errno = errno;
1013 if (cd != (iconv_t)(-1))
1015 errno = saved_errno;
1020 if (STRCASEEQ (to_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0)
1021 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
1022 || c_strcasecmp (to_codeset, "UTF-8//TRANSLIT") == 0
1025 cd2 = (iconv_t)(-1);
1028 cd2 = iconv_open (to_codeset, "UTF-8");
1029 if (cd2 == (iconv_t)(-1))
1031 int saved_errno = errno;
1032 if (cd1 != (iconv_t)(-1))
1034 if (cd != (iconv_t)(-1))
1036 errno = saved_errno;
1043 retval = mem_cd_iconveh (src, srclen, cd, cd1, cd2, handler, offsets,
1048 /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv. */
1049 int saved_errno = errno;
1050 if (cd2 != (iconv_t)(-1))
1052 if (cd1 != (iconv_t)(-1))
1054 if (cd != (iconv_t)(-1))
1056 errno = saved_errno;
1060 if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
1062 /* Return -1, but free the allocated memory, and while doing
1063 that, preserve the errno from iconv_close. */
1064 int saved_errno = errno;
1065 if (cd1 != (iconv_t)(-1))
1067 if (cd != (iconv_t)(-1))
1069 if (result != *resultp && result != NULL)
1071 errno = saved_errno;
1074 if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
1076 /* Return -1, but free the allocated memory, and while doing
1077 that, preserve the errno from iconv_close. */
1078 int saved_errno = errno;
1079 if (cd != (iconv_t)(-1))
1081 if (result != *resultp && result != NULL)
1083 errno = saved_errno;
1086 if (cd != (iconv_t)(-1) && iconv_close (cd) < 0)
1088 /* Return -1, but free the allocated memory, and while doing
1089 that, preserve the errno from iconv_close. */
1090 int saved_errno = errno;
1091 if (result != *resultp && result != NULL)
1093 errno = saved_errno;
1101 /* This is a different error code than if iconv_open existed but didn't
1102 support from_codeset and to_codeset, so that the caller can emit
1103 an error message such as
1104 "iconv() is not supported. Installing GNU libiconv and
1105 then reinstalling this package would fix this." */
1113 str_iconveh (const char *src,
1114 const char *from_codeset, const char *to_codeset,
1115 enum iconv_ilseq_handler handler)
1117 if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
1119 char *result = strdup (src);
1133 /* Avoid glibc-2.1 bug with EUC-KR. */
1134 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
1135 if (c_strcasecmp (from_codeset, "EUC-KR") == 0
1136 || c_strcasecmp (to_codeset, "EUC-KR") == 0)
1143 cd = iconv_open (to_codeset, from_codeset);
1145 if (STRCASEEQ (from_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0))
1146 cd1 = (iconv_t)(-1);
1149 cd1 = iconv_open ("UTF-8", from_codeset);
1150 if (cd1 == (iconv_t)(-1))
1152 int saved_errno = errno;
1153 if (cd != (iconv_t)(-1))
1155 errno = saved_errno;
1160 if (STRCASEEQ (to_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0)
1161 # if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2) || __GLIBC__ > 2 || _LIBICONV_VERSION >= 0x0105
1162 || c_strcasecmp (to_codeset, "UTF-8//TRANSLIT") == 0
1165 cd2 = (iconv_t)(-1);
1168 cd2 = iconv_open (to_codeset, "UTF-8");
1169 if (cd2 == (iconv_t)(-1))
1171 int saved_errno = errno;
1172 if (cd1 != (iconv_t)(-1))
1174 if (cd != (iconv_t)(-1))
1176 errno = saved_errno;
1181 result = str_cd_iconveh (src, cd, cd1, cd2, handler);
1185 /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv. */
1186 int saved_errno = errno;
1187 if (cd2 != (iconv_t)(-1))
1189 if (cd1 != (iconv_t)(-1))
1191 if (cd != (iconv_t)(-1))
1193 errno = saved_errno;
1197 if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
1199 /* Return NULL, but free the allocated memory, and while doing
1200 that, preserve the errno from iconv_close. */
1201 int saved_errno = errno;
1202 if (cd1 != (iconv_t)(-1))
1204 if (cd != (iconv_t)(-1))
1207 errno = saved_errno;
1210 if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
1212 /* Return NULL, but free the allocated memory, and while doing
1213 that, preserve the errno from iconv_close. */
1214 int saved_errno = errno;
1215 if (cd != (iconv_t)(-1))
1218 errno = saved_errno;
1221 if (cd != (iconv_t)(-1) && iconv_close (cd) < 0)
1223 /* Return NULL, but free the allocated memory, and while doing
1224 that, preserve the errno from iconv_close. */
1225 int saved_errno = errno;
1227 errno = saved_errno;
1233 /* This is a different error code than if iconv_open existed but didn't
1234 support from_codeset and to_codeset, so that the caller can emit
1235 an error message such as
1236 "iconv() is not supported. Installing GNU libiconv and
1237 then reinstalling this package would fix this." */