1 /* $NetBSD: utils.c,v 1.5 2026/07/08 02:13:38 kre Exp $ */ 2 3 /* 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #ifndef lint 28 __RCSID("$NetBSD: utils.c,v 1.5 2026/07/08 02:13:38 kre Exp $"); 29 #endif /* not lint */ 30 31 #include <sys/endian.h> 32 #include <sys/uuid.h> 33 34 #include <assert.h> 35 #include <ctype.h> 36 #include <err.h> 37 #include <errno.h> 38 #include <inttypes.h> 39 #include <limits.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <fcntl.h> 44 #include <unistd.h> 45 #include <util.h> 46 47 #include "defs.h" 48 #include "utils.h" 49 50 #define UCS2_REPLACEMENT_CHARACTER 0xfffd 51 52 /************************************************************************ 53 * Character encoding conversion routnes 54 * 55 * UEFI uses UCS-2 character encoding. Note that this is not UTF-16 56 * as it doesn't interpret surrogate pairs. 57 * 58 * From <https://www.unicode.org/faq/utf_bom.html#utf16-11>: 59 * 60 * "UCS-2 is obsolete terminology which refers to a Unicode 61 * implementation up to Unicode 1.1, before surrogate code points and 62 * UTF-16 were added to Version 2.0 of the standard. This term should 63 * now be avoided. 64 * 65 * UCS-2 does not describe a data format distinct from UTF-16, because 66 * both use exactly the same 16-bit code unit 67 * representations. However, UCS-2 does not interpret surrogate code 68 * points, and thus cannot be used to conformantly represent 69 * supplementary characters. 70 * 71 * Sometimes in the past an implementation has been labeled "UCS-2" to 72 * indicate that it does not support supplementary characters and 73 * doesn't interpret pairs of surrogate code points as 74 * characters. Such an implementation would not handle processing of 75 * character properties, code point boundaries, collation, etc. for 76 * supplementary characters, nor would it be able to support most 77 * emoji, for example. [AF]" 78 * 79 * Regarding illegal UTF-8 sequences, the same document says: 80 * 81 * "None of the UTFs can generate every arbitrary byte sequence. For 82 * example, in UTF-8 every byte of the form 110xxxxx_2 must be 83 * followed with a byte of the form 10xxxxxx_2. A sequence such as 84 * <110xxxxx_2 0xxxxxxx_2> is illegal, and must never be 85 * generated. When faced with this illegal byte sequence while 86 * transforming or interpreting, a UTF-8 conformant process must treat 87 * the first byte 110xxxxx_2 as an illegal termination error: for 88 * example, either signaling an error, filtering the byte out, or 89 * representing the byte with a marker such as U+FFFD REPLACEMENT 90 * CHARACTER. In the latter two cases, it will continue processing at 91 * the second byte 0xxxxxxx_2. 92 * 93 * A conformant process must not interpret illegal or ill-formed byte 94 * sequences as characters, however, it may take error recovery 95 * actions. No conformant process may use irregular byte sequences to 96 * encode out-of-band information." 97 */ 98 99 /* 100 * ibuf = input buffer (uint16_t *) 101 * isz = bytes in input buffer 102 * obuf = output buffer (char *) 103 * osz = bytes in output buffer (size_t *) 104 * 105 * if (obuf == NULL), malloc obuf. 106 * if (obuf != NULL), write to existing output buffer. 107 * 108 * Return resulting utf8 string. 109 */ 110 PUBLIC char * 111 ucs2_to_utf8(const uint16_t *ibuf, size_t isz, char *obuf, size_t *osz) 112 { 113 uint8_t *dst; 114 size_t dsz, i, j, j_max, n; 115 uint16_t c; 116 117 assert(isz > 0); 118 119 if (obuf != NULL) { 120 assert(osz != NULL); 121 dsz = *osz; 122 dst = (uint8_t *)obuf; 123 } 124 else { 125 dsz = isz * sizeof(*dst); 126 dst = malloc(dsz); 127 } 128 129 /* 130 * Each UCS2 character will encode as at most 3 UTF8 bytes. 131 * 'isz' is the number of bytes in the source buffer which 132 * may well be larger than the UCS2 string. 'osz' is the 133 * actual number of bytes in the NUL terminated USC2 string. 134 */ 135 n = isz / sizeof(*ibuf); /* max number of characters in input buffer */ 136 j = 0; 137 j_max = dsz / sizeof(*dst); 138 for (i = 0; i < n; i++) { 139 c = le16toh(ibuf[i]); 140 if (c == 0) { 141 break; 142 } 143 if (c < 0x0080) { 144 if (j + 1 >= j_max) 145 break; 146 dst[j++] = (uint8_t)c; 147 } 148 else if (c < 0x0800) { 149 if (j + 2 >= j_max) 150 break; 151 dst[j++] = 0xc0 | (uint8_t)(c >> 6); 152 dst[j++] = 0x80 | (uint8_t)(c & 0x3f); 153 } 154 else { 155 if (j + 3 >= j_max) 156 break; 157 /* if the char is a surrogate, replace it */ 158 if (0xD800 <= c && c <= 0xDFFF) 159 c = UCS2_REPLACEMENT_CHARACTER; 160 dst[j++] = 0xe0 | (uint8_t)(c >> 12); 161 dst[j++] = 0x80 | (uint8_t)((c >> 6) & 0x3f); 162 dst[j++] = 0x80 | (uint8_t)(c & 0x3f); 163 } 164 } 165 if (dst != NULL) 166 dst[j] = '\0'; 167 168 if (osz) 169 *osz = j; 170 171 return (char *)dst; 172 } 173 174 /* 175 * ibuf = input buffer (char *) 176 * isz = bytes in input buffer 177 * obuf = output buffer (uint16_t *) 178 * osz = bytes in output buffer (size_t *) 179 * 180 * if (obuf == NULL), malloc obuf. 181 * if (obuf != NULL), write to existing output buffer. 182 * 183 * Return resulting ucs2 string. 184 */ 185 PUBLIC uint16_t * 186 utf8_to_ucs2(const char *ibuf, size_t isz, uint16_t *obuf, size_t *osz) 187 { 188 const uint8_t *src = (const uint8_t *)ibuf; 189 uint16_t *dst; 190 uint16_t out; 191 size_t dsz, i, j, j_max; 192 193 if (obuf != NULL) { 194 assert(osz != NULL); 195 dst = obuf; 196 dsz = *osz; 197 } 198 else { 199 dsz = isz * sizeof(*dst); 200 dst = malloc(dsz); 201 } 202 203 j = 0; 204 j_max = dsz / sizeof(*dst); 205 for (i = 0; i < isz; i++) { 206 out = src[i]; 207 if (out == '\0') { 208 break; 209 } 210 else if (j + 1 >= j_max) { 211 break; 212 } 213 else if ((out & 0x80) == 0) { 214 /* we're good to go */ 215 } 216 else if ((out & 0xe0) == 0xc0) { 217 if (i + 1 >= isz) { /* insufficient source */ 218 break; 219 } 220 if ((src[i + 1] & 0xc0) != 0x80) { 221 out = UCS2_REPLACEMENT_CHARACTER; 222 } 223 else { 224 out &= 0x1f; 225 out <<= 6; 226 out |= src[++i] & 0x3f; 227 } 228 } 229 else if ((out & 0xf0) == 0xe0) { 230 if (i + 2 >= isz) { /* insufficient source */ 231 break; 232 } 233 if ((src[i + 1] & 0xc0) != 0x80 || 234 (src[i + 2] & 0xc0) != 0x80 || 235 (out == 0xed && src[i + 1] > 0x9f)) { 236 out = UCS2_REPLACEMENT_CHARACTER; 237 } 238 else { 239 out &= 0x0f; 240 out <<= 6; 241 out |= src[++i] & 0x3f; 242 out <<= 6; 243 out |= src[++i] & 0x3f; 244 } 245 } 246 else { /* cannot encode as USC2 */ 247 out = UCS2_REPLACEMENT_CHARACTER; 248 } 249 dst[j++] = htole16(out); 250 } 251 dst[j] = '\0'; 252 253 if (src[i] != '\0') 254 warnx("bad UTF8 string"); 255 256 if (osz) 257 *osz = (j + 1) * sizeof(*dst); 258 return dst; 259 } 260 261 PUBLIC size_t 262 utf8_to_ucs2_size(const char *src) 263 { 264 #if 0 265 uint16_t *dst; 266 size_t sz; 267 268 dst = utf8_to_ucs2(src, strlen(src) + 1, NULL, &sz); 269 free(dst); 270 return sz; 271 #else 272 const uint8_t *buf = (const uint8_t *)src; 273 uint out; 274 size_t i, j; 275 276 j = 0; 277 for (i = 0; (out = buf[i]) != '\0'; i++) { 278 if ((out & 0x80) == 0) { 279 /* we're good to go */ 280 } 281 else if ((out & 0xe0) == 0xc0) { 282 if ((buf[i + 1] & 0xc0) == 0x80) { 283 i++; 284 } 285 } 286 else if ((out & 0xf0) == 0xe0) { 287 if ((buf[i + 1] & 0xc0) == 0x80 && 288 (buf[i + 2] & 0xc0) == 0x80) { 289 i += 2; 290 } 291 } 292 j++; 293 } 294 295 return (j + 1) * sizeof(uint16_t); 296 #endif 297 } 298 299 /************************************************************************ 300 * UUID routines 301 */ 302 PUBLIC int 303 uuid_scanf(struct uuid *uuid, const char *str) 304 { 305 306 return sscanf(str, 307 "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", 308 &uuid->time_low, &uuid->time_mid, &uuid->time_hi_and_version, 309 &uuid->clock_seq_hi_and_reserved, &uuid->clock_seq_low, 310 &uuid->node[0], &uuid->node[1], &uuid->node[2], &uuid->node[3], 311 &uuid->node[4], &uuid->node[5]); 312 } 313 314 /* 315 * from sys/kern/kern_uuid.c 316 */ 317 PUBLIC int 318 uuid_snprintf(char *buf, size_t sz, const struct uuid *uuid) 319 { 320 321 return snprintf(buf, sz, 322 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 323 uuid->time_low, uuid->time_mid, uuid->time_hi_and_version, 324 uuid->clock_seq_hi_and_reserved, uuid->clock_seq_low, 325 uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], 326 uuid->node[4], uuid->node[5]); 327 } 328 329 PUBLIC int 330 uuid_printf(const struct uuid *uuid) 331 { 332 char buf[UUID_STR_LEN]; 333 334 (void) uuid_snprintf(buf, sizeof(buf), uuid); 335 printf("%s", buf); 336 return (0); 337 } 338 339 /************************************************************************ 340 * Misc routines 341 */ 342 343 PUBLIC void 344 show_data(const uint8_t *buf, size_t len, const char *prefix) 345 { 346 uint8_t line_buf[17]; 347 size_t i; 348 349 line_buf[16] = '\0'; 350 for (i = 0; i < len; i++) { 351 if ((i & 0xf) == 0) 352 printf("%s%08zx: ", prefix, i); 353 line_buf[i & 0xf] = isprint((int)buf[i]) ? buf[i] : '.'; 354 printf("%02x ", buf[i]); 355 if ((i & 0xf) == 0xf) 356 printf(" %s\n", line_buf); 357 else if ((i & 0x7) == 0x7) 358 printf(" "); 359 } 360 i &= 0xf; 361 if (i != 0) { 362 line_buf[i] = '\0'; 363 if (i < 8) 364 printf(" "); 365 while (i++ < 16) 366 printf(" "); 367 368 printf(" %s\n", line_buf); 369 } 370 } 371 372 PUBLIC uint16_t 373 strtous(const char *str, char **endptr, int base) 374 { 375 uintmax_t val; 376 int rstatus; 377 378 val = strtou(str, endptr, base, 0, USHRT_MAX, &rstatus); 379 380 switch (rstatus) { 381 case EINVAL: 382 assert(0); 383 break; 384 case ENOTSUP: 385 if (endptr != NULL) 386 break; 387 /*FALLTHROUGH*/ 388 case ECANCELED: 389 err(EXIT_FAILURE, "invalid numeric string: %s\n", str); 390 case ERANGE: 391 err(EXIT_FAILURE, "value out of range [0,%#x]: %s\n", 392 USHRT_MAX, str); 393 default: 394 break; 395 } 396 397 return (uint16_t)val; 398 } 399 400 char * 401 read_file(const char *fname, size_t *size) 402 { 403 char *buf, *cp, *ep; 404 size_t bufsz, cnt, sz; 405 ssize_t ssz; 406 int fd, fd_flags; 407 408 assert(fname != NULL); 409 if (fname == NULL) 410 return 0; 411 412 if (strcmp(fname, "-") == 0) { 413 fd = STDIN_FILENO; 414 if ((fd_flags = fcntl(fd, F_GETFL)) == -1) 415 err(EXIT_FAILURE, "fcntl F_GETFL"); 416 417 if (fcntl(fd, F_SETFL, O_NONBLOCK | fd_flags) == -1) 418 err(EXIT_FAILURE, "fcntl F_SETFL"); 419 } 420 else { 421 fd_flags = -1; 422 fd = open(fname, O_RDONLY); 423 if (fd == -1) 424 err(EXIT_FAILURE, "open"); 425 } 426 427 bufsz = 0x800; 428 buf = emalloc(bufsz); 429 cp = buf; 430 ep = buf + bufsz; 431 cnt = 0; 432 for (;;) { 433 ssz = read(fd, cp, (size_t)(ep - cp)); 434 if (ssz == -1) { 435 if (errno == EAGAIN) 436 continue; 437 err(EXIT_FAILURE, "read"); 438 } 439 assert(ssz >= 0); 440 #if 0 441 printf("ssz: %zd\n", ssz); 442 show_data((uint8_t *)cp, (size_t)ssz, ""); 443 #endif 444 if (ssz == 0) 445 break; 446 447 cp += ssz; 448 sz = (size_t)ssz; 449 cnt += sz; 450 451 if (cp < ep) { 452 /* XXX: what about UCS-2? */ 453 *cp = '\0'; 454 cnt++; 455 break; 456 } 457 458 if (cp == ep) { 459 bufsz *= 2; 460 buf = erealloc(buf, bufsz); 461 cp = buf + cnt; 462 ep = buf + bufsz; 463 } 464 } 465 if (fd_flags != -1) 466 fcntl(fd, F_SETFL, fd_flags); 467 else 468 close(fd); 469 470 *size = cnt; 471 return buf; 472 } 473