Blame lib/unistring/unistr/u32-to-u8.c

Packit 549fdc
/* Convert UTF-32 string to UTF-8 string.
Packit 549fdc
   Copyright (C) 2002, 2006-2007, 2009-2016 Free Software Foundation, Inc.
Packit 549fdc
   Written by Bruno Haible <bruno@clisp.org>, 2002.
Packit 549fdc
Packit 549fdc
   This program is free software: you can redistribute it and/or modify it
Packit 549fdc
   under the terms of either:
Packit 549fdc
Packit 549fdc
    * the GNU Lesser General Public License as published
Packit 549fdc
   by the Free Software Foundation; either version 3 of the License, or
Packit 549fdc
   (at your option) any later version.
Packit 549fdc
Packit 549fdc
   or
Packit 549fdc
Packit 549fdc
   * the GNU General Public License as published by the Free
Packit 549fdc
   Software Foundation; either version 2 of the License, or
Packit 549fdc
   (at your option) any later version.
Packit 549fdc
Packit 549fdc
   or both in parallel, as here.
Packit 549fdc
Packit 549fdc
   This program is distributed in the hope that it will be useful,
Packit 549fdc
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
   Lesser General Public License for more details.
Packit 549fdc
Packit 549fdc
   You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 549fdc
Packit 549fdc
#include <config.h>
Packit 549fdc
Packit 549fdc
/* Specification.  */
Packit 549fdc
#include "unistr.h"
Packit 549fdc
Packit 549fdc
#define FUNC u32_to_u8
Packit 549fdc
#define SRC_UNIT uint32_t
Packit 549fdc
#define DST_UNIT uint8_t
Packit 549fdc
Packit 549fdc
#include <errno.h>
Packit 549fdc
#include <stdlib.h>
Packit 549fdc
#include <string.h>
Packit 549fdc
Packit 549fdc
DST_UNIT *
Packit 549fdc
FUNC (const SRC_UNIT *s, size_t n, DST_UNIT *resultbuf, size_t *lengthp)
Packit 549fdc
{
Packit 549fdc
  const SRC_UNIT *s_end = s + n;
Packit 549fdc
  /* Output string accumulator.  */
Packit 549fdc
  DST_UNIT *result;
Packit 549fdc
  size_t allocated;
Packit 549fdc
  size_t length;
Packit 549fdc
Packit 549fdc
  if (resultbuf != NULL)
Packit 549fdc
    {
Packit 549fdc
      result = resultbuf;
Packit 549fdc
      allocated = *lengthp;
Packit 549fdc
    }
Packit 549fdc
  else
Packit 549fdc
    {
Packit 549fdc
      result = NULL;
Packit 549fdc
      allocated = 0;
Packit 549fdc
    }
Packit 549fdc
  length = 0;
Packit 549fdc
  /* Invariants:
Packit 549fdc
     result is either == resultbuf or == NULL or malloc-allocated.
Packit 549fdc
     If length > 0, then result != NULL.  */
Packit 549fdc
Packit 549fdc
  while (s < s_end)
Packit 549fdc
    {
Packit 549fdc
      ucs4_t uc;
Packit 549fdc
      int count;
Packit 549fdc
Packit 549fdc
      /* Fetch a Unicode character from the input string.  */
Packit 549fdc
      uc = *s++;
Packit 549fdc
      /* No need to call the safe variant u32_mbtouc, because
Packit 549fdc
         u8_uctomb will verify uc anyway.  */
Packit 549fdc
Packit 549fdc
      /* Store it in the output string.  */
Packit 549fdc
      count = u8_uctomb (result + length, uc, allocated - length);
Packit 549fdc
      if (count == -1)
Packit 549fdc
        {
Packit 549fdc
          if (!(result == resultbuf || result == NULL))
Packit 549fdc
            free (result);
Packit 549fdc
          errno = EILSEQ;
Packit 549fdc
          return NULL;
Packit 549fdc
        }
Packit 549fdc
      if (count == -2)
Packit 549fdc
        {
Packit 549fdc
          DST_UNIT *memory;
Packit 549fdc
Packit 549fdc
          allocated = (allocated > 0 ? 2 * allocated : 12);
Packit 549fdc
          if (length + 6 > allocated)
Packit 549fdc
            allocated = length + 6;
Packit 549fdc
          if (result == resultbuf || result == NULL)
Packit 549fdc
            memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
Packit 549fdc
          else
Packit 549fdc
            memory =
Packit 549fdc
              (DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
Packit 549fdc
Packit 549fdc
          if (memory == NULL)
Packit 549fdc
            {
Packit 549fdc
              if (!(result == resultbuf || result == NULL))
Packit 549fdc
                free (result);
Packit 549fdc
              errno = ENOMEM;
Packit 549fdc
              return NULL;
Packit 549fdc
            }
Packit 549fdc
          if (result == resultbuf && length > 0)
Packit 549fdc
            memcpy ((char *) memory, (char *) result,
Packit 549fdc
                    length * sizeof (DST_UNIT));
Packit 549fdc
          result = memory;
Packit 549fdc
          count = u8_uctomb (result + length, uc, allocated - length);
Packit 549fdc
          if (count < 0)
Packit 549fdc
            abort ();
Packit 549fdc
        }
Packit 549fdc
      length += count;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  if (length == 0)
Packit 549fdc
    {
Packit 549fdc
      if (result == NULL)
Packit 549fdc
        {
Packit 549fdc
          /* Return a non-NULL value.  NULL means error.  */
Packit 549fdc
          result = (DST_UNIT *) malloc (1);
Packit 549fdc
          if (result == NULL)
Packit 549fdc
            {
Packit 549fdc
              errno = ENOMEM;
Packit 549fdc
              return NULL;
Packit 549fdc
            }
Packit 549fdc
        }
Packit 549fdc
    }
Packit 549fdc
  else if (result != resultbuf && length < allocated)
Packit 549fdc
    {
Packit 549fdc
      /* Shrink the allocated memory if possible.  */
Packit 549fdc
      DST_UNIT *memory;
Packit 549fdc
Packit 549fdc
      memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
Packit 549fdc
      if (memory != NULL)
Packit 549fdc
        result = memory;
Packit 549fdc
    }
Packit 549fdc
Packit 549fdc
  *lengthp = length;
Packit 549fdc
  return result;
Packit 549fdc
}