1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2026 Oxide Computer Company 14 */ 15 16 /* 17 * Test the implementation of various pieces of uchar.h(3HEAD) functionality. 18 */ 19 20 #include <locale.h> 21 #include <err.h> 22 #include <stdlib.h> 23 #include <sys/types.h> 24 #include <sys/sysmacros.h> 25 #include <strings.h> 26 #include <wchar.h> 27 #include <uchar.h> 28 #include <errno.h> 29 #include <string.h> 30 31 static const char *uchar_wide = "光"; 32 static const char32_t uchar_value = 0x5149; 33 static const char *uchar_hello = "hello"; 34 static const char16_t uchar_euro = 0x20ac; 35 36 static void 37 update_locale(const char *loc) 38 { 39 const char *newloc = setlocale(LC_CTYPE, loc); 40 if (newloc == NULL) { 41 err(EXIT_FAILURE, "TEST FAILED: failed to update locale to %s", 42 loc); 43 } 44 45 if (strcmp(newloc, loc) != 0) { 46 errx(EXIT_FAILURE, "TEST FAILED: locale set to %s, but got %s", 47 loc, newloc); 48 } 49 } 50 51 static boolean_t 52 mbrtoc32_ascii(mbstate_t *mbs) 53 { 54 char32_t out; 55 size_t len; 56 boolean_t ret = B_TRUE; 57 58 if ((len = mbrtoc32(&out, uchar_hello, 5, mbs)) != 1) { 59 warnx("expected mbrtoc32 to return 1, returned %zu", len); 60 ret = B_FALSE; 61 } 62 63 if (out != 'h') { 64 warnx("got bad char32_t, expected 0x%x, found 0x%x", 'h', 65 out); 66 ret = B_FALSE; 67 } 68 69 if ((len = mbrtoc32(&out, uchar_hello + 1, 4, mbs)) != 1) { 70 warnx("expected mbrtoc32 to return 1, returned %zu", len); 71 ret = B_FALSE; 72 } 73 74 if (out != 'e') { 75 warnx("got bad char32_t, expected 0x%x, found 0x%x", 'h', 76 out); 77 ret = B_FALSE; 78 } 79 80 return (ret); 81 } 82 83 static boolean_t 84 mbrtoc32_ascii_internal(void) 85 { 86 return (mbrtoc32_ascii(NULL)); 87 } 88 89 static boolean_t 90 mbrtoc32_ascii_mbstate(void) 91 { 92 mbstate_t mbs; 93 94 bzero(&mbs, sizeof (mbs)); 95 return (mbrtoc32_ascii(&mbs)); 96 } 97 98 static boolean_t 99 mbrtoc32_badseq_utf8(void) 100 { 101 mbstate_t mbs; 102 size_t len; 103 char32_t out; 104 boolean_t ret = B_TRUE; 105 char *badstr; 106 107 bzero(&mbs, sizeof (mbs)); 108 len = mbrtoc32(&out, "\xa9", 1, &mbs); 109 if (len != (size_t)-1) { 110 warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1); 111 ret = B_FALSE; 112 } 113 114 if (errno != EILSEQ) { 115 warnx("found bad errno: expected %s, found %s", 116 strerrorname_np(EILSEQ), strerrorname_np(errno)); 117 ret = B_FALSE; 118 } 119 120 badstr = strdup(uchar_wide); 121 if (badstr == NULL) { 122 warn("failed to duplicate uchar_wide"); 123 return (B_FALSE); 124 } 125 126 badstr[1] = '?'; 127 bzero(&mbs, sizeof (mbs)); 128 len = mbrtoc32(&out, badstr, strlen(badstr), &mbs); 129 free(badstr); 130 if (len != (size_t)-1) { 131 warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1); 132 ret = B_FALSE; 133 } 134 135 if (errno != EILSEQ) { 136 warnx("found bad errno: expected %s, found %s", 137 strerrorname_np(EILSEQ), strerrorname_np(errno)); 138 ret = B_FALSE; 139 } 140 141 return (ret); 142 } 143 144 static boolean_t 145 mbrtoc32_badseq_8859(void) 146 { 147 mbstate_t mbs; 148 size_t len; 149 char32_t out; 150 boolean_t ret = B_TRUE; 151 152 bzero(&mbs, sizeof (mbs)); 153 len = mbrtoc32(&out, "\x87", 1, &mbs); 154 if (len != (size_t)-1) { 155 warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1); 156 ret = B_FALSE; 157 } 158 159 if (errno != EILSEQ) { 160 warnx("found bad errno: expected %s, found %s", 161 strerrorname_np(EILSEQ), strerrorname_np(errno)); 162 ret = B_FALSE; 163 } 164 165 return (ret); 166 } 167 168 static boolean_t 169 mbrtoc32_roundtrip(void) 170 { 171 char32_t out; 172 size_t len, clen; 173 mbstate_t mbs; 174 char buf[MB_CUR_MAX]; 175 boolean_t ret = B_TRUE; 176 177 bzero(&mbs, sizeof (mbs)); 178 len = mbrtoc32(&out, uchar_wide, strlen(uchar_wide), &mbs); 179 if (len != 3) { 180 warnx("mbrtoc32 returned %zu, expected %u", len, 3); 181 ret = B_FALSE; 182 } 183 184 if (out != uchar_value) { 185 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 186 out, uchar_value); 187 ret = B_FALSE; 188 } 189 190 bzero(&mbs, sizeof (mbs)); 191 clen = c32rtomb(buf, out, &mbs); 192 if (clen != len) { 193 warnx("c32rtomb returned %zu bytes, but we originally used %zu", 194 clen, len); 195 ret = B_FALSE; 196 } 197 198 if (strncmp(buf, uchar_wide, len) != 0) { 199 warnx("round trip string comparison failed"); 200 ret = B_FALSE; 201 } 202 203 return (ret); 204 } 205 206 static boolean_t 207 mbrtoc32_partial(void) 208 { 209 char32_t out; 210 size_t len, i; 211 mbstate_t mbs; 212 boolean_t ret = B_TRUE; 213 214 bzero(&mbs, sizeof (mbs)); 215 for (i = 0; i < strlen(uchar_wide) - 1; i++) { 216 len = mbrtoc32(&out, uchar_wide + i, 1, &mbs); 217 if (len != (size_t)-2) { 218 warnx("partial mbrtoc32 returned %zu, not -2", len); 219 ret = B_FALSE; 220 } 221 } 222 223 len = mbrtoc32(&out, uchar_wide + i, 1, &mbs); 224 if (len != 1) { 225 warnx("partial mbrtoc32 returned %zu, not 1", len); 226 ret = B_FALSE; 227 } 228 229 if (out != uchar_value) { 230 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 231 out, uchar_value); 232 ret = B_FALSE; 233 } 234 235 return (ret); 236 } 237 238 static boolean_t 239 mbrtoc32_zero(void) 240 { 241 char32_t out, exp = L'\0'; 242 size_t len; 243 mbstate_t mbs; 244 boolean_t ret = B_TRUE; 245 246 bzero(&mbs, sizeof (mbs)); 247 len = mbrtoc32(&out, "", 1, &mbs); 248 if (len != 0) { 249 warnx("partial mbrtoc32 returned %zu, not 0", len); 250 ret = B_FALSE; 251 } 252 253 if (out != exp) { 254 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 255 out, exp); 256 ret = B_FALSE; 257 } 258 259 return (ret); 260 } 261 262 static boolean_t 263 mbrtoc32_zero_len(void) 264 { 265 char32_t out = 0x12345, exp = 0x12345; 266 size_t len; 267 mbstate_t mbs; 268 boolean_t ret = B_TRUE; 269 270 bzero(&mbs, sizeof (mbs)); 271 len = mbrtoc32(&out, uchar_wide, 0, &mbs); 272 if (len != (size_t)-2) { 273 warnx("partial mbrtoc32 returned %zu, not -2", len); 274 ret = B_FALSE; 275 } 276 277 if (out != exp) { 278 warnx("mbrtoc32 incorrectly wrote to char32_t value with " 279 "zero string, found 0x%x not 0x%x", out, exp); 280 ret = B_FALSE; 281 } 282 283 return (ret); 284 } 285 286 static boolean_t 287 mbrtoc32_null(void) 288 { 289 char32_t out = 0x123456, exp = 0x123456; 290 size_t len; 291 mbstate_t mbs; 292 boolean_t ret = B_TRUE; 293 294 bzero(&mbs, sizeof (mbs)); 295 len = mbrtoc32(&out, NULL, 1, &mbs); 296 if (len != 0) { 297 warnx("partial mbrtoc32 returned %zu, not 0", len); 298 ret = B_FALSE; 299 } 300 301 if (out != exp) { 302 warnx("mbrtoc32 incorrectly wrote to char32_t value with " 303 "null string, found 0x%x not 0x%x", out, exp); 304 ret = B_FALSE; 305 } 306 307 return (ret); 308 } 309 310 /* 311 * Verify that a character string in 8859-1 (which nominally overlaps with UTF) 312 * can properly go through iconv and come out as what we expect on the other 313 * side. 314 */ 315 static boolean_t 316 mbrtoc32_iconv_8859(void) 317 { 318 mbstate_t mbs; 319 size_t len; 320 char32_t out, exp = L'\xa6'; 321 boolean_t ret = B_TRUE; 322 323 bzero(&mbs, sizeof (mbs)); 324 len = mbrtoc32(&out, "\xa6", 1, &mbs); 325 if (len != 1) { 326 warnx("mbrtoc32 returned %zu, not 1", len); 327 ret = B_FALSE; 328 } 329 330 if (out != exp) { 331 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 332 out, exp); 333 ret = B_FALSE; 334 } 335 336 return (ret); 337 } 338 339 /* 340 * Similar to the above but here we actually expect it to be something else 341 * entirely and not a 1:1 translation. This verifies that iconv is firing and 342 * getting us more than one character. 343 */ 344 static boolean_t 345 mbrtoc32_iconv_euro(void) 346 { 347 mbstate_t mbs; 348 size_t len; 349 char32_t out, exp = uchar_euro; 350 boolean_t ret = B_TRUE; 351 352 bzero(&mbs, sizeof (mbs)); 353 len = mbrtoc32(&out, "\xa4", 1, &mbs); 354 if (len != 1) { 355 warnx("mbrtoc32 returned %zu, not 1", len); 356 ret = B_FALSE; 357 } 358 359 if (out != exp) { 360 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 361 out, exp); 362 ret = B_FALSE; 363 } 364 365 return (ret); 366 } 367 368 static boolean_t 369 mbrtoc16_ascii(mbstate_t *mbs) 370 { 371 char16_t out; 372 size_t len; 373 boolean_t ret = B_TRUE; 374 375 if ((len = mbrtoc16(&out, uchar_hello, 5, mbs)) != 1) { 376 warnx("expected mbrtoc16 to return 1, returned %zu", len); 377 ret = B_FALSE; 378 } 379 380 if (out != 'h') { 381 warnx("got bad char16_t, expected 0x%x, found 0x%x", 'h', 382 out); 383 ret = B_FALSE; 384 } 385 386 if ((len = mbrtoc16(&out, uchar_hello + 1, 4, mbs)) != 1) { 387 warnx("expected mbrtoc16 to return 1, returned %zu", len); 388 ret = B_FALSE; 389 } 390 391 if (out != 'e') { 392 warnx("got bad char16_t, expected 0x%x, found 0x%x", 'h', 393 out); 394 ret = B_FALSE; 395 } 396 397 return (ret); 398 } 399 400 static boolean_t 401 mbrtoc16_ascii_internal(void) 402 { 403 return (mbrtoc16_ascii(NULL)); 404 } 405 406 static boolean_t 407 mbrtoc16_ascii_mbstate(void) 408 { 409 mbstate_t mbs; 410 411 bzero(&mbs, sizeof (mbs)); 412 return (mbrtoc16_ascii(&mbs)); 413 } 414 415 static boolean_t 416 mbrtoc16_null(void) 417 { 418 char16_t out = 0x1234, exp = 0x1234; 419 size_t len; 420 mbstate_t mbs; 421 boolean_t ret = B_TRUE; 422 423 bzero(&mbs, sizeof (mbs)); 424 len = mbrtoc16(&out, NULL, 1, &mbs); 425 if (len != 0) { 426 warnx("partial mbrtoc16 returned %zu, not 0", len); 427 ret = B_FALSE; 428 } 429 430 if (out != exp) { 431 warnx("mbrtoc16 incorrectly wrote to char16_t value with " 432 "null string, found 0x%x not 0x%x", out, exp); 433 ret = B_FALSE; 434 } 435 436 return (ret); 437 } 438 439 static boolean_t 440 mbrtoc16_zero(void) 441 { 442 char16_t out, exp = L'\0'; 443 size_t len; 444 mbstate_t mbs; 445 boolean_t ret = B_TRUE; 446 447 bzero(&mbs, sizeof (mbs)); 448 len = mbrtoc16(&out, "", 1, &mbs); 449 if (len != 0) { 450 warnx("partial mbrtoc16 returned %zu, not 0", len); 451 ret = B_FALSE; 452 } 453 454 if (out != exp) { 455 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 456 out, exp); 457 ret = B_FALSE; 458 } 459 460 return (ret); 461 } 462 463 static boolean_t 464 mbrtoc16_zero_len(void) 465 { 466 char16_t out = 0x5432, exp = 0x5432; 467 size_t len; 468 mbstate_t mbs; 469 boolean_t ret = B_TRUE; 470 471 bzero(&mbs, sizeof (mbs)); 472 len = mbrtoc16(&out, uchar_wide, 0, &mbs); 473 if (len != (size_t)-2) { 474 warnx("partial mbrtoc16 returned %zu, not -2", len); 475 ret = B_FALSE; 476 } 477 478 if (out != exp) { 479 warnx("mbrtoc16 incorrectly wrote to char16_t value with " 480 "zero length string, found 0x%x not 0x%x", out, exp); 481 ret = B_FALSE; 482 } 483 484 return (ret); 485 } 486 487 static boolean_t 488 mbrtoc16_roundtrip(void) 489 { 490 char16_t out; 491 size_t len, clen; 492 mbstate_t mbs; 493 char buf[MB_CUR_MAX]; 494 boolean_t ret = B_TRUE; 495 496 bzero(&mbs, sizeof (mbs)); 497 len = mbrtoc16(&out, uchar_wide, strlen(uchar_wide), &mbs); 498 if (len != 3) { 499 warnx("mbrtoc16 returned %zu, expected %u", len, 3); 500 ret = B_FALSE; 501 } 502 503 if (out != uchar_value) { 504 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 505 out, uchar_value); 506 ret = B_FALSE; 507 } 508 509 clen = c16rtomb(buf, out, &mbs); 510 if (clen != len) { 511 warnx("c16rtomb returned %zu bytes, but we originally used %zu", 512 clen, len); 513 ret = B_FALSE; 514 } 515 516 if (strncmp(buf, uchar_wide, len) != 0) { 517 warnx("round trip string comparison failed"); 518 ret = B_FALSE; 519 } 520 521 return (ret); 522 } 523 524 static boolean_t 525 mbrtoc16_partial(void) 526 { 527 char16_t out; 528 size_t len, i; 529 mbstate_t mbs; 530 boolean_t ret = B_TRUE; 531 532 bzero(&mbs, sizeof (mbs)); 533 for (i = 0; i < strlen(uchar_wide) - 1; i++) { 534 len = mbrtoc16(&out, uchar_wide + i, 1, &mbs); 535 if (len != (size_t)-2) { 536 warnx("partial mbrtoc16 returned %zu, not -2", len); 537 ret = B_FALSE; 538 } 539 } 540 541 len = mbrtoc16(&out, uchar_wide + i, 1, &mbs); 542 if (len != 1) { 543 warnx("partial mbrtoc16 returned %zu, not 1", len); 544 ret = B_FALSE; 545 } 546 547 if (out != uchar_value) { 548 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 549 out, uchar_value); 550 ret = B_FALSE; 551 } 552 553 return (ret); 554 } 555 556 static boolean_t 557 mbrtoc16_surrogate(void) 558 { 559 char16_t out0, out1; 560 size_t len, clen; 561 mbstate_t mbs; 562 const char *surrogate = "\xF0\x9F\x92\xA9"; 563 char16_t exp0 = 0xd83d, exp1 = 0xdca9; 564 size_t slen = strlen(surrogate); 565 boolean_t ret = B_TRUE; 566 char buf[MB_CUR_MAX]; 567 568 bzero(&mbs, sizeof (mbs)); 569 len = mbrtoc16(&out0, surrogate, slen, &mbs); 570 if (len != slen) { 571 warnx("mbrtoc16 returned %zu, expected %zu", len, slen); 572 ret = B_FALSE; 573 } 574 575 if (out0 != exp0) { 576 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 577 out0, exp0); 578 ret = B_FALSE; 579 } 580 581 if (mbsinit(&mbs) != 0) { 582 warnx("mb state with a surrogate character is somehow in the " 583 "initial state"); 584 ret = B_FALSE; 585 } 586 587 len = mbrtoc16(&out1, uchar_wide, strlen(uchar_wide), &mbs); 588 if (len != (size_t)-3) { 589 warnx("mbrtoc16 returned %zu, expected -3", len); 590 ret = B_FALSE; 591 } 592 593 if (mbsinit(&mbs) == 0) { 594 warnx("mb state with after both surrogate characters isn't " 595 "in initial state"); 596 ret = B_FALSE; 597 } 598 599 if (out1 != exp1) { 600 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 601 out1, exp1); 602 ret = B_FALSE; 603 } 604 605 clen = c16rtomb(buf, out0, &mbs); 606 if (clen != 0) { 607 warnx("c16rtomb returned %zu bytes, but expected zero for the " 608 "first surrogate", clen); 609 ret = B_FALSE; 610 } 611 612 if (mbsinit(&mbs) != 0) { 613 warnx("mb state with a surrogate character is somehow in the " 614 "initial state"); 615 ret = B_FALSE; 616 } 617 618 clen = c16rtomb(buf, out1, &mbs); 619 if (clen != slen) { 620 warnx("c16rtomb returned %zu, expected %zu", len, slen); 621 ret = B_FALSE; 622 } 623 624 if (mbsinit(&mbs) == 0) { 625 warnx("mb state with after both surrogate characters isn't " 626 "in initial state"); 627 ret = B_FALSE; 628 } 629 630 if (strncmp(buf, surrogate, slen) != 0) { 631 warnx("round trip string comparison failed"); 632 ret = B_FALSE; 633 } 634 635 return (ret); 636 } 637 638 static boolean_t 639 c32rtomb_eilseq_iso8859(void) 640 { 641 char buf[MB_CUR_MAX]; 642 mbstate_t mbs; 643 size_t len; 644 boolean_t ret = B_TRUE; 645 646 bzero(&mbs, sizeof (mbs)); 647 len = c32rtomb(buf, uchar_value, &mbs); 648 if (len != (size_t)-1) { 649 warnx("c32rtomb returned %zd, expected -1", len); 650 ret = B_FALSE; 651 } 652 653 if (errno != EILSEQ) { 654 warnx("found bad errno: expected %s, found %s", 655 strerrorname_np(EILSEQ), strerrorname_np(errno)); 656 ret = B_FALSE; 657 } 658 659 return (ret); 660 } 661 662 static boolean_t 663 c16rtomb_eilseq_iso8859(void) 664 { 665 char buf[MB_CUR_MAX]; 666 mbstate_t mbs; 667 size_t len; 668 boolean_t ret = B_TRUE; 669 670 bzero(&mbs, sizeof (mbs)); 671 len = c32rtomb(buf, (char16_t)uchar_value, &mbs); 672 if (len != (size_t)-1) { 673 warnx("c32rtomb returned %zd, expected -1", len); 674 ret = B_FALSE; 675 } 676 677 if (errno != EILSEQ) { 678 warnx("found bad errno: expected %s, found %s", 679 strerrorname_np(EILSEQ), strerrorname_np(errno)); 680 ret = B_FALSE; 681 } 682 683 return (ret); 684 } 685 686 static boolean_t 687 c32rtomb_eilseq_utf8(void) 688 { 689 char buf[MB_CUR_MAX]; 690 mbstate_t mbs; 691 size_t len; 692 boolean_t ret = B_TRUE; 693 694 bzero(&mbs, sizeof (mbs)); 695 len = c32rtomb(buf, UINT32_MAX, &mbs); 696 if (len != (size_t)-1) { 697 warnx("c32rtomb returned %zd, expected -1", len); 698 ret = B_FALSE; 699 } 700 701 if (errno != EILSEQ) { 702 warnx("found bad errno: expected %s, found %s", 703 strerrorname_np(EILSEQ), strerrorname_np(errno)); 704 ret = B_FALSE; 705 } 706 707 return (ret); 708 } 709 710 static boolean_t 711 c16rtomb_bad_first(void) 712 { 713 char buf[MB_CUR_MAX]; 714 mbstate_t mbs; 715 size_t len, i; 716 char16_t first = 0xd83d; 717 char16_t bad[] = { 0x0, 0xd7ff, 0xd83d, 0xd900, 0xffff }; 718 boolean_t ret = B_TRUE; 719 720 for (i = 0; i < ARRAY_SIZE(bad); i++) { 721 bzero(&mbs, sizeof (mbs)); 722 len = c16rtomb(buf, first, &mbs); 723 if (len != 0) { 724 warnx("c16rtomb returned %zd, expected 0", len); 725 ret = B_FALSE; 726 } 727 728 len = c16rtomb(buf, bad[i], &mbs); 729 if (len != (size_t)-1) { 730 warnx("c16rtomb surrogate %x returned %zd, expected " 731 "-1", bad[i], len); 732 ret = B_FALSE; 733 } 734 735 if (errno != EILSEQ) { 736 warnx("found bad errno: expected %s, found %s", 737 strerrorname_np(EILSEQ), strerrorname_np(errno)); 738 ret = B_FALSE; 739 } 740 } 741 742 return (ret); 743 } 744 745 static boolean_t 746 c16rtomb_bad_second(void) 747 { 748 char buf[MB_CUR_MAX]; 749 mbstate_t mbs; 750 size_t len, i; 751 char16_t bad[] = { 0xdc00, 0xdd34, 0xdfff }; 752 boolean_t ret = B_TRUE; 753 754 for (i = 0; i < ARRAY_SIZE(bad); i++) { 755 bzero(&mbs, sizeof (mbs)); 756 len = c16rtomb(buf, bad[i], &mbs); 757 if (len != (size_t)-1) { 758 warnx("c16rtomb surrogate %x returned %zd, expected -1", 759 bad[i], len); 760 ret = B_FALSE; 761 } 762 763 if (errno != EILSEQ) { 764 warnx("found bad errno: expected %s, found %s", 765 strerrorname_np(EILSEQ), strerrorname_np(errno)); 766 ret = B_FALSE; 767 } 768 } 769 770 return (ret); 771 } 772 773 static boolean_t 774 c32rtomb_null(void) 775 { 776 size_t len; 777 mbstate_t mbs; 778 boolean_t ret = B_TRUE; 779 780 bzero(&mbs, sizeof (mbs)); 781 len = c32rtomb(NULL, uchar_value, &mbs); 782 if (len != 1) { 783 warnx("c32rtomb returned %zu, expected %d", len, 1); 784 ret = B_FALSE; 785 } 786 787 return (ret); 788 } 789 790 static boolean_t 791 c32rtomb_null_euro(void) 792 { 793 size_t len; 794 mbstate_t mbs; 795 boolean_t ret = B_TRUE; 796 797 bzero(&mbs, sizeof (mbs)); 798 len = c32rtomb(NULL, uchar_euro, &mbs); 799 if (len != 1) { 800 warnx("c32rtomb returned %zu, expected %d", len, 1); 801 ret = B_FALSE; 802 } 803 804 return (ret); 805 } 806 807 static boolean_t 808 c16rtomb_null(void) 809 { 810 size_t len; 811 mbstate_t mbs; 812 boolean_t ret = B_TRUE; 813 814 bzero(&mbs, sizeof (mbs)); 815 len = c16rtomb(NULL, uchar_value, &mbs); 816 if (len != 1) { 817 warnx("c16rtomb returned %zu, expected %d", len, 1); 818 ret = B_FALSE; 819 } 820 821 return (ret); 822 } 823 824 static boolean_t 825 c16rtomb_null_euro(void) 826 { 827 size_t len; 828 mbstate_t mbs; 829 boolean_t ret = B_TRUE; 830 831 bzero(&mbs, sizeof (mbs)); 832 len = c16rtomb(NULL, uchar_euro, &mbs); 833 if (len != 1) { 834 warnx("c16rtomb returned %zu, expected %d", len, 1); 835 ret = B_FALSE; 836 } 837 838 return (ret); 839 } 840 841 static boolean_t 842 c32rtomb_iconv_ascii(void) 843 { 844 char buf[MB_CUR_MAX]; 845 const char *exp = "h"; 846 mbstate_t mbs; 847 size_t len; 848 boolean_t ret = B_TRUE; 849 850 bzero(&mbs, sizeof (mbs)); 851 len = c32rtomb(buf, L'h', &mbs); 852 if (len != 1) { 853 warnx("c32rtomb returned %zd, expected 1", len); 854 ret = B_FALSE; 855 } 856 857 if (strncmp(buf, exp, len) != 0) { 858 warnx("c32rtomb didn't return expected string"); 859 ret = B_FALSE; 860 } 861 862 return (ret); 863 } 864 865 static boolean_t 866 c32rtomb_iconv_euro(void) 867 { 868 char buf[MB_CUR_MAX]; 869 const char *exp = "\xa4"; 870 mbstate_t mbs; 871 size_t len; 872 boolean_t ret = B_TRUE; 873 874 bzero(&mbs, sizeof (mbs)); 875 len = c32rtomb(buf, uchar_euro, &mbs); 876 if (len != 1) { 877 warnx("c32rtomb returned %zd, expected 1", len); 878 ret = B_FALSE; 879 } 880 881 if (strncmp(buf, exp, len) != 0) { 882 warnx("c32rtomb didn't return expected string"); 883 ret = B_FALSE; 884 } 885 886 return (ret); 887 } 888 889 /* 890 * Do a full round trip of our wide character into a full UTF-8 sequence and 891 * then back again. 892 */ 893 static boolean_t 894 mbrtoc8_roundtrip(const char *input, size_t inlen, const char8_t *out, 895 size_t outlen) 896 { 897 char8_t c8[4]; 898 size_t exp_mbret[4]; 899 size_t exp_csret[4]; 900 char str[MB_CUR_MAX]; 901 mbstate_t mbs; 902 boolean_t ret = B_TRUE; 903 904 /* 905 * Construct results based on the given lengths. We will need to call 906 * mbrtoc8 one time for each output character. The first time we call it 907 * though it'll output the input length as it should consume it all. The 908 * opposite is true when we go in reverse. 909 */ 910 for (size_t i = 0; i < outlen; i++) { 911 if (i == 0) { 912 exp_mbret[i] = inlen; 913 } else { 914 exp_mbret[i] = (size_t)-3; 915 } 916 } 917 918 for (size_t i = 0; i < outlen; i++) { 919 if (i == outlen - 1) { 920 exp_csret[i] = inlen; 921 } else { 922 exp_csret[i] = 0; 923 } 924 } 925 926 bzero(&mbs, sizeof (mbs)); 927 for (size_t i = 0; i < outlen; i++) { 928 size_t len = mbrtoc8(&c8[i], input, inlen, &mbs); 929 if (len != exp_mbret[i]) { 930 warnx("mbrtoc8[%zu] returned %zu, expected %zd", i, len, 931 exp_mbret[i]); 932 ret = B_FALSE; 933 } 934 935 if (c8[i] != out[i]) { 936 warnx("mbrtoc8[%zu] converted character to 0x%x not " 937 "0x%x", i, c8[i], out[i]); 938 ret = B_FALSE; 939 } 940 } 941 942 bzero(&mbs, sizeof (mbs)); 943 for (size_t i = 0; i < outlen; i++) { 944 size_t len = c8rtomb(str, c8[i], &mbs); 945 if (len != exp_csret[i]) { 946 warnx("c8rtomb[%zu] returned %zu, expected %zd", i, len, 947 exp_csret[i]); 948 ret = B_FALSE; 949 } 950 } 951 952 if (strncmp(str, input, inlen) != 0) { 953 warnx("round trip string comparison failed"); 954 ret = B_FALSE; 955 } 956 957 return (ret); 958 } 959 960 static boolean_t 961 mbrtoc8_roundtrip_1b(void) 962 { 963 const char8_t out[1] = { 'h' }; 964 return (mbrtoc8_roundtrip(uchar_hello, 1, out, ARRAY_SIZE(out))); 965 } 966 967 static boolean_t 968 mbrtoc8_roundtrip_2b(void) 969 { 970 const char *in = "ũ"; 971 const char8_t out[2] = { 0xc5, 0xa9 }; 972 return (mbrtoc8_roundtrip(in, strlen(in), out, ARRAY_SIZE(out))); 973 } 974 975 static boolean_t 976 mbrtoc8_roundtrip_3b(void) 977 { 978 const char8_t out[3] = { 0xe5, 0x85, 0x89 }; 979 return (mbrtoc8_roundtrip(uchar_wide, strlen(uchar_wide), out, 980 ARRAY_SIZE(out))); 981 } 982 983 static boolean_t 984 mbrtoc8_roundtrip_4b(void) 985 { 986 /* U+1B001 */ 987 const char four[4] = { 0xf0, 0x9b, 0x80, 0x81 }; 988 const char8_t out[4] = { 0xf0, 0x9b, 0x80, 0x81 }; 989 return (mbrtoc8_roundtrip(four, ARRAY_SIZE(four), out, 990 ARRAY_SIZE(out))); 991 } 992 993 static boolean_t 994 mbrtoc8_roundtrip_euro(void) 995 { 996 const char *in = "\xa4"; 997 const char8_t out[3] = { 0xe2, 0x82, 0xac }; 998 return (mbrtoc8_roundtrip(in, strlen(in), out, ARRAY_SIZE(out))); 999 } 1000 1001 typedef struct { 1002 const char *bu_desc; 1003 uint8_t bu_nchar; 1004 char8_t bu_chars[4]; 1005 } bad_utf8_t; 1006 1007 /* 1008 * This covers various bad orderings of characters. See c8rtomb.c in libc. One 1009 * caveat is that we can't use a 0x00 character in here. That will be 1010 * interpreted per the standard as a null character regardless of what was 1011 * already there. 1012 */ 1013 static const bad_utf8_t bad_utf[] = { 1014 { "invalid starting (1)", 1, { 0x80 } }, 1015 { "invalid starting (2)", 1, { 0xff } }, 1016 { "invalid starting (3)", 1, { 0xc1 } }, 1017 { "invalid 2-byte (1)", 2, { 0xc2, 0x01 } }, 1018 { "invalid 2-byte (2)", 2, { 0xc2, 0x42 } }, 1019 { "invalid 2-byte (3)", 2, { 0xc2, 0xc0 } }, 1020 { "invalid 2-byte (4)", 2, { 0xdd, 0xdd } }, 1021 { "invalid 2-byte (5)", 2, { 0xde, 0xed } }, 1022 { "invalid 2-byte (6)", 2, { 0xdf, 0xfd } }, 1023 { "invalid 3-byte e0 (1)", 2, { 0xe0, 0x9f } }, 1024 { "invalid 3-byte e0 (2)", 2, { 0xe0, 0xc0 } }, 1025 { "invalid 3-byte e0 (3)", 3, { 0xe0, 0xaa, 0x07 } }, 1026 { "invalid 3-byte e0 (4)", 3, { 0xe0, 0xbf, 0xfb } }, 1027 { "invalid 3-byte ed (1)", 2, { 0xed, 0x7f } }, 1028 { "invalid 3-byte ed (2)", 2, { 0xed, 0xa0 } }, 1029 { "invalid 3-byte ed (3)", 3, { 0xed, 0x98, 0x07 } }, 1030 { "invalid 3-byte ed (4)", 3, { 0xed, 0x87, 0xcd } }, 1031 { "invalid 3-byte (1)", 2, { 0xea, 0xea } }, 1032 { "invalid 3-byte (2)", 2, { 0xe9, 0x23 } }, 1033 { "invalid 3-byte (3)", 2, { 0xe8, 0xdc } }, 1034 { "invalid 3-byte (4)", 3, { 0xe8, 0x80, 0x08 } }, 1035 { "invalid 3-byte (5)", 3, { 0xe8, 0x81, 0xc0 } }, 1036 { "invalid 3-byte (6)", 3, { 0xe2, 0xaa, 0xce } }, 1037 { "invalid 4-byte f0 (1)", 2, { 0xf0, 0x89 } }, 1038 { "invalid 4-byte f0 (2)", 2, { 0xf0, 0x17 } }, 1039 { "invalid 4-byte f0 (3)", 2, { 0xf0, 0xc0 } }, 1040 { "invalid 4-byte f0 (4)", 3, { 0xf0, 0x90, 0x23 } }, 1041 { "invalid 4-byte f0 (5)", 3, { 0xf0, 0x90, 0x42 } }, 1042 { "invalid 4-byte f0 (6)", 3, { 0xf0, 0xa0, 0xc0 } }, 1043 { "invalid 4-byte f0 (7)", 3, { 0xf0, 0xb0, 0xdf } }, 1044 { "invalid 4-byte f0 (9)", 4, { 0xf0, 0xa1, 0x81, 0x18 } }, 1045 { "invalid 4-byte f0 (10)", 4, { 0xf0, 0x92, 0x82, 0x2b } }, 1046 { "invalid 4-byte f0 (11)", 4, { 0xf0, 0xa3, 0x92, 0xc9 } }, 1047 { "invalid 4-byte f0 (12)", 4, { 0xf0, 0xb4, 0xbf, 0xfb } }, 1048 { "invalid 4-byte f4 (1)", 2, { 0xf4, 0xa2 } }, 1049 { "invalid 4-byte f4 (2)", 2, { 0xf4, 0x17 } }, 1050 { "invalid 4-byte f4 (3)", 2, { 0xf4, 0xc0 } }, 1051 { "invalid 4-byte f4 (4)", 3, { 0xf4, 0x80, 0x23 } }, 1052 { "invalid 4-byte f4 (5)", 3, { 0xf4, 0x81, 0x42 } }, 1053 { "invalid 4-byte f4 (6)", 3, { 0xf4, 0x82, 0xc0 } }, 1054 { "invalid 4-byte f4 (7)", 3, { 0xf4, 0x83, 0xdf } }, 1055 { "invalid 4-byte f4 (9)", 4, { 0xf4, 0x84, 0x81, 0x18 } }, 1056 { "invalid 4-byte f4 (10)", 4, { 0xf4, 0x85, 0x82, 0x2b } }, 1057 { "invalid 4-byte f4 (11)", 4, { 0xf4, 0x86, 0x92, 0xc9 } }, 1058 { "invalid 4-byte f4 (12)", 4, { 0xf4, 0x87, 0xbf, 0xfb } }, 1059 { "invalid 4-byte (1)", 2, { 0xf1, 0xcc } }, 1060 { "invalid 4-byte (2)", 2, { 0xf2, 0x17 } }, 1061 { "invalid 4-byte (3)", 2, { 0xf3, 0xc0 } }, 1062 { "invalid 4-byte (4)", 3, { 0xf2, 0x80, 0x23 } }, 1063 { "invalid 4-byte (5)", 3, { 0xf1, 0x91, 0x42 } }, 1064 { "invalid 4-byte (6)", 3, { 0xf2, 0xa2, 0xc0 } }, 1065 { "invalid 4-byte (7)", 3, { 0xf3, 0xb3, 0xdf } }, 1066 { "invalid 4-byte (9)", 4, { 0xf2, 0xa4, 0x81, 0x18 } }, 1067 { "invalid 4-byte (10)", 4, { 0xf1, 0x95, 0x82, 0x2b } }, 1068 { "invalid 4-byte (11)", 4, { 0xf2, 0x86, 0x92, 0xc9 } }, 1069 { "invalid 4-byte (12)", 4, { 0xf3, 0xa7, 0xbf, 0xfb } }, 1070 }; 1071 1072 static boolean_t 1073 c8rtomb_bad_utf8(void) 1074 { 1075 boolean_t ret = B_TRUE; 1076 1077 for (size_t i = 0; i < ARRAY_SIZE(bad_utf); i++) { 1078 const bad_utf8_t *t = &bad_utf[i]; 1079 char buf[MB_CUR_MAX]; 1080 mbstate_t mbs = { 0 }; 1081 boolean_t cont = B_TRUE; 1082 1083 /* 1084 * Only the last character in the list should cause a failure. 1085 * Make sure all the preceding ones are accepted, but don't 1086 * form a complete character. 1087 */ 1088 for (size_t c = 0; c < t->bu_nchar - 1; c++) { 1089 size_t r = c8rtomb(buf, t->bu_chars[c], &mbs); 1090 if (r != 0) { 1091 warnx("%s: c8rtomb char[%zu] (0x%x) returned " 1092 "%zd, not 0", t->bu_desc, c, 1093 t->bu_chars[c], r); 1094 ret = cont = B_FALSE; 1095 break; 1096 } 1097 } 1098 1099 if (!cont) 1100 continue; 1101 1102 size_t r = c8rtomb(buf, t->bu_chars[t->bu_nchar - 1], &mbs); 1103 if (r != (size_t)-1) { 1104 warnx("%s: c8rtomb final char (0x%x) returned %zu, " 1105 "not -1", t->bu_desc, t->bu_chars[t->bu_nchar - 1], 1106 r); 1107 ret = B_FALSE; 1108 } else if (errno != EILSEQ) { 1109 warnx("%s: c8rtomb failed with %s, not %s", t->bu_desc, 1110 strerrorname_np(errno), strerrorname_np(EILSEQ)); 1111 ret = B_FALSE; 1112 } 1113 } 1114 1115 return (ret); 1116 } 1117 1118 static boolean_t 1119 mbrtoc8_null(void) 1120 { 1121 char8_t out = 0x23, exp = 0x23; 1122 size_t len; 1123 mbstate_t mbs = { 0 }; 1124 boolean_t ret = B_TRUE; 1125 1126 len = mbrtoc8(&out, NULL, 1, &mbs); 1127 if (len != 0) { 1128 warnx("partial mbrtoc8 returned %zu, not 0", len); 1129 ret = B_FALSE; 1130 } 1131 1132 if (out != exp) { 1133 warnx("mbrtoc8 incorrectly wrote to char8_t value with " 1134 "null string, found 0x%x not 0x%x", out, exp); 1135 ret = B_FALSE; 1136 } 1137 1138 return (ret); 1139 } 1140 1141 static boolean_t 1142 mbrtoc8_zero(void) 1143 { 1144 char8_t out, exp = '\0'; 1145 size_t len; 1146 mbstate_t mbs; 1147 boolean_t ret = B_TRUE; 1148 1149 bzero(&mbs, sizeof (mbs)); 1150 len = mbrtoc8(&out, "", 1, &mbs); 1151 if (len != 0) { 1152 warnx("partial mbrtoc8 returned %zu, not 0", len); 1153 ret = B_FALSE; 1154 } 1155 1156 if (out != exp) { 1157 warnx("mbrtoc8 converted character to 0x%x not 0x%x", 1158 out, exp); 1159 ret = B_FALSE; 1160 } 1161 1162 return (ret); 1163 } 1164 1165 static boolean_t 1166 mbrtoc8_zero_len(void) 1167 { 1168 char8_t out = 0x23, exp = 0x23; 1169 size_t len; 1170 mbstate_t mbs = { 0 }; 1171 boolean_t ret = B_TRUE; 1172 1173 len = mbrtoc8(&out, uchar_wide, 0, &mbs); 1174 if (len != (size_t)-2) { 1175 warnx("partial mbrtoc8 returned %zu, not -2", len); 1176 ret = B_FALSE; 1177 } 1178 1179 if (out != exp) { 1180 warnx("mbrtoc8 incorrectly wrote to char8_t value with " 1181 "zero length string, found 0x%x not 0x%x", out, exp); 1182 ret = B_FALSE; 1183 } 1184 1185 return (ret); 1186 } 1187 1188 static boolean_t 1189 c8rtomb_null(void) 1190 { 1191 size_t len; 1192 mbstate_t mbs = { 0 }; 1193 boolean_t ret = B_TRUE; 1194 1195 len = c8rtomb(NULL, 0x42, &mbs); 1196 if (len != 1) { 1197 warnx("c32rtomb returned %zu, expected %d", len, 1); 1198 ret = B_FALSE; 1199 } 1200 1201 return (ret); 1202 } 1203 1204 1205 typedef boolean_t (*uchar_test_f)(void); 1206 1207 typedef struct uchar_test { 1208 uchar_test_f ut_func; 1209 const char *ut_test; 1210 const char *ut_locale; 1211 } uchar_test_t; 1212 1213 static const uchar_test_t uchar_tests[] = { 1214 { mbrtoc32_ascii_mbstate, "mbrtoc32: ascii conversion" }, 1215 { mbrtoc32_ascii_internal, "mbrtoc32: ascii conversion (internal " 1216 "mbstate_t)" }, 1217 { mbrtoc32_ascii_mbstate, "mbrtoc32: ascii conversion (C locale)", 1218 "C" }, 1219 { mbrtoc32_ascii_internal, "mbrtoc32: ascii conversion (internal " 1220 "mbstate_t) (C Locale)", "C" }, 1221 { mbrtoc32_badseq_utf8, "mbrtoc32: bad locale sequence (UTF-8)" }, 1222 { mbrtoc32_badseq_8859, "mbrtoc32: bad locale sequence (8859-1)" }, 1223 { mbrtoc32_roundtrip, "mbrtoc32: round trip conversion" }, 1224 { mbrtoc32_partial, "mbrtoc32: correctly consume partial sequences" }, 1225 { mbrtoc32_zero, "mbrtoc32: correctly handle L'\\0'" }, 1226 { mbrtoc32_zero, "mbrtoc32: correctly handle L'\\0' (C)", "C" }, 1227 { mbrtoc32_zero, "mbrtoc32: correctly handle L'\\0' (8859-15)", 1228 "en_US.ISO8859-15" }, 1229 { mbrtoc32_zero_len, "mbrtoc32: correctly handle length of zero" }, 1230 { mbrtoc32_zero_len, "mbrtoc32: correctly handle length of zero (C)", 1231 "C" }, 1232 { mbrtoc32_zero_len, "mbrtoc32: correctly handle length of zero " 1233 "(8859-1)", "en_US.ISO8859-15" }, 1234 { mbrtoc32_null, "mbrtoc32: correctly handle null string" }, 1235 { mbrtoc32_null, "mbrtoc32: correctly handle null string (C)", "C" }, 1236 { mbrtoc32_null, "mbrtoc32: correctly handle null string (8859-15)", 1237 "en_US.ISO8859-15" }, 1238 { mbrtoc32_iconv_8859, "mbrtoc32: correctly convert data from 8859-1", 1239 "en_US.ISO8859-1" }, 1240 { mbrtoc32_iconv_euro, "mbrtoc32: correctly convert data from 8859-15", 1241 "en_US.ISO8859-15" }, 1242 { mbrtoc16_ascii_mbstate, "mbrtoc16: ascii conversion" }, 1243 { mbrtoc16_ascii_internal, "mbrtoc16: ascii conversion (internal " 1244 "mbstate_t)" }, 1245 { mbrtoc16_ascii_mbstate, "mbrtoc16: ascii conversion (C)", "C" }, 1246 { mbrtoc16_ascii_internal, "mbrtoc16: ascii conversion (internal " 1247 "mbstate_t) (C)", "C" }, 1248 { mbrtoc16_null, "mbrtoc16: correctly handle null string" }, 1249 { mbrtoc16_null, "mbrtoc16: correctly handle null string (C)", "C" }, 1250 { mbrtoc16_null, "mbrtoc16: correctly handle null string (8859-15)", 1251 "en_US.ISO8859-15" }, 1252 { mbrtoc16_zero, "mbrtoc16: correctly handle L'\\0'" }, 1253 { mbrtoc16_zero, "mbrtoc16: correctly handle L'\\0' (C)", "C" }, 1254 { mbrtoc16_zero, "mbrtoc16: correctly handle L'\\0' (8859-15)" 1255 "en_US.ISO8859-15" }, 1256 { mbrtoc16_zero_len, "mbrtoc16: correctly handle length of zero" }, 1257 { mbrtoc16_zero_len, "mbrtoc16: correctly handle length of zero (C)", 1258 "C" }, 1259 { mbrtoc16_zero_len, "mbrtoc16: correctly handle length of zero " 1260 "(8859-15)", "en_US.ISO8859-15" }, 1261 { mbrtoc16_roundtrip, "mbrtoc16: round trip conversion" }, 1262 { mbrtoc16_partial, "mbrtoc16: correctly consume partial sequences" }, 1263 { mbrtoc16_surrogate, "mbrtoc16: correctly generate surrogate pairs " 1264 "and round trip conversion" }, 1265 { c32rtomb_eilseq_iso8859, "c32rtomb: character outside of locale is " 1266 "caught (8859-15)", "en_US.ISO8859-15" }, 1267 { c16rtomb_eilseq_iso8859, "c16rtomb: character outside of locale is " 1268 "caught (8859-1)", "en_US.ISO8859-1" }, 1269 { c32rtomb_eilseq_utf8, "c32rtomb: character outside of locale is " 1270 "caught (UTF-8)" }, 1271 { c16rtomb_bad_first, "c16rtomb: bad first surrogate pair" }, 1272 { c16rtomb_bad_second, "c16rtomb: bad second surrogate pair" }, 1273 { c32rtomb_null, "c32rtomb: correctly handle null buffer" }, 1274 { c32rtomb_null_euro, "c32rtomb: correctly handle null buffer " 1275 "(8859-15)", "en_US.ISO8859-15" }, 1276 { c16rtomb_null, "c16rtomb: correctly handle null buffer" }, 1277 { c16rtomb_null_euro, "c16rtomb: correctly handle null buffer " 1278 "(8859-15)", "en_US.ISO8859-15" }, 1279 { c32rtomb_iconv_ascii, "c32rtomb: correctly convert data from C", 1280 "C" }, 1281 { c32rtomb_iconv_euro, "c32rtomb: correctly convert data from 8859-15", 1282 "en_US.ISO8859-15" }, 1283 { mbrtoc8_roundtrip_1b, "mbrtoc8 roundtrip ascii" }, 1284 { mbrtoc8_roundtrip_1b, "mbrtoc8 roundtrip ascii (C)", "C" }, 1285 { mbrtoc8_roundtrip_1b, "mbrtoc8 roundtrip ascii (8859-1)", 1286 "en_US.ISO8859-1" }, 1287 { mbrtoc8_roundtrip_2b, "mbrtoc8 roundtrip 2b (UTF-8)" }, 1288 { mbrtoc8_roundtrip_3b, "mbrtoc8 roundtrip 3b (UTF-8)" }, 1289 { mbrtoc8_roundtrip_4b, "mbrtoc8 roundtrip 4b (UTF-8)" }, 1290 { mbrtoc8_roundtrip_euro, "mbrtoc8 roundtrip conv 8859-15", 1291 "en_US.ISO8859-15" }, 1292 { c8rtomb_bad_utf8, "invalid UTF-8 sequences" }, 1293 /* Even if these are representable, they should still fail */ 1294 { c8rtomb_bad_utf8, "invalid UTF-8 sequences (C)", "C" }, 1295 { mbrtoc8_null, "mbrtoc8: correctly handle null string" }, 1296 { mbrtoc8_null, "mbrtoc8: correctly handle null string (C)", "C" }, 1297 { mbrtoc8_null, "mbrtoc8: correctly handle null string (8859-15)", 1298 "en_US.ISO8859-15" }, 1299 { c8rtomb_null, "c8rtomb: correctly handle null string" }, 1300 { c8rtomb_null, "c8rtomb: correctly handle null string (C)", "C" }, 1301 { c8rtomb_null, "c8rtomb: correctly handle null string (8859-15)", 1302 "en_US.ISO8859-15" }, 1303 { mbrtoc8_zero, "mbrtoc8: correctly handle '\\0' string" }, 1304 { mbrtoc8_zero, "mbrtoc8: correctly handle '\\0' string (C)", "C" }, 1305 { mbrtoc8_zero, "mbrtoc8: correctly handle '\\0' string (8859-15)", 1306 "en_US.ISO8859-15" }, 1307 1308 { mbrtoc8_zero_len, "mbrtoc8: correctly handle zero length string" }, 1309 { mbrtoc8_zero_len, "mbrtoc8: correctly handle zero length string (C)", 1310 "C" }, 1311 { mbrtoc8_zero_len, "mbrtoc8: correctly handle zero length string " 1312 "(8859-15)", "en_US.ISO8859-15" }, 1313 }; 1314 1315 int 1316 main(void) 1317 { 1318 uint_t i; 1319 uint_t passes = 0; 1320 uint_t ntests = ARRAY_SIZE(uchar_tests); 1321 1322 for (i = 0; i < ntests; i++) { 1323 boolean_t r; 1324 1325 /* 1326 * Default to a standard UTF-8 locale if none is requested by 1327 * the test. 1328 */ 1329 if (uchar_tests[i].ut_locale != NULL) { 1330 update_locale(uchar_tests[i].ut_locale); 1331 } else { 1332 update_locale("en_US.UTF-8"); 1333 } 1334 1335 r = uchar_tests[i].ut_func(); 1336 (void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED", 1337 uchar_tests[i].ut_test); 1338 if (r) { 1339 passes++; 1340 } 1341 } 1342 1343 (void) printf("%d/%d test%s passed\n", passes, ntests, 1344 passes > 1 ? "s" : ""); 1345 return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE); 1346 1347 } 1348