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