1/* Convert UTF-8 string to UTF-16 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 u8_to_u16
24#define SRC_UNIT uint8_t
25#define DST_UNIT uint16_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 = u8_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      count = u16_uctomb (result + length, uc, allocated - length);
73      if (count == -1)
74        {
75          if (!(result == resultbuf || result == NULL))
76            free (result);
77          errno = EILSEQ;
78          return NULL;
79        }
80      if (count == -2)
81        {
82          DST_UNIT *memory;
83
84          allocated = (allocated > 0 ? 2 * allocated : 12);
85          if (length + 2 > allocated)
86            allocated = length + 2;
87          if (result == resultbuf || result == NULL)
88            memory = (DST_UNIT *) malloc (allocated * sizeof (DST_UNIT));
89          else
90            memory =
91              (DST_UNIT *) realloc (result, allocated * sizeof (DST_UNIT));
92
93          if (memory == NULL)
94            {
95              if (!(result == resultbuf || result == NULL))
96                free (result);
97              errno = ENOMEM;
98              return NULL;
99            }
100          if (result == resultbuf && length > 0)
101            memcpy ((char *) memory, (char *) result,
102                    length * sizeof (DST_UNIT));
103          result = memory;
104          count = u16_uctomb (result + length, uc, allocated - length);
105          if (count < 0)
106            abort ();
107        }
108      length += count;
109    }
110
111  if (length == 0)
112    {
113      if (result == NULL)
114        {
115          /* Return a non-NULL value.  NULL means error.  */
116          result = (DST_UNIT *) malloc (1);
117          if (result == NULL)
118            {
119              errno = ENOMEM;
120              return NULL;
121            }
122        }
123    }
124  else if (result != resultbuf && length < allocated)
125    {
126      /* Shrink the allocated memory if possible.  */
127      DST_UNIT *memory;
128
129      memory = (DST_UNIT *) realloc (result, length * sizeof (DST_UNIT));
130      if (memory != NULL)
131        result = memory;
132    }
133
134  *lengthp = length;
135  return result;
136}
137