1/* Convert UTF-16 string to UTF-32 string.
2   Copyright (C) 2002, 2006-2007, 2009-2010 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2002.
4
5   This program is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Lesser General Public License as published
7   by 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 GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser 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 "unistr.h"
22
23#define FUNC u16_to_u32
24#define SRC_UNIT uint16_t
25#define DST_UNIT uint32_t
26
27#include <errno.h>
28#include <stdlib.h>
29#include <string.h>
30
31DST_UNIT *
32FUNC (const SRC_UNIT *s, size_t n, DST_UNIT *resultbuf, size_t *lengthp)
33{
34  const SRC_UNIT *s_end = s + n;
35  /* Output string accumulator.  */
36  DST_UNIT *result;
37  size_t allocated;
38  size_t length;
39
40  if (resultbuf != NULL)
41    {
42      result = resultbuf;
43      allocated = *lengthp;
44    }
45  else
46    {
47      result = NULL;
48      allocated = 0;
49    }
50  length = 0;
51  /* Invariants:
52     result is either == resultbuf or == NULL or malloc-allocated.
53     If length > 0, then result != NULL.  */
54
55  while (s < s_end)
56    {
57      ucs4_t uc;
58      int count;
59
60      /* Fetch a Unicode character from the input string.  */
61      count = u16_mbtoucr (&uc, s, s_end - s);
62      if (count < 0)
63        {
64          if (!(result == resultbuf || result == NULL))
65            free (result);
66          errno = EILSEQ;
67          return NULL;
68        }
69      s += count;
70
71      /* Store it in the output string.  */
72      if (length + 1 > allocated)
73        {
74          DST_UNIT *memory;
75
76          allocated = (allocated > 0 ? 2 * allocated : 12);
77          if (length + 1 > allocated)
78            allocated = length + 1;
79          if (result == resultbuf || result == NULL)
80            memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
81          else
82            memory =
83              (DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
84
85          if (memory == NULL)
86            {
87              if (!(result == resultbuf || result == NULL))
88                free (result);
89              errno = ENOMEM;
90              return NULL;
91            }
92          if (result == resultbuf && length > 0)
93            memcpy ((char *) memory, (char *) result,
94                    length * sizeof (DST_UNIT));
95          result = memory;
96        }
97      result[length++] = uc;
98    }
99
100  if (length == 0)
101    {
102      if (result == NULL)
103        {
104          /* Return a non-NULL value.  NULL means error.  */
105          result = (DST_UNIT *) malloc (1);
106          if (result == NULL)
107            {
108              errno = ENOMEM;
109              return NULL;
110            }
111        }
112    }
113  else if (result != resultbuf && length < allocated)
114    {
115      /* Shrink the allocated memory if possible.  */
116      DST_UNIT *memory;
117
118      memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
119      if (memory != NULL)
120        result = memory;
121    }
122
123  *lengthp = length;
124  return result;
125}
126