Blame lib/iconv.c

Packit 33f14e
/* Character set conversion.
Packit 33f14e
   Copyright (C) 1999-2001, 2007, 2009-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software; you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3, or (at your option)
Packit 33f14e
   any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License along
Packit 33f14e
   with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
#include <config.h>
Packit 33f14e
Packit 33f14e
/* Specification.  */
Packit 33f14e
#include <iconv.h>
Packit 33f14e
Packit 33f14e
#include <stddef.h>
Packit 33f14e
Packit 33f14e
#if REPLACE_ICONV_UTF
Packit 33f14e
# include <errno.h>
Packit 33f14e
# include <stdint.h>
Packit 33f14e
# include <stdlib.h>
Packit 33f14e
# include "unistr.h"
Packit 33f14e
# ifndef uintptr_t
Packit 33f14e
#  define uintptr_t unsigned long
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#if REPLACE_ICONV_UTF
Packit 33f14e
Packit 33f14e
/* UTF-{16,32}{BE,LE} converters taken from GNU libiconv 1.11.  */
Packit 33f14e
Packit 33f14e
/* Return code if invalid. (xxx_mbtowc) */
Packit 33f14e
# define RET_ILSEQ      -1
Packit 33f14e
/* Return code if no bytes were read. (xxx_mbtowc) */
Packit 33f14e
# define RET_TOOFEW     -2
Packit 33f14e
Packit 33f14e
/* Return code if invalid. (xxx_wctomb) */
Packit 33f14e
# define RET_ILUNI      -1
Packit 33f14e
/* Return code if output buffer is too small. (xxx_wctomb, xxx_reset) */
Packit 33f14e
# define RET_TOOSMALL   -2
Packit 33f14e
Packit 33f14e
/*
Packit 33f14e
 * UTF-16BE
Packit 33f14e
 */
Packit 33f14e
Packit 33f14e
/* Specification: RFC 2781 */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf16be_mbtowc (ucs4_t *pwc, const unsigned char *s, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (n >= 2)
Packit 33f14e
    {
Packit 33f14e
      ucs4_t wc = (s[0] << 8) + s[1];
Packit 33f14e
      if (wc >= 0xd800 && wc < 0xdc00)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 4)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t wc2 = (s[2] << 8) + s[3];
Packit 33f14e
              if (!(wc2 >= 0xdc00 && wc2 < 0xe000))
Packit 33f14e
                return RET_ILSEQ;
Packit 33f14e
              *pwc = 0x10000 + ((wc - 0xd800) << 10) + (wc2 - 0xdc00);
Packit 33f14e
              return 4;
Packit 33f14e
            }
Packit 33f14e
        }
Packit 33f14e
      else if (wc >= 0xdc00 && wc < 0xe000)
Packit 33f14e
        {
Packit 33f14e
          return RET_ILSEQ;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        {
Packit 33f14e
          *pwc = wc;
Packit 33f14e
          return 2;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
  return RET_TOOFEW;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf16be_wctomb (unsigned char *r, ucs4_t wc, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (!(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
    {
Packit 33f14e
      if (wc < 0x10000)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 2)
Packit 33f14e
            {
Packit 33f14e
              r[0] = (unsigned char) (wc >> 8);
Packit 33f14e
              r[1] = (unsigned char) wc;
Packit 33f14e
              return 2;
Packit 33f14e
            }
Packit 33f14e
          else
Packit 33f14e
            return RET_TOOSMALL;
Packit 33f14e
        }
Packit 33f14e
      else if (wc < 0x110000)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 4)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t wc1 = 0xd800 + ((wc - 0x10000) >> 10);
Packit 33f14e
              ucs4_t wc2 = 0xdc00 + ((wc - 0x10000) & 0x3ff);
Packit 33f14e
              r[0] = (unsigned char) (wc1 >> 8);
Packit 33f14e
              r[1] = (unsigned char) wc1;
Packit 33f14e
              r[2] = (unsigned char) (wc2 >> 8);
Packit 33f14e
              r[3] = (unsigned char) wc2;
Packit 33f14e
              return 4;
Packit 33f14e
            }
Packit 33f14e
          else
Packit 33f14e
            return RET_TOOSMALL;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
  return RET_ILUNI;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/*
Packit 33f14e
 * UTF-16LE
Packit 33f14e
 */
Packit 33f14e
Packit 33f14e
/* Specification: RFC 2781 */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf16le_mbtowc (ucs4_t *pwc, const unsigned char *s, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (n >= 2)
Packit 33f14e
    {
Packit 33f14e
      ucs4_t wc = s[0] + (s[1] << 8);
Packit 33f14e
      if (wc >= 0xd800 && wc < 0xdc00)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 4)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t wc2 = s[2] + (s[3] << 8);
Packit 33f14e
              if (!(wc2 >= 0xdc00 && wc2 < 0xe000))
Packit 33f14e
                return RET_ILSEQ;
Packit 33f14e
              *pwc = 0x10000 + ((wc - 0xd800) << 10) + (wc2 - 0xdc00);
Packit 33f14e
              return 4;
Packit 33f14e
            }
Packit 33f14e
        }
Packit 33f14e
      else if (wc >= 0xdc00 && wc < 0xe000)
Packit 33f14e
        {
Packit 33f14e
          return RET_ILSEQ;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        {
Packit 33f14e
          *pwc = wc;
Packit 33f14e
          return 2;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
  return RET_TOOFEW;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf16le_wctomb (unsigned char *r, ucs4_t wc, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (!(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
    {
Packit 33f14e
      if (wc < 0x10000)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 2)
Packit 33f14e
            {
Packit 33f14e
              r[0] = (unsigned char) wc;
Packit 33f14e
              r[1] = (unsigned char) (wc >> 8);
Packit 33f14e
              return 2;
Packit 33f14e
            }
Packit 33f14e
          else
Packit 33f14e
            return RET_TOOSMALL;
Packit 33f14e
        }
Packit 33f14e
      else if (wc < 0x110000)
Packit 33f14e
        {
Packit 33f14e
          if (n >= 4)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t wc1 = 0xd800 + ((wc - 0x10000) >> 10);
Packit 33f14e
              ucs4_t wc2 = 0xdc00 + ((wc - 0x10000) & 0x3ff);
Packit 33f14e
              r[0] = (unsigned char) wc1;
Packit 33f14e
              r[1] = (unsigned char) (wc1 >> 8);
Packit 33f14e
              r[2] = (unsigned char) wc2;
Packit 33f14e
              r[3] = (unsigned char) (wc2 >> 8);
Packit 33f14e
              return 4;
Packit 33f14e
            }
Packit 33f14e
          else
Packit 33f14e
            return RET_TOOSMALL;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
  return RET_ILUNI;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/*
Packit 33f14e
 * UTF-32BE
Packit 33f14e
 */
Packit 33f14e
Packit 33f14e
/* Specification: Unicode 3.1 Standard Annex #19 */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf32be_mbtowc (ucs4_t *pwc, const unsigned char *s, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (n >= 4)
Packit 33f14e
    {
Packit 33f14e
      ucs4_t wc = (s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3];
Packit 33f14e
      if (wc < 0x110000 && !(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
        {
Packit 33f14e
          *pwc = wc;
Packit 33f14e
          return 4;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        return RET_ILSEQ;
Packit 33f14e
    }
Packit 33f14e
  return RET_TOOFEW;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf32be_wctomb (unsigned char *r, ucs4_t wc, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (wc < 0x110000 && !(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
    {
Packit 33f14e
      if (n >= 4)
Packit 33f14e
        {
Packit 33f14e
          r[0] = 0;
Packit 33f14e
          r[1] = (unsigned char) (wc >> 16);
Packit 33f14e
          r[2] = (unsigned char) (wc >> 8);
Packit 33f14e
          r[3] = (unsigned char) wc;
Packit 33f14e
          return 4;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        return RET_TOOSMALL;
Packit 33f14e
    }
Packit 33f14e
  return RET_ILUNI;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/*
Packit 33f14e
 * UTF-32LE
Packit 33f14e
 */
Packit 33f14e
Packit 33f14e
/* Specification: Unicode 3.1 Standard Annex #19 */
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf32le_mbtowc (ucs4_t *pwc, const unsigned char *s, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (n >= 4)
Packit 33f14e
    {
Packit 33f14e
      ucs4_t wc = s[0] + (s[1] << 8) + (s[2] << 16) + (s[3] << 24);
Packit 33f14e
      if (wc < 0x110000 && !(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
        {
Packit 33f14e
          *pwc = wc;
Packit 33f14e
          return 4;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        return RET_ILSEQ;
Packit 33f14e
    }
Packit 33f14e
  return RET_TOOFEW;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
utf32le_wctomb (unsigned char *r, ucs4_t wc, size_t n)
Packit 33f14e
{
Packit 33f14e
  if (wc < 0x110000 && !(wc >= 0xd800 && wc < 0xe000))
Packit 33f14e
    {
Packit 33f14e
      if (n >= 4)
Packit 33f14e
        {
Packit 33f14e
          r[0] = (unsigned char) wc;
Packit 33f14e
          r[1] = (unsigned char) (wc >> 8);
Packit 33f14e
          r[2] = (unsigned char) (wc >> 16);
Packit 33f14e
          r[3] = 0;
Packit 33f14e
          return 4;
Packit 33f14e
        }
Packit 33f14e
      else
Packit 33f14e
        return RET_TOOSMALL;
Packit 33f14e
    }
Packit 33f14e
  return RET_ILUNI;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
size_t
Packit 33f14e
rpl_iconv (iconv_t cd,
Packit 33f14e
           ICONV_CONST char **inbuf, size_t *inbytesleft,
Packit 33f14e
           char **outbuf, size_t *outbytesleft)
Packit 33f14e
#undef iconv
Packit 33f14e
{
Packit 33f14e
#if REPLACE_ICONV_UTF
Packit 33f14e
  switch ((uintptr_t) cd)
Packit 33f14e
    {
Packit 33f14e
      {
Packit 33f14e
        int (*xxx_wctomb) (unsigned char *, ucs4_t, size_t);
Packit 33f14e
Packit 33f14e
        case (uintptr_t) _ICONV_UTF8_UTF16BE:
Packit 33f14e
          xxx_wctomb = utf16be_wctomb;
Packit 33f14e
          goto loop_from_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF8_UTF16LE:
Packit 33f14e
          xxx_wctomb = utf16le_wctomb;
Packit 33f14e
          goto loop_from_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF8_UTF32BE:
Packit 33f14e
          xxx_wctomb = utf32be_wctomb;
Packit 33f14e
          goto loop_from_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF8_UTF32LE:
Packit 33f14e
          xxx_wctomb = utf32le_wctomb;
Packit 33f14e
          goto loop_from_utf8;
Packit 33f14e
Packit 33f14e
       loop_from_utf8:
Packit 33f14e
        if (inbuf == NULL || *inbuf == NULL)
Packit 33f14e
          return 0;
Packit 33f14e
        {
Packit 33f14e
          ICONV_CONST char *inptr = *inbuf;
Packit 33f14e
          size_t inleft = *inbytesleft;
Packit 33f14e
          char *outptr = *outbuf;
Packit 33f14e
          size_t outleft = *outbytesleft;
Packit 33f14e
          size_t res = 0;
Packit 33f14e
          while (inleft > 0)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t uc;
Packit 33f14e
              int m = u8_mbtoucr (&uc, (const uint8_t *) inptr, inleft);
Packit 33f14e
              if (m <= 0)
Packit 33f14e
                {
Packit 33f14e
                  if (m == -1)
Packit 33f14e
                    {
Packit 33f14e
                      errno = EILSEQ;
Packit 33f14e
                      res = (size_t)(-1);
Packit 33f14e
                      break;
Packit 33f14e
                    }
Packit 33f14e
                  if (m == -2)
Packit 33f14e
                    {
Packit 33f14e
                      errno = EINVAL;
Packit 33f14e
                      res = (size_t)(-1);
Packit 33f14e
                      break;
Packit 33f14e
                    }
Packit 33f14e
                  abort ();
Packit 33f14e
                }
Packit 33f14e
              else
Packit 33f14e
                {
Packit 33f14e
                  int n = xxx_wctomb ((uint8_t *) outptr, uc, outleft);
Packit 33f14e
                  if (n < 0)
Packit 33f14e
                    {
Packit 33f14e
                      if (n == RET_ILUNI)
Packit 33f14e
                        {
Packit 33f14e
                          errno = EILSEQ;
Packit 33f14e
                          res = (size_t)(-1);
Packit 33f14e
                          break;
Packit 33f14e
                        }
Packit 33f14e
                      if (n == RET_TOOSMALL)
Packit 33f14e
                        {
Packit 33f14e
                          errno = E2BIG;
Packit 33f14e
                          res = (size_t)(-1);
Packit 33f14e
                          break;
Packit 33f14e
                        }
Packit 33f14e
                      abort ();
Packit 33f14e
                    }
Packit 33f14e
                  else
Packit 33f14e
                    {
Packit 33f14e
                      inptr += m;
Packit 33f14e
                      inleft -= m;
Packit 33f14e
                      outptr += n;
Packit 33f14e
                      outleft -= n;
Packit 33f14e
                    }
Packit 33f14e
                }
Packit 33f14e
            }
Packit 33f14e
          *inbuf = inptr;
Packit 33f14e
          *inbytesleft = inleft;
Packit 33f14e
          *outbuf = outptr;
Packit 33f14e
          *outbytesleft = outleft;
Packit 33f14e
          return res;
Packit 33f14e
        }
Packit 33f14e
      }
Packit 33f14e
Packit 33f14e
      {
Packit 33f14e
        int (*xxx_mbtowc) (ucs4_t *, const unsigned char *, size_t);
Packit 33f14e
Packit 33f14e
        case (uintptr_t) _ICONV_UTF16BE_UTF8:
Packit 33f14e
          xxx_mbtowc = utf16be_mbtowc;
Packit 33f14e
          goto loop_to_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF16LE_UTF8:
Packit 33f14e
          xxx_mbtowc = utf16le_mbtowc;
Packit 33f14e
          goto loop_to_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF32BE_UTF8:
Packit 33f14e
          xxx_mbtowc = utf32be_mbtowc;
Packit 33f14e
          goto loop_to_utf8;
Packit 33f14e
        case (uintptr_t) _ICONV_UTF32LE_UTF8:
Packit 33f14e
          xxx_mbtowc = utf32le_mbtowc;
Packit 33f14e
          goto loop_to_utf8;
Packit 33f14e
Packit 33f14e
       loop_to_utf8:
Packit 33f14e
        if (inbuf == NULL || *inbuf == NULL)
Packit 33f14e
          return 0;
Packit 33f14e
        {
Packit 33f14e
          ICONV_CONST char *inptr = *inbuf;
Packit 33f14e
          size_t inleft = *inbytesleft;
Packit 33f14e
          char *outptr = *outbuf;
Packit 33f14e
          size_t outleft = *outbytesleft;
Packit 33f14e
          size_t res = 0;
Packit 33f14e
          while (inleft > 0)
Packit 33f14e
            {
Packit 33f14e
              ucs4_t uc;
Packit 33f14e
              int m = xxx_mbtowc (&uc, (const uint8_t *) inptr, inleft);
Packit 33f14e
              if (m <= 0)
Packit 33f14e
                {
Packit 33f14e
                  if (m == RET_ILSEQ)
Packit 33f14e
                    {
Packit 33f14e
                      errno = EILSEQ;
Packit 33f14e
                      res = (size_t)(-1);
Packit 33f14e
                      break;
Packit 33f14e
                    }
Packit 33f14e
                  if (m == RET_TOOFEW)
Packit 33f14e
                    {
Packit 33f14e
                      errno = EINVAL;
Packit 33f14e
                      res = (size_t)(-1);
Packit 33f14e
                      break;
Packit 33f14e
                    }
Packit 33f14e
                  abort ();
Packit 33f14e
                }
Packit 33f14e
              else
Packit 33f14e
                {
Packit 33f14e
                  int n = u8_uctomb ((uint8_t *) outptr, uc, outleft);
Packit 33f14e
                  if (n < 0)
Packit 33f14e
                    {
Packit 33f14e
                      if (n == -1)
Packit 33f14e
                        {
Packit 33f14e
                          errno = EILSEQ;
Packit 33f14e
                          res = (size_t)(-1);
Packit 33f14e
                          break;
Packit 33f14e
                        }
Packit 33f14e
                      if (n == -2)
Packit 33f14e
                        {
Packit 33f14e
                          errno = E2BIG;
Packit 33f14e
                          res = (size_t)(-1);
Packit 33f14e
                          break;
Packit 33f14e
                        }
Packit 33f14e
                      abort ();
Packit 33f14e
                    }
Packit 33f14e
                  else
Packit 33f14e
                    {
Packit 33f14e
                      inptr += m;
Packit 33f14e
                      inleft -= m;
Packit 33f14e
                      outptr += n;
Packit 33f14e
                      outleft -= n;
Packit 33f14e
                    }
Packit 33f14e
                }
Packit 33f14e
            }
Packit 33f14e
          *inbuf = inptr;
Packit 33f14e
          *inbytesleft = inleft;
Packit 33f14e
          *outbuf = outptr;
Packit 33f14e
          *outbytesleft = outleft;
Packit 33f14e
          return res;
Packit 33f14e
        }
Packit 33f14e
      }
Packit 33f14e
    }
Packit 33f14e
#endif
Packit 33f14e
  return iconv (cd, inbuf, inbytesleft, outbuf, outbytesleft);
Packit 33f14e
}