(root)/
gettext-0.22.4/
libtextstyle/
gnulib-local/
lib/
iconv-ostream.oo.c
       1  /* Output stream that converts the output to another encoding.
       2     Copyright (C) 2006-2007, 2010, 2019-2020 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2006.
       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 <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "iconv-ostream.h"
      22  
      23  #if HAVE_ICONV
      24  
      25  #include <errno.h>
      26  #include <iconv.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  
      30  #include "c-strcase.h"
      31  #include "error.h"
      32  #include "xalloc.h"
      33  #include "gettext.h"
      34  
      35  #define _(str) gettext (str)
      36  
      37  #endif /* HAVE_ICONV */
      38  
      39  struct iconv_ostream : struct ostream
      40  {
      41  fields:
      42  #if HAVE_ICONV
      43    /* The destination stream.  */
      44    ostream_t destination;
      45    /* The from and to encodings.  */
      46    char *from_encoding;
      47    char *to_encoding;
      48    /* The converter.  */
      49    iconv_t cd;
      50    /* Last few bytes that could not yet be converted.  */
      51    #define BUFSIZE 64
      52    char buf[BUFSIZE];
      53    size_t buflen;
      54  #endif /* HAVE_ICONV */
      55  };
      56  
      57  #if HAVE_ICONV
      58  
      59  /* Implementation of ostream_t methods.  */
      60  
      61  static void
      62  iconv_ostream::write_mem (iconv_ostream_t stream, const void *data, size_t len)
      63  {
      64    if (len > 0)
      65      {
      66        #define BUFFERSIZE 256
      67        char inbuffer[BUFFERSIZE];
      68        size_t inbufcount;
      69  
      70        inbufcount = stream->buflen;
      71        if (inbufcount > 0)
      72          memcpy (inbuffer, stream->buf, inbufcount);
      73        for (;;)
      74          {
      75            /* At this point, inbuffer[0..inbufcount-1] is filled.  */
      76            {
      77              /* Combine the previous rest with a chunk of new input.  */
      78              size_t n =
      79                (len <= BUFFERSIZE - inbufcount ? len : BUFFERSIZE - inbufcount);
      80  
      81              if (n > 0)
      82                {
      83                  memcpy (inbuffer + inbufcount, data, n);
      84                  data = (const char *) data + n;
      85                  inbufcount += n;
      86                  len -= n;
      87                }
      88            }
      89            {
      90              /* Attempt to convert the combined input.  */
      91              char outbuffer[8*BUFFERSIZE];
      92  
      93              const char *inptr = inbuffer;
      94              size_t insize = inbufcount;
      95              char *outptr = outbuffer;
      96              size_t outsize = sizeof (outbuffer);
      97  
      98              size_t res = iconv (stream->cd,
      99                                  (ICONV_CONST char **) &inptr, &insize,
     100                                  &outptr, &outsize);
     101              #if !defined _LIBICONV_VERSION \
     102                  && !(defined __GLIBC__ && !defined __UCLIBC__)
     103              /* Irix iconv() inserts a NUL byte if it cannot convert.
     104                 NetBSD iconv() inserts a question mark if it cannot convert.
     105                 Only GNU libiconv and GNU libc are known to prefer to fail rather
     106                 than doing a lossy conversion.  */
     107              if (res > 0)
     108                {
     109                  errno = EILSEQ;
     110                  res = -1;
     111                }
     112              #endif
     113              if (res == (size_t)(-1) && errno != EINVAL)
     114                error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
     115                       "iconv_ostream",
     116                       stream->from_encoding, stream->to_encoding);
     117              /* Output the converted part.  */
     118              if (sizeof (outbuffer) - outsize > 0)
     119                ostream_write_mem (stream->destination,
     120                                   outbuffer, sizeof (outbuffer) - outsize);
     121              /* Put back the unconverted part.  */
     122              if (insize > BUFSIZE)
     123                error (EXIT_FAILURE, 0, _("%s: shift sequence too long"),
     124                       "iconv_ostream");
     125              if (len == 0)
     126                {
     127                  if (insize > 0)
     128                    memcpy (stream->buf, inptr, insize);
     129                  stream->buflen = insize;
     130                  break;
     131                }
     132              if (insize > 0)
     133                memmove (inbuffer, inptr, insize);
     134              inbufcount = insize;
     135            }
     136          }
     137        #undef BUFFERSIZE
     138      }
     139  }
     140  
     141  static void
     142  iconv_ostream::flush (iconv_ostream_t stream, ostream_flush_scope_t scope)
     143  {
     144    /* For scope == FLUSH_THIS_STREAM, there's nothing we can do here, since
     145       stream->buf[] contains only a few bytes that don't correspond to a
     146       character.  */
     147    if (scope != FLUSH_THIS_STREAM)
     148      ostream_flush (stream->destination, scope);
     149  }
     150  
     151  static void
     152  iconv_ostream::free (iconv_ostream_t stream)
     153  {
     154    /* Silently ignore the few bytes in stream->buf[] that don't correspond to a
     155       character.  */
     156  
     157    /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
     158    #if defined _LIBICONV_VERSION \
     159        || !(((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) \
     160              && !defined __UCLIBC__) \
     161             || defined __sun)
     162    {
     163      char outbuffer[2048];
     164      char *outptr = outbuffer;
     165      size_t outsize = sizeof (outbuffer);
     166      size_t res = iconv (stream->cd, NULL, NULL, &outptr, &outsize);
     167      if (res == (size_t)(-1))
     168        error (EXIT_FAILURE, 0, _("%s: cannot convert from %s to %s"),
     169               "iconv_ostream", stream->from_encoding, stream->to_encoding);
     170      /* Output the converted part.  */
     171      if (sizeof (outbuffer) - outsize > 0)
     172        ostream_write_mem (stream->destination,
     173                           outbuffer, sizeof (outbuffer) - outsize);
     174    }
     175    #endif
     176  
     177    iconv_close (stream->cd);
     178    free (stream->from_encoding);
     179    free (stream->to_encoding);
     180    free (stream);
     181  }
     182  
     183  /* Constructor.  */
     184  
     185  iconv_ostream_t
     186  iconv_ostream_create (const char *from_encoding, const char *to_encoding,
     187                        ostream_t destination)
     188  {
     189    iconv_ostream_t stream = XMALLOC (struct iconv_ostream_representation);
     190  
     191    stream->base.vtable = &iconv_ostream_vtable;
     192    stream->destination = destination;
     193    stream->from_encoding = xstrdup (from_encoding);
     194    stream->to_encoding = xstrdup (to_encoding);
     195  
     196    /* Avoid glibc-2.1 bug with EUC-KR.  */
     197    #if ((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) \
     198         && !defined __UCLIBC__) \
     199        && !defined _LIBICONV_VERSION
     200    if (c_strcasecmp (from_encoding, "EUC-KR") == 0
     201        || c_strcasecmp (to_encoding, "EUC-KR") == 0)
     202      stream->cd = (iconv_t)(-1):
     203    else
     204    #endif
     205      stream->cd = iconv_open (to_encoding, from_encoding);
     206    if (stream->cd == (iconv_t)(-1))
     207      {
     208        if (iconv_open ("UTF-8", from_encoding) == (iconv_t)(-1))
     209          error (EXIT_FAILURE, 0, _("%s does not support conversion from %s"),
     210                 "iconv", from_encoding);
     211        else if (iconv_open (to_encoding, "UTF-8") == (iconv_t)(-1))
     212          error (EXIT_FAILURE, 0, _("%s does not support conversion to %s"),
     213                 "iconv", to_encoding);
     214        else
     215          error (EXIT_FAILURE, 0,
     216                 _("%s does not support conversion from %s to %s"),
     217                 "iconv", from_encoding, to_encoding);
     218      }
     219  
     220    stream->buflen = 0;
     221  
     222    return stream;
     223  }
     224  
     225  /* Accessors.  */
     226  
     227  static const char *
     228  iconv_ostream::get_from_encoding (iconv_ostream_t stream)
     229  {
     230    return stream->from_encoding;
     231  }
     232  
     233  static const char *
     234  iconv_ostream::get_to_encoding (iconv_ostream_t stream)
     235  {
     236    return stream->to_encoding;
     237  }
     238  
     239  static ostream_t
     240  iconv_ostream::get_destination (iconv_ostream_t stream)
     241  {
     242    return stream->destination;
     243  }
     244  
     245  /* Instanceof test.  */
     246  
     247  bool
     248  is_instance_of_iconv_ostream (ostream_t stream)
     249  {
     250    return IS_INSTANCE (stream, ostream, iconv_ostream);
     251  }
     252  
     253  #else
     254  
     255  static void
     256  iconv_ostream::write_mem (iconv_ostream_t stream, const void *data, size_t len)
     257  {
     258    abort ();
     259  }
     260  
     261  static void
     262  iconv_ostream::flush (iconv_ostream_t stream)
     263  {
     264    abort ();
     265  }
     266  
     267  static void
     268  iconv_ostream::free (iconv_ostream_t stream)
     269  {
     270    abort ();
     271  }
     272  
     273  /* Accessors.  */
     274  
     275  static const char *
     276  iconv_ostream::get_from_encoding (iconv_ostream_t stream)
     277  {
     278    abort ();
     279  }
     280  
     281  static const char *
     282  iconv_ostream::get_to_encoding (iconv_ostream_t stream)
     283  {
     284    abort ();
     285  }
     286  
     287  static ostream_t
     288  iconv_ostream::get_destination (iconv_ostream_t stream)
     289  {
     290    abort ();
     291  }
     292  
     293  /* Instanceof test.  */
     294  
     295  bool
     296  is_instance_of_iconv_ostream (ostream_t stream)
     297  {
     298    return false;
     299  }
     300  
     301  #endif /* HAVE_ICONV */