Merge branch 'master' of ssh://karl@git.sv.gnu.org/srv/git/gnulib
[gnulib.git] / lib / striconveh.c
1 /* Character set conversion with error handling.
2    Copyright (C) 2001-2008 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 3 of the License, or
8    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "striconveh.h"
22
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #if HAVE_ICONV
29 # include <iconv.h>
30 # include "unistr.h"
31 #endif
32
33 #include "c-strcase.h"
34 #include "c-strcaseeq.h"
35
36 #ifndef SIZE_MAX
37 # define SIZE_MAX ((size_t) -1)
38 #endif
39
40
41 #if HAVE_ICONV
42
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.  */
46
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.  */
55 static size_t
56 iconv_carefully (iconv_t cd,
57                  const char **inbuf, size_t *inbytesleft,
58                  char **outbuf, size_t *outbytesleft,
59                  bool *incremented)
60 {
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;
66   size_t res;
67
68   do
69     {
70       size_t insize;
71
72       inptr_before = inptr;
73       res = (size_t)(-1);
74
75       for (insize = 1; inptr + insize <= inptr_end; insize++)
76         {
77           res = iconv (cd,
78                        (ICONV_CONST char **) &inptr, &insize,
79                        &outptr, &outsize);
80           if (!(res == (size_t)(-1) && errno == EINVAL))
81             break;
82           /* We expect that no input bytes have been consumed so far.  */
83           if (inptr != inptr_before)
84             abort ();
85         }
86
87       if (res == 0)
88         {
89           *outbuf = outptr;
90           *outbytesleft = outsize;
91         }
92     }
93   while (res == 0 && inptr < inptr_end);
94
95   *inbuf = inptr;
96   *inbytesleft = inptr_end - inptr;
97   if (res != (size_t)(-1) && res > 0)
98     {
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);
104       errno = EILSEQ;
105       return (size_t)(-1);
106     }
107   else
108     {
109       *incremented = false;
110       return res;
111     }
112 }
113 # else
114 #  define iconv_carefully(cd, inbuf, inbytesleft, outbuf, outbytesleft, incremented) \
115      (*(incremented) = false, \
116       iconv (cd, (ICONV_CONST char **) (inbuf), inbytesleft, outbuf, outbytesleft))
117 # endif
118
119 /* iconv_carefully_1 is like iconv_carefully, except that it stops after
120    converting one character.  */
121 static size_t
122 iconv_carefully_1 (iconv_t cd,
123                    const char **inbuf, size_t *inbytesleft,
124                    char **outbuf, size_t *outbytesleft,
125                    bool *incremented)
126 {
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);
133   size_t insize;
134
135   for (insize = 1; inptr + insize <= inptr_end; insize++)
136     {
137       res = iconv (cd,
138                    (ICONV_CONST char **) &inptr, &insize,
139                    &outptr, &outsize);
140       if (!(res == (size_t)(-1) && errno == EINVAL))
141         break;
142       /* We expect that no input bytes have been consumed so far.  */
143       if (inptr != inptr_before)
144         abort ();
145     }
146
147   *inbuf = inptr;
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)
155     {
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);
161       errno = EILSEQ;
162       return (size_t)(-1);
163     }
164 # endif
165
166   if (res != (size_t)(-1))
167     {
168       *outbuf = outptr;
169       *outbytesleft = outsize;
170     }
171   *incremented = false;
172   return res;
173 }
174
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
181        character.  */
182 static size_t
183 utf8conv_carefully (bool one_character_only,
184                     const char **inbuf, size_t *inbytesleft,
185                     char **outbuf, size_t *outbytesleft,
186                     bool *incremented)
187 {
188   const char *inptr = *inbuf;
189   size_t insize = *inbytesleft;
190   char *outptr = *outbuf;
191   size_t outsize = *outbytesleft;
192   size_t res;
193
194   res = 0;
195   do
196     {
197       ucs4_t uc;
198       int n;
199       int m;
200
201       n = u8_mbtoucr (&uc, (const uint8_t *) inptr, insize);
202       if (n < 0)
203         {
204           errno = (n == -2 ? EINVAL : EILSEQ);
205           n = u8_mbtouc (&uc, (const uint8_t *) inptr, insize);
206           inptr += n;
207           insize -= n;
208           res = (size_t)(-1);
209           *incremented = true;
210           break;
211         }
212       if (outsize == 0)
213         {
214           errno = E2BIG;
215           res = (size_t)(-1);
216           *incremented = false;
217           break;
218         }
219       m = u8_uctomb ((uint8_t *) outptr, uc, outsize);
220       if (m == -2)
221         {
222           errno = E2BIG;
223           res = (size_t)(-1);
224           *incremented = false;
225           break;
226         }
227       inptr += n;
228       insize -= n;
229       if (m == -1)
230         {
231           errno = EILSEQ;
232           res = (size_t)(-1);
233           *incremented = true;
234           break;
235         }
236       outptr += m;
237       outsize -= m;
238     }
239   while (!one_character_only && insize > 0);
240
241   *inbuf = inptr;
242   *inbytesleft = insize;
243   *outbuf = outptr;
244   *outbytesleft = outsize;
245   return res;
246 }
247
248 static int
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,
252                          size_t extra_alloc,
253                          size_t *offsets,
254                          char **resultp, size_t *lengthp)
255 {
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
266
267   char *initial_result;
268   char *result;
269   size_t allocated;
270   size_t length;
271   size_t last_length = (size_t)(-1); /* only needed if offsets != NULL */
272
273   if (*resultp != NULL && *lengthp >= sizeof (tmpbuf))
274     {
275       initial_result = *resultp;
276       allocated = *lengthp;
277     }
278   else
279     {
280       initial_result = tmpbuf;
281       allocated = sizeof (tmpbuf);
282     }
283   result = initial_result;
284
285   /* Test whether a direct conversion is possible at all.  */
286   if (cd == (iconv_t)(-1))
287     goto indirectly;
288
289   if (offsets != NULL)
290     {
291       size_t i;
292
293       for (i = 0; i < srclen; i++)
294         offsets[i] = (size_t)(-1);
295
296       last_length = (size_t)(-1);
297     }
298   length = 0;
299
300   /* First, try a direct conversion, and see whether a conversion error
301      occurs at all.  */
302   {
303     const char *inptr = src;
304     size_t insize = srclen;
305
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);
311 # endif
312
313     while (insize > 0)
314       {
315         char *outptr = result + length;
316         size_t outsize = allocated - extra_alloc - length;
317         bool incremented;
318         size_t res;
319         bool grow;
320
321         if (offsets != NULL)
322           {
323             if (length != last_length) /* ensure that offset[] be increasing */
324               {
325                 offsets[inptr - src] = length;
326                 last_length = length;
327               }
328             res = iconv_carefully_1 (cd,
329                                      &inptr, &insize,
330                                      &outptr, &outsize,
331                                      &incremented);
332           }
333         else
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,
341                                  &inptr, &insize,
342                                  &outptr, &outsize,
343                                  &incremented);
344
345         length = outptr - result;
346         grow = (length + extra_alloc > allocated / 2);
347         if (res == (size_t)(-1))
348           {
349             if (errno == E2BIG)
350               grow = true;
351             else if (errno == EINVAL)
352               break;
353             else if (errno == EILSEQ && handler != iconveh_error)
354               {
355                 if (cd2 == (iconv_t)(-1))
356                   {
357                     /* TO_CODESET is UTF-8.  */
358                     /* Error handling can produce up to 1 byte of output.  */
359                     if (length + 1 + extra_alloc > allocated)
360                       {
361                         char *memory;
362
363                         allocated = 2 * allocated;
364                         if (length + 1 + extra_alloc > allocated)
365                           abort ();
366                         if (result == initial_result)
367                           memory = (char *) malloc (allocated);
368                         else
369                           memory = (char *) realloc (result, allocated);
370                         if (memory == NULL)
371                           {
372                             if (result != initial_result)
373                               free (result);
374                             errno = ENOMEM;
375                             return -1;
376                           }
377                         if (result == initial_result)
378                           memcpy (memory, initial_result, length);
379                         result = memory;
380                         grow = false;
381                       }
382                     /* The input is invalid in FROM_CODESET.  Eat up one byte
383                        and emit a question mark.  */
384                     if (!incremented)
385                       {
386                         if (insize == 0)
387                           abort ();
388                         inptr++;
389                         insize--;
390                       }
391                     result[length] = '?';
392                     length++;
393                   }
394                 else
395                   goto indirectly;
396               }
397             else
398               {
399                 if (result != initial_result)
400                   {
401                     int saved_errno = errno;
402                     free (result);
403                     errno = saved_errno;
404                   }
405                 return -1;
406               }
407           }
408         if (insize == 0)
409           break;
410         if (grow)
411           {
412             char *memory;
413
414             allocated = 2 * allocated;
415             if (result == initial_result)
416               memory = (char *) malloc (allocated);
417             else
418               memory = (char *) realloc (result, allocated);
419             if (memory == NULL)
420               {
421                 if (result != initial_result)
422                   free (result);
423                 errno = ENOMEM;
424                 return -1;
425               }
426             if (result == initial_result)
427               memcpy (memory, initial_result, length);
428             result = memory;
429           }
430       }
431   }
432
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)
437   for (;;)
438     {
439       char *outptr = result + length;
440       size_t outsize = allocated - extra_alloc - length;
441       size_t res;
442
443       res = iconv (cd, NULL, NULL, &outptr, &outsize);
444       length = outptr - result;
445       if (res == (size_t)(-1))
446         {
447           if (errno == E2BIG)
448             {
449               char *memory;
450
451               allocated = 2 * allocated;
452               if (result == initial_result)
453                 memory = (char *) malloc (allocated);
454               else
455                 memory = (char *) realloc (result, allocated);
456               if (memory == NULL)
457                 {
458                   if (result != initial_result)
459                     free (result);
460                   errno = ENOMEM;
461                   return -1;
462                 }
463               if (result == initial_result)
464                 memcpy (memory, initial_result, length);
465               result = memory;
466             }
467           else
468             {
469               if (result != initial_result)
470                 {
471                   int saved_errno = errno;
472                   free (result);
473                   errno = saved_errno;
474                 }
475               return -1;
476             }
477         }
478       else
479         break;
480     }
481 #endif
482
483   /* The direct conversion succeeded.  */
484   goto done;
485
486  indirectly:
487   /* The direct conversion failed.
488      Use a conversion through UTF-8.  */
489   if (offsets != NULL)
490     {
491       size_t i;
492
493       for (i = 0; i < srclen; i++)
494         offsets[i] = (size_t)(-1);
495
496       last_length = (size_t)(-1);
497     }
498   length = 0;
499   {
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];
503     size_t utf8len = 0;
504     const char *in1ptr = src;
505     size_t in1size = srclen;
506     bool do_final_flush1 = true;
507     bool do_final_flush2 = true;
508
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);
517 # endif
518
519     while (in1size > 0 || do_final_flush1 || utf8len > 0 || do_final_flush2)
520       {
521         char *out1ptr = utf8buf + utf8len;
522         size_t out1size = utf8bufsize - utf8len;
523         bool incremented1;
524         size_t res1;
525         int errno1;
526
527         /* Conversion step 1: from FROM_CODESET to UTF-8.  */
528         if (in1size > 0)
529           {
530             if (offsets != NULL
531                 && length != last_length) /* ensure that offset[] be increasing */
532               {
533                 offsets[in1ptr - src] = length;
534                 last_length = length;
535               }
536             if (cd1 != (iconv_t)(-1))
537               {
538                 if (slowly)
539                   res1 = iconv_carefully_1 (cd1,
540                                             &in1ptr, &in1size,
541                                             &out1ptr, &out1size,
542                                             &incremented1);
543                 else
544                   res1 = iconv_carefully (cd1,
545                                           &in1ptr, &in1size,
546                                           &out1ptr, &out1size,
547                                           &incremented1);
548               }
549             else
550               {
551                 /* FROM_CODESET is UTF-8.  */
552                 res1 = utf8conv_carefully (slowly,
553                                            &in1ptr, &in1size,
554                                            &out1ptr, &out1size,
555                                            &incremented1);
556               }
557           }
558         else if (do_final_flush1)
559           {
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);
566             else
567 # endif
568               res1 = 0;
569             do_final_flush1 = false;
570             incremented1 = true;
571           }
572         else
573           {
574             res1 = 0;
575             incremented1 = true;
576           }
577         if (res1 == (size_t)(-1)
578             && !(errno == E2BIG || errno == EINVAL || errno == EILSEQ))
579           {
580             if (result != initial_result)
581               {
582                 int saved_errno = errno;
583                 free (result);
584                 errno = saved_errno;
585               }
586             return -1;
587           }
588         if (res1 == (size_t)(-1)
589             && errno == EILSEQ && handler != iconveh_error)
590           {
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.  */
594             if (!incremented1)
595               {
596                 if (in1size == 0)
597                   abort ();
598                 in1ptr++;
599                 in1size--;
600               }
601             utf8buf[utf8len++] = '?';
602           }
603         errno1 = errno;
604         utf8len = out1ptr - utf8buf;
605
606         if (offsets != NULL
607             || in1size == 0
608             || utf8len > utf8bufsize / 2
609             || (res1 == (size_t)(-1) && errno1 == E2BIG))
610           {
611             /* Conversion step 2: from UTF-8 to TO_CODESET.  */
612             const char *in2ptr = utf8buf;
613             size_t in2size = utf8len;
614
615             while (in2size > 0
616                    || (in1size == 0 && !do_final_flush1 && do_final_flush2))
617               {
618                 char *out2ptr = result + length;
619                 size_t out2size = allocated - extra_alloc - length;
620                 bool incremented2;
621                 size_t res2;
622                 bool grow;
623
624                 if (in2size > 0)
625                   {
626                     if (cd2 != (iconv_t)(-1))
627                       res2 = iconv_carefully (cd2,
628                                               &in2ptr, &in2size,
629                                               &out2ptr, &out2size,
630                                               &incremented2);
631                     else
632                       /* TO_CODESET is UTF-8.  */
633                       res2 = utf8conv_carefully (false,
634                                                  &in2ptr, &in2size,
635                                                  &out2ptr, &out2size,
636                                                  &incremented2);
637                   }
638                 else /* in1size == 0 && !do_final_flush1
639                         && in2size == 0 && do_final_flush2 */
640                   {
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);
647                     else
648 # endif
649                       res2 = 0;
650                     do_final_flush2 = false;
651                     incremented2 = true;
652                   }
653
654                 length = out2ptr - result;
655                 grow = (length + extra_alloc > allocated / 2);
656                 if (res2 == (size_t)(-1))
657                   {
658                     if (errno == E2BIG)
659                       grow = true;
660                     else if (errno == EINVAL)
661                       break;
662                     else if (errno == EILSEQ && handler != iconveh_error)
663                       {
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.  */
667                         char scratchbuf[10];
668                         size_t scratchlen;
669                         ucs4_t uc;
670                         const char *inptr;
671                         size_t insize;
672                         size_t res;
673
674                         if (incremented2)
675                           {
676                             if (u8_prev (&uc, (const uint8_t *) in2ptr,
677                                          (const uint8_t *) utf8buf)
678                                 == NULL)
679                               abort ();
680                           }
681                         else
682                           {
683                             int n;
684                             if (in2size == 0)
685                               abort ();
686                             n = u8_mbtouc_unsafe (&uc, (const uint8_t *) in2ptr,
687                                                   in2size);
688                             in2ptr += n;
689                             in2size -= n;
690                           }
691
692                         if (handler == iconveh_escape_sequence)
693                           {
694                             static char hex[16] = "0123456789ABCDEF";
695                             scratchlen = 0;
696                             scratchbuf[scratchlen++] = '\\';
697                             if (uc < 0x10000)
698                               scratchbuf[scratchlen++] = 'u';
699                             else
700                               {
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];
706                               }
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];
711                           }
712                         else
713                           {
714                             scratchbuf[0] = '?';
715                             scratchlen = 1;
716                           }
717
718                         inptr = scratchbuf;
719                         insize = scratchlen;
720                         if (cd2 != (iconv_t)(-1))
721                           res = iconv (cd2,
722                                        (ICONV_CONST char **) &inptr, &insize,
723                                        &out2ptr, &out2size);
724                         else
725                           {
726                             /* TO_CODESET is UTF-8.  */
727                             if (out2size >= insize)
728                               {
729                                 memcpy (out2ptr, inptr, insize);
730                                 out2ptr += insize;
731                                 out2size -= insize;
732                                 inptr += insize;
733                                 insize = 0;
734                                 res = 0;
735                               }
736                             else
737                               {
738                                 errno = E2BIG;
739                                 res = (size_t)(-1);
740                               }
741                           }
742                         length = out2ptr - result;
743                         if (res == (size_t)(-1) && errno == E2BIG)
744                           {
745                             char *memory;
746
747                             allocated = 2 * allocated;
748                             if (length + 1 + extra_alloc > allocated)
749                               abort ();
750                             if (result == initial_result)
751                               memory = (char *) malloc (allocated);
752                             else
753                               memory = (char *) realloc (result, allocated);
754                             if (memory == NULL)
755                               {
756                                 if (result != initial_result)
757                                   free (result);
758                                 errno = ENOMEM;
759                                 return -1;
760                               }
761                             if (result == initial_result)
762                               memcpy (memory, initial_result, length);
763                             result = memory;
764                             grow = false;
765
766                             out2ptr = result + length;
767                             out2size = allocated - extra_alloc - length;
768                             if (cd2 != (iconv_t)(-1))
769                               res = iconv (cd2,
770                                            (ICONV_CONST char **) &inptr,
771                                            &insize,
772                                            &out2ptr, &out2size);
773                             else
774                               {
775                                 /* TO_CODESET is UTF-8.  */
776                                 if (!(out2size >= insize))
777                                   abort ();
778                                 memcpy (out2ptr, inptr, insize);
779                                 out2ptr += insize;
780                                 out2size -= insize;
781                                 inptr += insize;
782                                 insize = 0;
783                                 res = 0;
784                               }
785                             length = out2ptr - result;
786                           }
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
790                            convert.
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)
794                           {
795                             errno = EILSEQ;
796                             res = (size_t)(-1);
797                           }
798 # endif
799                         if (res == (size_t)(-1))
800                           {
801                             /* Failure converting the ASCII replacement.  */
802                             if (result != initial_result)
803                               {
804                                 int saved_errno = errno;
805                                 free (result);
806                                 errno = saved_errno;
807                               }
808                             return -1;
809                           }
810                       }
811                     else
812                       {
813                         if (result != initial_result)
814                           {
815                             int saved_errno = errno;
816                             free (result);
817                             errno = saved_errno;
818                           }
819                         return -1;
820                       }
821                   }
822                 if (!(in2size > 0
823                       || (in1size == 0 && !do_final_flush1 && do_final_flush2)))
824                   break;
825                 if (grow)
826                   {
827                     char *memory;
828
829                     allocated = 2 * allocated;
830                     if (result == initial_result)
831                       memory = (char *) malloc (allocated);
832                     else
833                       memory = (char *) realloc (result, allocated);
834                     if (memory == NULL)
835                       {
836                         if (result != initial_result)
837                           free (result);
838                         errno = ENOMEM;
839                         return -1;
840                       }
841                     if (result == initial_result)
842                       memcpy (memory, initial_result, length);
843                     result = memory;
844                   }
845               }
846
847             /* Move the remaining bytes to the beginning of utf8buf.  */
848             if (in2size > 0)
849               memmove (utf8buf, in2ptr, in2size);
850             utf8len = in2size;
851           }
852
853         if (res1 == (size_t)(-1))
854           {
855             if (errno1 == EINVAL)
856               in1size = 0;
857             else if (errno1 == EILSEQ)
858               {
859                 if (result != initial_result)
860                   free (result);
861                 errno = errno1;
862                 return -1;
863               }
864           }
865       }
866 # undef utf8bufsize
867   }
868
869  done:
870   /* Now the final memory allocation.  */
871   if (result == tmpbuf)
872     {
873       size_t memsize = length + extra_alloc;
874       char *memory;
875
876       memory = (char *) malloc (memsize > 0 ? memsize : 1);
877       if (memory != NULL)
878         {
879           memcpy (memory, tmpbuf, length);
880           result = memory;
881         }
882       else
883         {
884           errno = ENOMEM;
885           return -1;
886         }
887     }
888   else if (result != *resultp && length + extra_alloc < allocated)
889     {
890       /* Shrink the allocated memory if possible.  */
891       size_t memsize = length + extra_alloc;
892       char *memory;
893
894       memory = (char *) realloc (result, memsize > 0 ? memsize : 1);
895       if (memory != NULL)
896         result = memory;
897     }
898   *resultp = result;
899   *lengthp = length;
900   return 0;
901 # undef tmpbuf
902 # undef tmpbufsize
903 }
904
905 int
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,
909                 size_t *offsets,
910                 char **resultp, size_t *lengthp)
911 {
912   return mem_cd_iconveh_internal (src, srclen, cd, cd1, cd2, handler, 0,
913                                   offsets, resultp, lengthp);
914 }
915
916 char *
917 str_cd_iconveh (const char *src,
918                 iconv_t cd, iconv_t cd1, iconv_t cd2,
919                 enum iconv_ilseq_handler handler)
920 {
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.  */
925   char *result = NULL;
926   size_t length = 0;
927   int retval = mem_cd_iconveh_internal (src, strlen (src),
928                                         cd, cd1, cd2, handler, 1, NULL,
929                                         &result, &length);
930
931   if (retval < 0)
932     {
933       if (result != NULL)
934         {
935           int saved_errno = errno;
936           free (result);
937           errno = saved_errno;
938         }
939       return NULL;
940     }
941
942   /* Add the terminating NUL byte.  */
943   result[length] = '\0';
944
945   return result;
946 }
947
948 #endif
949
950 int
951 mem_iconveh (const char *src, size_t srclen,
952              const char *from_codeset, const char *to_codeset,
953              enum iconv_ilseq_handler handler,
954              size_t *offsets,
955              char **resultp, size_t *lengthp)
956 {
957   if (srclen == 0)
958     {
959       /* Nothing to convert.  */
960       *lengthp = 0;
961       return 0;
962     }
963   else if (offsets == NULL && c_strcasecmp (from_codeset, to_codeset) == 0)
964     {
965       char *result;
966
967       if (*resultp != NULL && *lengthp >= srclen)
968         result = *resultp;
969       else
970         {
971           result = (char *) malloc (srclen);
972           if (result == NULL)
973             {
974               errno = ENOMEM;
975               return -1;
976             }
977         }
978       memcpy (result, src, srclen);
979       *resultp = result;
980       *lengthp = srclen;
981       return 0;
982     }
983   else
984     {
985 #if HAVE_ICONV
986       iconv_t cd;
987       iconv_t cd1;
988       iconv_t cd2;
989       char *result;
990       size_t length;
991       int retval;
992
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)
997         {
998           errno = EINVAL;
999           return -1;
1000         }
1001 # endif
1002
1003       cd = iconv_open (to_codeset, from_codeset);
1004
1005       if (STRCASEEQ (from_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0))
1006         cd1 = (iconv_t)(-1);
1007       else
1008         {
1009           cd1 = iconv_open ("UTF-8", from_codeset);
1010           if (cd1 == (iconv_t)(-1))
1011             {
1012               int saved_errno = errno;
1013               if (cd != (iconv_t)(-1))
1014                 iconv_close (cd);
1015               errno = saved_errno;
1016               return -1;
1017             }
1018         }
1019
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
1023 # endif
1024          )
1025         cd2 = (iconv_t)(-1);
1026       else
1027         {
1028           cd2 = iconv_open (to_codeset, "UTF-8");
1029           if (cd2 == (iconv_t)(-1))
1030             {
1031               int saved_errno = errno;
1032               if (cd1 != (iconv_t)(-1))
1033                 iconv_close (cd1);
1034               if (cd != (iconv_t)(-1))
1035                 iconv_close (cd);
1036               errno = saved_errno;
1037               return -1;
1038             }
1039         }
1040
1041       result = *resultp;
1042       length = *lengthp;
1043       retval = mem_cd_iconveh (src, srclen, cd, cd1, cd2, handler, offsets,
1044                                &result, &length);
1045
1046       if (retval < 0)
1047         {
1048           /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv.  */
1049           int saved_errno = errno;
1050           if (cd2 != (iconv_t)(-1))
1051             iconv_close (cd2);
1052           if (cd1 != (iconv_t)(-1))
1053             iconv_close (cd1);
1054           if (cd != (iconv_t)(-1))
1055             iconv_close (cd);
1056           errno = saved_errno;
1057         }
1058       else
1059         {
1060           if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
1061             {
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))
1066                 iconv_close (cd1);
1067               if (cd != (iconv_t)(-1))
1068                 iconv_close (cd);
1069               if (result != *resultp && result != NULL)
1070                 free (result);
1071               errno = saved_errno;
1072               return -1;
1073             }
1074           if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
1075             {
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))
1080                 iconv_close (cd);
1081               if (result != *resultp && result != NULL)
1082                 free (result);
1083               errno = saved_errno;
1084               return -1;
1085             }
1086           if (cd != (iconv_t)(-1) && iconv_close (cd) < 0)
1087             {
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)
1092                 free (result);
1093               errno = saved_errno;
1094               return -1;
1095             }
1096           *resultp = result;
1097           *lengthp = length;
1098         }
1099       return retval;
1100 #else
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."  */
1106       errno = ENOSYS;
1107       return -1;
1108 #endif
1109     }
1110 }
1111
1112 char *
1113 str_iconveh (const char *src,
1114              const char *from_codeset, const char *to_codeset,
1115              enum iconv_ilseq_handler handler)
1116 {
1117   if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
1118     {
1119       char *result = strdup (src);
1120
1121       if (result == NULL)
1122         errno = ENOMEM;
1123       return result;
1124     }
1125   else
1126     {
1127 #if HAVE_ICONV
1128       iconv_t cd;
1129       iconv_t cd1;
1130       iconv_t cd2;
1131       char *result;
1132
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)
1137         {
1138           errno = EINVAL;
1139           return NULL;
1140         }
1141 # endif
1142
1143       cd = iconv_open (to_codeset, from_codeset);
1144
1145       if (STRCASEEQ (from_codeset, "UTF-8", 'U','T','F','-','8',0,0,0,0))
1146         cd1 = (iconv_t)(-1);
1147       else
1148         {
1149           cd1 = iconv_open ("UTF-8", from_codeset);
1150           if (cd1 == (iconv_t)(-1))
1151             {
1152               int saved_errno = errno;
1153               if (cd != (iconv_t)(-1))
1154                 iconv_close (cd);
1155               errno = saved_errno;
1156               return NULL;
1157             }
1158         }
1159
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
1163 # endif
1164          )
1165         cd2 = (iconv_t)(-1);
1166       else
1167         {
1168           cd2 = iconv_open (to_codeset, "UTF-8");
1169           if (cd2 == (iconv_t)(-1))
1170             {
1171               int saved_errno = errno;
1172               if (cd1 != (iconv_t)(-1))
1173                 iconv_close (cd1);
1174               if (cd != (iconv_t)(-1))
1175                 iconv_close (cd);
1176               errno = saved_errno;
1177               return NULL;
1178             }
1179         }
1180
1181       result = str_cd_iconveh (src, cd, cd1, cd2, handler);
1182
1183       if (result == NULL)
1184         {
1185           /* Close cd, cd1, cd2, but preserve the errno from str_cd_iconv.  */
1186           int saved_errno = errno;
1187           if (cd2 != (iconv_t)(-1))
1188             iconv_close (cd2);
1189           if (cd1 != (iconv_t)(-1))
1190             iconv_close (cd1);
1191           if (cd != (iconv_t)(-1))
1192             iconv_close (cd);
1193           errno = saved_errno;
1194         }
1195       else
1196         {
1197           if (cd2 != (iconv_t)(-1) && iconv_close (cd2) < 0)
1198             {
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))
1203                 iconv_close (cd1);
1204               if (cd != (iconv_t)(-1))
1205                 iconv_close (cd);
1206               free (result);
1207               errno = saved_errno;
1208               return NULL;
1209             }
1210           if (cd1 != (iconv_t)(-1) && iconv_close (cd1) < 0)
1211             {
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))
1216                 iconv_close (cd);
1217               free (result);
1218               errno = saved_errno;
1219               return NULL;
1220             }
1221           if (cd != (iconv_t)(-1) && iconv_close (cd) < 0)
1222             {
1223               /* Return NULL, but free the allocated memory, and while doing
1224                  that, preserve the errno from iconv_close.  */
1225               int saved_errno = errno;
1226               free (result);
1227               errno = saved_errno;
1228               return NULL;
1229             }
1230         }
1231       return result;
1232 #else
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."  */
1238       errno = ENOSYS;
1239       return NULL;
1240 #endif
1241     }
1242 }