New function mem_iconveh.
[pspp] / lib / striconveh.c
1 /* Character set conversion with error handling.
2    Copyright (C) 2001-2007 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 "striconveh.h"
23
24 #include <errno.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #if HAVE_ICONV
30 # include <iconv.h>
31 # include "utf8-ucs4-safe.h"
32 # include "ucs4-utf8.h"
33 # include "unistr.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 /* The caller must provide CD, CD1, CD2, not just CD, because when a conversion
47    error occurs, we may have to determine the Unicode representation of the
48    inconvertible character.  */
49
50 /* iconv_carefully is like iconv, except that it stops as soon as it encounters
51    a conversion error, and it returns in *INCREMENTED a boolean telling whether
52    it has incremented the input pointers past the error location.  */
53 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
54 /* Irix iconv() inserts a NUL byte if it cannot convert.
55    NetBSD iconv() inserts a question mark if it cannot convert.
56    Only GNU libiconv and GNU libc are known to prefer to fail rather
57    than doing a lossy conversion.  */
58 static size_t
59 iconv_carefully (iconv_t cd,
60                  const char **inbuf, size_t *inbytesleft,
61                  char **outbuf, size_t *outbytesleft,
62                  bool *incremented)
63 {
64   const char *inptr = *inbuf;
65   const char *inptr_end = inptr + *inbytesleft;
66   char *outptr = *outbuf;
67   size_t outsize = *outbytesleft;
68   const char *inptr_before;
69   size_t res;
70
71   do
72     {
73       size_t insize;
74
75       inptr_before = inptr;
76       res = (size_t)(-1);
77
78       for (insize = 1; inptr + insize <= inptr_end; insize++)
79         {
80           res = iconv (cd,
81                        (ICONV_CONST char **) &inptr, &insize,
82                        &outptr, &outsize);
83           if (!(res == (size_t)(-1) && errno == EINVAL))
84             break;
85           /* We expect that no input bytes have been consumed so far.  */
86           if (inptr != inptr_before)
87             abort ();
88         }
89
90       if (res == 0)
91         {
92           *outbuf = outptr;
93           *outbytesleft = outsize;
94         }
95     }
96   while (res == 0 && inptr < inptr_end);
97
98   *inbuf = inptr;
99   *inbytesleft = inptr_end - inptr;
100   if (res != (size_t)(-1) && res > 0)
101     {
102       /* iconv() has already incremented INPTR.  We cannot go back to a
103          previous INPTR, otherwise the state inside CD would become invalid,
104          if FROM_CODESET is a stateful encoding.  So, tell the caller that
105          *INBUF has already been incremented.  */
106       *incremented = (inptr > inptr_before);
107       errno = EILSEQ;
108       return (size_t)(-1);
109     }
110   else
111     {
112       *incremented = false;
113       return res;
114     }
115 }
116 # else
117 #  define iconv_carefully(cd, inbuf, inbytesleft, outbuf, outbytesleft, incremented) \
118      (*(incremented) = false, \
119       iconv (cd, (ICONV_CONST char **) (inbuf), inbytesleft, outbuf, outbytesleft))
120 # endif
121
122 static int
123 mem_cd_iconveh_internal (const char *src, size_t srclen,
124                          iconv_t cd, iconv_t cd1, iconv_t cd2,
125                          enum iconv_ilseq_handler handler,
126                          size_t extra_alloc,
127                          char **resultp, size_t *lengthp)
128 {
129   /* When a conversion error occurs, we cannot start using CD1 and CD2 at
130      this point: FROM_CODESET may be a stateful encoding like ISO-2022-KR.
131      Instead, we have to start afresh from the beginning of SRC.  */
132   /* Use a temporary buffer, so that for small strings, a single malloc()
133      call will be sufficient.  */
134 # define tmpbufsize 4096
135   /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
136      libiconv's UCS-4-INTERNAL encoding.  */
137   union { unsigned int align; char buf[tmpbufsize]; } tmp;
138 # define tmpbuf tmp.buf
139
140   char *initial_result;
141   char *result;
142   size_t allocated;
143   size_t length;
144
145   if (*lengthp >= sizeof (tmpbuf))
146     {
147       initial_result = *resultp;
148       allocated = *lengthp;
149     }
150   else
151     {
152       initial_result = tmpbuf;
153       allocated = sizeof (tmpbuf);
154     }
155   result = initial_result;
156   length = 0;
157
158   /* First, try a direct conversion, and see whether a conversion error
159      occurs at all.  */
160   {
161     const char *inptr = src;
162     size_t insize = srclen;
163
164     /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
165 # if defined _LIBICONV_VERSION \
166      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
167     /* Set to the initial state.  */
168     iconv (cd, NULL, NULL, NULL, NULL);
169 # endif
170
171     while (insize > 0)
172       {
173         char *outptr = result + length;
174         size_t outsize = allocated - extra_alloc - length;
175         bool incremented;
176         size_t res;
177         bool grow;
178
179         /* Use iconv_carefully instead of iconv here, because:
180            - If TO_CODESET is UTF-8, we can do the error handling in this loop,
181              no need for a second loop,
182            - With iconv() implementations other than GNU libiconv and GNU libc,
183              if we use iconv() in a big swoop, checking for an E2BIG return,
184              we lose the number of irreversible conversions.  */
185         res = iconv_carefully (cd,
186                                &inptr, &insize,
187                                &outptr, &outsize,
188                                &incremented);
189
190         length = outptr - result;
191         grow = (length + extra_alloc > allocated / 2);
192         if (res == (size_t)(-1))
193           {
194             if (errno == E2BIG)
195               grow = true;
196             else if (errno == EINVAL)
197               break;
198             else if (errno == EILSEQ && handler != iconveh_error)
199               {
200                 if (cd2 == (iconv_t)(-1))
201                   {
202                     /* TO_CODESET is UTF-8.  */
203                     /* Error handling can produce up to 1 byte of output.  */
204                     if (length + 1 + extra_alloc > allocated)
205                       {
206                         char *memory;
207
208                         allocated = 2 * allocated;
209                         if (length + 1 + extra_alloc > allocated)
210                           abort ();
211                         if (result == initial_result)
212                           memory = (char *) malloc (allocated);
213                         else
214                           memory = (char *) realloc (result, allocated);
215                         if (memory == NULL)
216                           {
217                             if (result != initial_result)
218                               free (result);
219                             errno = ENOMEM;
220                             return -1;
221                           }
222                         if (result == initial_result)
223                           memcpy (memory, initial_result, length);
224                         result = memory;
225                         grow = false;
226                       }
227                     /* The input is invalid in FROM_CODESET.  Eat up one byte
228                        and emit a question mark.  */
229                     if (!incremented)
230                       {
231                         if (insize == 0)
232                           abort ();
233                         inptr++;
234                         insize--;
235                       }
236                     result[length] = '?';
237                     length++;
238                   }
239                 else
240                   goto indirectly;
241               }
242             else
243               {
244                 if (result != initial_result)
245                   {
246                     int saved_errno = errno;
247                     free (result);
248                     errno = saved_errno;
249                   }
250                 return -1;
251               }
252           }
253         if (insize == 0)
254           break;
255         if (grow)
256           {
257             char *memory;
258
259             allocated = 2 * allocated;
260             if (result == initial_result)
261               memory = (char *) malloc (allocated);
262             else
263               memory = (char *) realloc (result, allocated);
264             if (memory == NULL)
265               {
266                 if (result != initial_result)
267                   free (result);
268                 errno = ENOMEM;
269                 return -1;
270               }
271             if (result == initial_result)
272               memcpy (memory, initial_result, length);
273             result = memory;
274           }
275       }
276   }
277
278   /* Now get the conversion state back to the initial state.
279      But avoid glibc-2.1 bug and Solaris 2.7 bug.  */
280 #if defined _LIBICONV_VERSION \
281     || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
282   for (;;)
283     {
284       char *outptr = result + length;
285       size_t outsize = allocated - extra_alloc - length;
286       size_t res;
287
288       res = iconv (cd, NULL, NULL, &outptr, &outsize);
289       length = outptr - result;
290       if (res == (size_t)(-1))
291         {
292           if (errno == E2BIG)
293             {
294               char *memory;
295
296               allocated = 2 * allocated;
297               if (result == initial_result)
298                 memory = (char *) malloc (allocated);
299               else
300                 memory = (char *) realloc (result, allocated);
301               if (memory == NULL)
302                 {
303                   if (result != initial_result)
304                     free (result);
305                   errno = ENOMEM;
306                   return -1;
307                 }
308               if (result == initial_result)
309                 memcpy (memory, initial_result, length);
310               result = memory;
311             }
312           else
313             {
314               if (result != initial_result)
315                 {
316                   int saved_errno = errno;
317                   free (result);
318                   errno = saved_errno;
319                 }
320               return -1;
321             }
322         }
323       else
324         break;
325     }
326 #endif
327
328   /* The direct conversion succeeded.  */
329   goto done;
330
331  indirectly:
332   /* The direct conversion failed, handler != iconveh_error,
333      and cd2 != (iconv_t)(-1).
334      Use a conversion through UTF-8.  */
335   length = 0;
336   {
337 # define utf8bufsize 4096 /* may also be smaller or larger than tmpbufsize */
338     char utf8buf[utf8bufsize + 1];
339     size_t utf8len = 0;
340     const char *in1ptr = src;
341     size_t in1size = srclen;
342     bool do_final_flush1 = true;
343     bool do_final_flush2 = true;
344
345     /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
346 # if defined _LIBICONV_VERSION \
347      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
348     /* Set to the initial state.  */
349     if (cd1 != (iconv_t)(-1))
350       iconv (cd1, NULL, NULL, NULL, NULL);
351     iconv (cd2, NULL, NULL, NULL, NULL);
352 # endif
353
354     while (in1size > 0 || do_final_flush1 || utf8len > 0 || do_final_flush2)
355       {
356         char *out1ptr = utf8buf + utf8len;
357         size_t out1size = utf8bufsize - utf8len;
358         bool incremented1;
359         size_t res1;
360         int errno1;
361
362         /* Conversion step 1: from FROM_CODESET to UTF-8.  */
363         if (in1size > 0)
364           {
365             if (cd1 != (iconv_t)(-1))
366               res1 = iconv_carefully (cd1,
367                                       (ICONV_CONST char **) &in1ptr, &in1size,
368                                       &out1ptr, &out1size,
369                                       &incremented1);
370             else
371               {
372                 /* FROM_CODESET is UTF-8.  */
373                 res1 = 0;
374                 do
375                   {
376                     ucs4_t uc;
377                     int n;
378                     int m;
379
380                     n = u8_mbtouc_safe (&uc, (const uint8_t *) in1ptr, in1size);
381                     if (uc == 0xfffd
382                         && !(n >= 3
383                              && (uint8_t)in1ptr[0] == 0xEF
384                              && (uint8_t)in1ptr[1] == 0xBF
385                              && (uint8_t)in1ptr[2] == 0xBD))
386                       {
387                         in1ptr += n;
388                         in1size -= n;
389                         errno = EILSEQ;
390                         res1 = (size_t)(-1);
391                         incremented1 = true;
392                         break;
393                       }
394                     if (out1size == 0)
395                       {
396                         errno = E2BIG;
397                         res1 = (size_t)(-1);
398                         incremented1 = false;
399                         break;
400                       }
401                     m = u8_uctomb ((uint8_t *) out1ptr, uc, out1size);
402                     if (m == -2)
403                       {
404                         errno = E2BIG;
405                         res1 = (size_t)(-1);
406                         incremented1 = false;
407                         break;
408                       }
409                     in1ptr += n;
410                     in1size -= n;
411                     if (m == -1)
412                       {
413                         errno = EILSEQ;
414                         res1 = (size_t)(-1);
415                         incremented1 = true;
416                         break;
417                       }
418                     out1ptr += m;
419                     out1size -= m;
420                   }
421                 while (in1size > 0);
422               }
423           }
424         else if (do_final_flush1)
425           {
426             /* Now get the conversion state of CD1 back to the initial state.
427                But avoid glibc-2.1 bug and Solaris 2.7 bug.  */
428 # if defined _LIBICONV_VERSION \
429      || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
430             if (cd1 != (iconv_t)(-1))
431               res1 = iconv (cd1, NULL, NULL, &out1ptr, &out1size);
432             else
433 # endif
434               res1 = 0;
435             do_final_flush1 = false;
436             incremented1 = true;
437           }
438         else
439           {
440             res1 = 0;
441             incremented1 = true;
442           }
443         if (res1 == (size_t)(-1)
444             && !(errno == E2BIG || errno == EINVAL || errno == EILSEQ))
445           {
446             if (result != initial_result)
447               {
448                 int saved_errno = errno;
449                 free (result);
450                 errno = saved_errno;
451               }
452             return -1;
453           }
454         if (res1 == (size_t)(-1)
455             && errno == EILSEQ && handler != iconveh_error)
456           {
457             /* The input is invalid in FROM_CODESET.  Eat up one byte and
458                emit a question mark.  Room for the question mark was allocated
459                at the end of utf8buf.  */
460             if (!incremented1)
461               {
462                 if (in1size == 0)
463                   abort ();
464                 in1ptr++;
465                 in1size--;
466               }
467             utf8buf[utf8len++] = '?';
468           }
469         errno1 = errno;
470         utf8len = out1ptr - utf8buf;
471
472         if (in1size == 0
473             || utf8len > utf8bufsize / 2
474             || (res1 == (size_t)(-1) && errno1 == E2BIG))
475           {
476             /* Conversion step 2: from UTF-8 to TO_CODESET.  */
477             const char *in2ptr = utf8buf;
478             size_t in2size = utf8len;
479
480             while (in2size > 0
481                    || (in1size == 0 && !do_final_flush1 && do_final_flush2))
482               {
483                 char *out2ptr = result + length;
484                 size_t out2size = allocated - extra_alloc - length;
485                 bool incremented2;
486                 size_t res2;
487                 bool grow;
488
489                 if (in2size > 0)
490                   res2 = iconv_carefully (cd2,
491                                           &in2ptr, &in2size,
492                                           &out2ptr, &out2size,
493                                           &incremented2);
494                 else /* in1size == 0 && !do_final_flush1
495                         && in2size == 0 && do_final_flush2 */
496                   {
497                     /* Now get the conversion state of CD1 back to the initial
498                        state.  But avoid glibc-2.1 bug and Solaris 2.7 bug.  */
499 # if defined _LIBICONV_VERSION \
500      || !((__GLIBC__ == 2 && __GLIBC_MINOR__ <= 1) || defined __sun)
501                     res2 = iconv (cd2, NULL, NULL, &out2ptr, &out2size);
502 # else
503                     res2 = 0;
504 # endif
505                     do_final_flush2 = false;
506                     incremented2 = true;
507                   }
508
509                 length = out2ptr - result;
510                 grow = (length + extra_alloc > allocated / 2);
511                 if (res2 == (size_t)(-1))
512                   {
513                     if (errno == E2BIG)
514                       grow = true;
515                     else if (errno == EINVAL)
516                       break;
517                     else if (errno == EILSEQ && handler != iconveh_error)
518                       {
519                         /* Error handling can produce up to 10 bytes of ASCII
520                            output.  But TO_CODESET may be UCS-2, UTF-16 or
521                            UCS-4, so use CD2 here as well.  */
522                         char scratchbuf[10];
523                         size_t scratchlen;
524                         ucs4_t uc;
525                         const char *inptr;
526                         size_t insize;
527                         size_t res;
528
529                         if (incremented2)
530                           {
531                             if (u8_prev (&uc, (const uint8_t *) in2ptr,
532                                          (const uint8_t *) utf8buf)
533                                 == NULL)
534                               abort ();
535                           }
536                         else
537                           {
538                             int n;
539                             if (in2size == 0)
540                               abort ();
541                             n = u8_mbtouc (&uc, (const uint8_t *) in2ptr,
542                                            in2size);
543                             in2ptr += n;
544                             in2size -= n;
545                           }
546
547                         if (handler == iconveh_escape_sequence)
548                           {
549                             static char hex[16] = "0123456789ABCDEF";
550                             scratchlen = 0;
551                             scratchbuf[scratchlen++] = '\\';
552                             if (uc < 0x10000)
553                               scratchbuf[scratchlen++] = 'u';
554                             else
555                               {
556                                 scratchbuf[scratchlen++] = 'U';
557                                 scratchbuf[scratchlen++] = hex[(uc>>28) & 15];
558                                 scratchbuf[scratchlen++] = hex[(uc>>24) & 15];
559                                 scratchbuf[scratchlen++] = hex[(uc>>20) & 15];
560                                 scratchbuf[scratchlen++] = hex[(uc>>16) & 15];
561                               }
562                             scratchbuf[scratchlen++] = hex[(uc>>12) & 15];
563                             scratchbuf[scratchlen++] = hex[(uc>>8) & 15];
564                             scratchbuf[scratchlen++] = hex[(uc>>4) & 15];
565                             scratchbuf[scratchlen++] = hex[uc & 15];
566                           }
567                         else
568                           {
569                             scratchbuf[0] = '?';
570                             scratchlen = 1;
571                           }
572
573                         inptr = scratchbuf;
574                         insize = scratchlen;
575                         res = iconv (cd2,
576                                      (ICONV_CONST char **) &inptr, &insize,
577                                      &out2ptr, &out2size);
578                         length = out2ptr - result;
579                         if (res == (size_t)(-1) && errno == E2BIG)
580                           {
581                             char *memory;
582
583                             allocated = 2 * allocated;
584                             if (length + 1 + extra_alloc > allocated)
585                               abort ();
586                             if (result == initial_result)
587                               memory = (char *) malloc (allocated);
588                             else
589                               memory = (char *) realloc (result, allocated);
590                             if (memory == NULL)
591                               {
592                                 if (result != initial_result)
593                                   free (result);
594                                 errno = ENOMEM;
595                                 return -1;
596                               }
597                             if (result == initial_result)
598                               memcpy (memory, initial_result, length);
599                             result = memory;
600                             grow = false;
601
602                             out2ptr = result + length;
603                             out2size = allocated - extra_alloc - length;
604                             res = iconv (cd2,
605                                          (ICONV_CONST char **) &inptr, &insize,
606                                          &out2ptr, &out2size);
607                             length = out2ptr - result;
608                           }
609 # if !defined _LIBICONV_VERSION && !defined __GLIBC__
610                         /* Irix iconv() inserts a NUL byte if it cannot convert.
611                            NetBSD iconv() inserts a question mark if it cannot
612                            convert.
613                            Only GNU libiconv and GNU libc are known to prefer
614                            to fail rather than doing a lossy conversion.  */
615                         if (res != (size_t)(-1) && res > 0)
616                           {
617                             errno = EILSEQ;
618                             res = (size_t)(-1);
619                           }
620 # endif
621                         if (res == (size_t)(-1))
622                           {
623                             /* Failure converting the ASCII replacement.  */
624                             if (result != initial_result)
625                               {
626                                 int saved_errno = errno;
627                                 free (result);
628                                 errno = saved_errno;
629                               }
630                             return -1;
631                           }
632                       }
633                     else
634                       {
635                         if (result != initial_result)
636                           {
637                             int saved_errno = errno;
638                             free (result);
639                             errno = saved_errno;
640                           }
641                         return -1;
642                       }
643                   }
644                 if (!(in2size > 0
645                       || (in1size == 0 && !do_final_flush1 && do_final_flush2)))
646                   break;
647                 if (grow)
648                   {
649                     char *memory;
650
651                     allocated = 2 * allocated;
652                     if (result == initial_result)
653                       memory = (char *) malloc (allocated);
654                     else
655                       memory = (char *) realloc (result, allocated);
656                     if (memory == NULL)
657                       {
658                         if (result != initial_result)
659                           free (result);
660                         errno = ENOMEM;
661                         return -1;
662                       }
663                     if (result == initial_result)
664                       memcpy (memory, initial_result, length);
665                     result = memory;
666                   }
667               }
668
669             /* Move the remaining bytes to the beginning of utf8buf.  */
670             if (in2size > 0)
671               memmove (utf8buf, in2ptr, in2size);
672             utf8len = in2size;
673           }
674
675         if (res1 == (size_t)(-1))
676           {
677             if (errno1 == EINVAL)
678               in1size = 0;
679             else if (errno1 == EILSEQ)
680               {
681                 if (result != initial_result)
682                   free (result);
683                 errno = errno1;
684                 return -1;
685               }
686           }
687       }
688 # undef utf8bufsize
689   }
690
691  done:
692   /* Now the final memory allocation.  */
693   if (result == tmpbuf)
694     {
695       char *memory;
696
697       memory = (char *) malloc (length + extra_alloc);
698       if (memory != NULL)
699         {
700           memcpy (memory, tmpbuf, length);
701           result = memory;
702         }
703       else
704         {
705           errno = ENOMEM;
706           return -1;
707         }
708     }
709   else if (result != *resultp && length + extra_alloc < allocated)
710     {
711       /* Shrink the allocated memory if possible.  */
712       char *memory;
713
714       memory = (char *) realloc (result, length + extra_alloc);
715       if (memory != NULL)
716         result = memory;
717     }
718   *resultp = result;
719   *lengthp = length;
720   return 0;
721 # undef tmpbuf
722 # undef tmpbufsize
723 }
724
725 int
726 mem_cd_iconveh (const char *src, size_t srclen,
727                 iconv_t cd, iconv_t cd1, iconv_t cd2,
728                 enum iconv_ilseq_handler handler,
729                 char **resultp, size_t *lengthp)
730 {
731   return mem_cd_iconveh_internal (src, srclen, cd, cd1, cd2, handler, 0,
732                                   resultp, lengthp);
733 }
734
735 char *
736 str_cd_iconveh (const char *src,
737                 iconv_t cd, iconv_t cd1, iconv_t cd2,
738                 enum iconv_ilseq_handler handler)
739 {
740   /* For most encodings, a trailing NUL byte in the input will be converted
741      to a trailing NUL byte in the output.  But not for UTF-7.  So that this
742      function is usable for UTF-7, we have to exclude the NUL byte from the
743      conversion and add it by hand afterwards.  */
744   char *result = NULL;
745   size_t length = 0;
746   int retval = mem_cd_iconveh_internal (src, strlen (src),
747                                         cd, cd1, cd2, handler, 1,
748                                         &result, &length);
749
750   if (retval < 0)
751     {
752       if (result != NULL)
753         {
754           int saved_errno = errno;
755           free (result);
756           errno = saved_errno;
757         }
758       return NULL;
759     }
760
761   /* Add the terminating NUL byte.  */
762   result[length] = '\0';
763
764   return result;
765 }
766
767 #endif
768
769 int
770 mem_iconveh (const char *src, size_t srclen,
771              const char *from_codeset, const char *to_codeset,
772              enum iconv_ilseq_handler handler,
773              char **resultp, size_t *lengthp)
774 {
775   if (c_strcasecmp (from_codeset, to_codeset) == 0)
776     {
777       char *result;
778
779       if (*resultp != NULL && *lengthp >= srclen)
780         result = *resultp;
781       else
782         {
783           result = (char *) malloc (srclen);
784           if (result == NULL)
785             {
786               errno = ENOMEM;
787               return -1;
788             }
789         }
790       memcpy (result, src, srclen);
791       *resultp = result;
792       *lengthp = srclen;
793       return 0;
794     }
795   else
796     {
797 #if HAVE_ICONV
798       iconv_t cd;
799       iconv_t cd1;
800       iconv_t cd2;
801       char *result;
802       size_t length;
803       int retval;
804
805       /* Avoid glibc-2.1 bug with EUC-KR.  */
806 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
807       if (c_strcasecmp (from_codeset, "EUC-KR") == 0
808           || c_strcasecmp (to_codeset, "EUC-KR") == 0)
809         {
810           errno = EINVAL;
811           return -1;
812         }
813 # endif
814
815       cd = iconv_open (to_codeset, from_codeset);
816       if (cd == (iconv_t)(-1))
817         return -1;
818
819       if (c_strcasecmp (from_codeset, "UTF-8") == 0)
820         cd1 = (iconv_t)(-1);
821       else
822         {
823           cd1 = iconv_open ("UTF-8", from_codeset);
824           if (cd1 == (iconv_t)(-1))
825             {
826               int saved_errno = errno;
827               iconv_close (cd);
828               errno = saved_errno;
829               return -1;
830             }
831         }
832
833       if (c_strcasecmp (to_codeset, "UTF-8") == 0)
834         cd2 = (iconv_t)(-1);
835       else
836         {
837           cd2 = iconv_open (to_codeset, "UTF-8");
838           if (cd2 == (iconv_t)(-1))
839             {
840               int saved_errno = errno;
841               if (cd1 != (iconv_t)(-1))
842                 iconv_close (cd1);
843               iconv_close (cd);
844               errno = saved_errno;
845               return -1;
846             }
847         }
848
849       result = *resultp;
850       length = *lengthp;
851       retval =
852         mem_cd_iconveh (src, srclen, cd, cd1, cd2, handler, &result, &length);
853
854       if (retval < 0)
855         {
856           /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv.  */
857           int saved_errno = errno;
858           if (cd2 != (iconv_t)(-1))
859             iconv_close (cd2);
860           if (cd1 != (iconv_t)(-1))
861             iconv_close (cd1);
862           iconv_close (cd);
863           errno = saved_errno;
864         }
865       else
866         {
867           if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
868             {
869               /* Return -1, but free the allocated memory, and while doing
870                  that, preserve the errno from iconv_close.  */
871               int saved_errno = errno;
872               if (cd1 != (iconv_t)(-1))
873                 iconv_close (cd1);
874               iconv_close (cd);
875               if (result != *resultp && result != NULL)
876                 free (result);
877               errno = saved_errno;
878               return -1;
879             }
880           if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
881             {
882               /* Return -1, but free the allocated memory, and while doing
883                  that, preserve the errno from iconv_close.  */
884               int saved_errno = errno;
885               iconv_close (cd);
886               if (result != *resultp && result != NULL)
887                 free (result);
888               errno = saved_errno;
889               return -1;
890             }
891           if (iconv_close (cd) < 0)
892             {
893               /* Return -1, but free the allocated memory, and while doing
894                  that, preserve the errno from iconv_close.  */
895               int saved_errno = errno;
896               if (result != *resultp && result != NULL)
897                 free (result);
898               errno = saved_errno;
899               return -1;
900             }
901           *resultp = result;
902           *lengthp = length;
903         }
904       return retval;
905 #else
906       /* This is a different error code than if iconv_open existed but didn't
907          support from_codeset and to_codeset, so that the caller can emit
908          an error message such as
909            "iconv() is not supported. Installing GNU libiconv and
910             then reinstalling this package would fix this."  */
911       errno = ENOSYS;
912       return -1;
913 #endif
914     }
915 }
916
917 char *
918 str_iconveh (const char *src,
919              const char *from_codeset, const char *to_codeset,
920              enum iconv_ilseq_handler handler)
921 {
922   if (c_strcasecmp (from_codeset, to_codeset) == 0)
923     return strdup (src);
924   else
925     {
926 #if HAVE_ICONV
927       iconv_t cd;
928       iconv_t cd1;
929       iconv_t cd2;
930       char *result;
931
932       /* Avoid glibc-2.1 bug with EUC-KR.  */
933 # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
934       if (c_strcasecmp (from_codeset, "EUC-KR") == 0
935           || c_strcasecmp (to_codeset, "EUC-KR") == 0)
936         {
937           errno = EINVAL;
938           return NULL;
939         }
940 # endif
941
942       cd = iconv_open (to_codeset, from_codeset);
943       if (cd == (iconv_t)(-1))
944         return NULL;
945
946       if (c_strcasecmp (from_codeset, "UTF-8") == 0)
947         cd1 = (iconv_t)(-1);
948       else
949         {
950           cd1 = iconv_open ("UTF-8", from_codeset);
951           if (cd1 == (iconv_t)(-1))
952             {
953               int saved_errno = errno;
954               iconv_close (cd);
955               errno = saved_errno;
956               return NULL;
957             }
958         }
959
960       if (c_strcasecmp (to_codeset, "UTF-8") == 0)
961         cd2 = (iconv_t)(-1);
962       else
963         {
964           cd2 = iconv_open (to_codeset, "UTF-8");
965           if (cd2 == (iconv_t)(-1))
966             {
967               int saved_errno = errno;
968               if (cd1 != (iconv_t)(-1))
969                 iconv_close (cd1);
970               iconv_close (cd);
971               errno = saved_errno;
972               return NULL;
973             }
974         }
975
976       result = str_cd_iconveh (src, cd, cd1, cd2, handler);
977
978       if (result == NULL)
979         {
980           /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv.  */
981           int saved_errno = errno;
982           if (cd2 != (iconv_t)(-1))
983             iconv_close (cd2);
984           if (cd1 != (iconv_t)(-1))
985             iconv_close (cd1);
986           iconv_close (cd);
987           errno = saved_errno;
988         }
989       else
990         {
991           if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
992             {
993               /* Return NULL, but free the allocated memory, and while doing
994                  that, preserve the errno from iconv_close.  */
995               int saved_errno = errno;
996               if (cd1 != (iconv_t)(-1))
997                 iconv_close (cd1);
998               iconv_close (cd);
999               free (result);
1000               errno = saved_errno;
1001               return NULL;
1002             }
1003           if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
1004             {
1005               /* Return NULL, but free the allocated memory, and while doing
1006                  that, preserve the errno from iconv_close.  */
1007               int saved_errno = errno;
1008               iconv_close (cd);
1009               free (result);
1010               errno = saved_errno;
1011               return NULL;
1012             }
1013           if (iconv_close (cd) < 0)
1014             {
1015               /* Return NULL, but free the allocated memory, and while doing
1016                  that, preserve the errno from iconv_close.  */
1017               int saved_errno = errno;
1018               free (result);
1019               errno = saved_errno;
1020               return NULL;
1021             }
1022         }
1023       return result;
1024 #else
1025       /* This is a different error code than if iconv_open existed but didn't
1026          support from_codeset and to_codeset, so that the caller can emit
1027          an error message such as
1028            "iconv() is not supported. Installing GNU libiconv and
1029             then reinstalling this package would fix this."  */
1030       errno = ENOSYS;
1031       return NULL;
1032 #endif
1033     }
1034 }