1/*	$NetBSD: efichar.c,v 1.1 2018/08/24 02:01:06 jmcneill Exp $	*/
2
3/*-
4 * Copyright (c) 2010 Marcel Moolenaar
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30#if 0
31__FBSDID("$FreeBSD: head/stand/efi/libefi/efichar.c 328061 2018-01-16 20:35:54Z tsoome $");
32#endif
33
34#include "efiboot.h"
35
36size_t
37ucs2len(const CHAR16 *str)
38{
39	size_t i;
40
41	i = 0;
42	while (*str++)
43		i++;
44	return i;
45}
46
47/*
48 * If nm were converted to utf8, what what would strlen
49 * return on the resulting string?
50 */
51static size_t
52utf8_len_of_ucs2(const CHAR16 *nm)
53{
54	size_t len;
55	CHAR16 c;
56
57	len = 0;
58	while (*nm) {
59		c = *nm++;
60		if (c > 0x7ff)
61			len += 3;
62		else if (c > 0x7f)
63			len += 2;
64		else
65			len++;
66	}
67
68	return len;
69}
70
71int
72ucs2_to_utf8(const CHAR16 *nm, char **name)
73{
74	size_t len, sz;
75	CHAR16 c;
76	char *cp;
77	int freeit = *name == NULL;
78
79	sz = utf8_len_of_ucs2(nm) + 1;
80	len = 0;
81	if (*name != NULL)
82		cp = *name;
83	else
84		cp = *name = AllocatePool(sz);
85	if (*name == NULL)
86		return ENOMEM;
87
88	while (*nm) {
89		c = *nm++;
90		if (c > 0x7ff) {
91			if (len++ < sz)
92				*cp++ = (char)(0xE0 | (c >> 12));
93			if (len++ < sz)
94				*cp++ = (char)(0x80 | ((c >> 6) & 0x3f));
95			if (len++ < sz)
96				*cp++ = (char)(0x80 | (c & 0x3f));
97		} else if (c > 0x7f) {
98			if (len++ < sz)
99				*cp++ = (char)(0xC0 | ((c >> 6) & 0x1f));
100			if (len++ < sz)
101				*cp++ = (char)(0x80 | (c & 0x3f));
102		} else {
103			if (len++ < sz)
104				*cp++ = (char)(c & 0x7f);
105		}
106	}
107
108	if (len >= sz) {
109		/* Absent bugs, we'll never return EOVERFLOW */
110		if (freeit) {
111			FreePool(*name);
112			*name = NULL;
113		}
114		return EOVERFLOW;
115	}
116	*cp++ = '\0';
117
118	return 0;
119}
120
121int
122utf8_to_ucs2(const char *name, CHAR16 **nmp, size_t *len)
123{
124	CHAR16 *nm;
125	size_t sz;
126	uint32_t ucs4;
127	int c, bytes;
128	int freeit = *nmp == NULL;
129
130	sz = strlen(name) * 2 + 2;
131	if (*nmp == NULL)
132		*nmp = AllocatePool(sz);
133	if (*nmp == NULL)
134		return ENOMEM;
135	nm = *nmp;
136	*len = sz;
137
138	ucs4 = 0;
139	bytes = 0;
140	while (sz > 1 && *name != '\0') {
141		c = *name++;
142		/*
143		 * Conditionalize on the two major character types:
144		 * initial and followup characters.
145		 */
146		if ((c & 0xc0) != 0x80) {
147			/* Initial characters. */
148			if (bytes != 0)
149				goto ilseq;
150			if ((c & 0xf8) == 0xf0) {
151				ucs4 = c & 0x07;
152				bytes = 3;
153			} else if ((c & 0xf0) == 0xe0) {
154				ucs4 = c & 0x0f;
155				bytes = 2;
156			} else if ((c & 0xe0) == 0xc0) {
157				ucs4 = c & 0x1f;
158				bytes = 1;
159			} else {
160				ucs4 = c & 0x7f;
161				bytes = 0;
162			}
163		} else {
164			/* Followup characters. */
165			if (bytes > 0) {
166				ucs4 = (ucs4 << 6) + (c & 0x3f);
167				bytes--;
168			} else if (bytes == 0)
169				goto ilseq;
170		}
171		if (bytes == 0) {
172			if (ucs4 > 0xffff)
173				goto ilseq;
174			*nm++ = (CHAR16)ucs4;
175			sz -= 2;
176		}
177	}
178	if (sz < 2) {
179		if (freeit) {
180			FreePool(nm);
181			*nmp = NULL;
182		}
183		return EINVAL;
184	}
185	sz -= 2;
186	*nm = 0;
187	*len -= sz;
188	return 0;
189ilseq:
190	if (freeit) {
191		FreePool(nm);
192		*nmp = NULL;
193	}
194	return EILSEQ;
195}
196