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 2020 Robert Mustacchi 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 30 static const char *uchar_wide = "光"; 31 static const char32_t uchar_value = 0x5149; 32 static const char *uchar_hello = "hello"; 33 34 static void 35 update_locale(const char *loc) 36 { 37 const char *newloc = setlocale(LC_CTYPE, loc); 38 if (newloc == NULL) { 39 err(EXIT_FAILURE, "TEST FAILED: failed to update locale to %s", 40 loc); 41 } 42 43 if (strcmp(newloc, loc) != 0) { 44 errx(EXIT_FAILURE, "TEST FAILED: locale set to %s, but got %s", 45 loc, newloc); 46 } 47 } 48 49 static boolean_t 50 mbrtoc32_ascii(mbstate_t *mbs) 51 { 52 char32_t out; 53 size_t len; 54 boolean_t ret = B_TRUE; 55 56 if ((len = mbrtoc32(&out, uchar_hello, 5, mbs)) != 1) { 57 warnx("expected mbrtoc32 to return 1, returned %zu", len); 58 ret = B_FALSE; 59 } 60 61 if (out != 'h') { 62 warnx("got bad char32_t, expected 0x%x, found 0x%x\n", 'h', 63 out); 64 ret = B_FALSE; 65 } 66 67 if ((len = mbrtoc32(&out, uchar_hello + 1, 4, mbs)) != 1) { 68 warnx("expected mbrtoc32 to return 1, returned %zu", len); 69 ret = B_FALSE; 70 } 71 72 if (out != 'e') { 73 warnx("got bad char32_t, expected 0x%x, found 0x%x\n", 'h', 74 out); 75 ret = B_FALSE; 76 } 77 78 return (ret); 79 } 80 81 static boolean_t 82 mbrtoc32_ascii_internal(void) 83 { 84 return (mbrtoc32_ascii(NULL)); 85 } 86 87 static boolean_t 88 mbrtoc32_ascii_mbstate(void) 89 { 90 mbstate_t mbs; 91 92 bzero(&mbs, sizeof (mbs)); 93 return (mbrtoc32_ascii(&mbs)); 94 } 95 96 static boolean_t 97 mbrtoc32_badseq_utf8(void) 98 { 99 mbstate_t mbs; 100 size_t len; 101 char32_t out; 102 boolean_t ret = B_TRUE; 103 char *badstr; 104 105 bzero(&mbs, sizeof (mbs)); 106 len = mbrtoc32(&out, "\xa9", 1, &mbs); 107 if (len != (size_t)-1) { 108 warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1); 109 ret = B_FALSE; 110 } 111 112 if (errno != EILSEQ) { 113 warnx("found bad errno, expected %d, found %d\n", errno, 114 EILSEQ); 115 ret = B_FALSE; 116 } 117 118 badstr = strdup(uchar_wide); 119 if (badstr == NULL) { 120 warn("failed to duplicate uchar_wide"); 121 return (B_FALSE); 122 } 123 124 badstr[1] = '?'; 125 bzero(&mbs, sizeof (mbs)); 126 len = mbrtoc32(&out, badstr, strlen(badstr), &mbs); 127 free(badstr); 128 if (len != (size_t)-1) { 129 warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1); 130 ret = B_FALSE; 131 } 132 133 if (errno != EILSEQ) { 134 warnx("found bad errno, expected %d, found %d\n", errno, 135 EILSEQ); 136 ret = B_FALSE; 137 } 138 139 return (ret); 140 } 141 142 static boolean_t 143 mbrtoc32_roundtrip(void) 144 { 145 char32_t out; 146 size_t len, clen; 147 mbstate_t mbs; 148 char buf[MB_CUR_MAX]; 149 boolean_t ret = B_TRUE; 150 151 bzero(&mbs, sizeof (mbs)); 152 len = mbrtoc32(&out, uchar_wide, strlen(uchar_wide), &mbs); 153 if (len != 3) { 154 warnx("mbrtoc32 returned %zu, expected %u", len, 3); 155 ret = B_FALSE; 156 } 157 158 if (out != uchar_value) { 159 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 160 out, uchar_value); 161 ret = B_FALSE; 162 } 163 164 clen = c32rtomb(buf, out, &mbs); 165 if (clen != len) { 166 warnx("c32rtomb returned %d bytes, but we originally used %d", 167 clen, len); 168 ret = B_FALSE; 169 } 170 171 if (strncmp(buf, uchar_wide, len) != 0) { 172 warnx("round trip string comparison failed"); 173 ret = B_FALSE; 174 } 175 176 return (ret); 177 } 178 179 static boolean_t 180 mbrtoc32_partial(void) 181 { 182 char32_t out; 183 size_t len, i; 184 mbstate_t mbs; 185 boolean_t ret = B_TRUE; 186 187 bzero(&mbs, sizeof (mbs)); 188 for (i = 0; i < strlen(uchar_wide) - 1; i++) { 189 len = mbrtoc32(&out, uchar_wide + i, 1, &mbs); 190 if (len != (size_t)-2) { 191 warnx("partial mbrtoc32 returned %zu, not -2", len); 192 ret = B_FALSE; 193 } 194 } 195 196 len = mbrtoc32(&out, uchar_wide + i, 1, &mbs); 197 if (len != 1) { 198 warnx("partial mbrtoc32 returned %zu, not 1", len); 199 ret = B_FALSE; 200 } 201 202 if (out != uchar_value) { 203 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 204 out, uchar_value); 205 ret = B_FALSE; 206 } 207 208 return (ret); 209 } 210 211 static boolean_t 212 mbrtoc32_zero(void) 213 { 214 char32_t out, exp = L'\0'; 215 size_t len; 216 mbstate_t mbs; 217 boolean_t ret = B_TRUE; 218 219 bzero(&mbs, sizeof (mbs)); 220 len = mbrtoc32(&out, "", 1, &mbs); 221 if (len != 0) { 222 warnx("partial mbrtoc32 returned %zu, not 0", len); 223 ret = B_FALSE; 224 } 225 226 if (out != exp) { 227 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 228 out, exp); 229 ret = B_FALSE; 230 } 231 232 return (ret); 233 } 234 235 static boolean_t 236 mbrtoc32_zero_len(void) 237 { 238 char32_t out = 0x12345, exp = 0x12345; 239 size_t len; 240 mbstate_t mbs; 241 boolean_t ret = B_TRUE; 242 243 bzero(&mbs, sizeof (mbs)); 244 len = mbrtoc32(&out, uchar_wide, 0, &mbs); 245 if (len != (size_t)-2) { 246 warnx("partial mbrtoc32 returned %zu, not -2", len); 247 ret = B_FALSE; 248 } 249 250 if (out != exp) { 251 warnx("mbrtoc32 incorrectly wrote to char32_t value with " 252 "zero string, found 0x%x not 0x%x", out, exp); 253 ret = B_FALSE; 254 } 255 256 return (ret); 257 } 258 259 static boolean_t 260 mbrtoc32_null(void) 261 { 262 char32_t out = 0x123456, exp = 0x123456; 263 size_t len; 264 mbstate_t mbs; 265 boolean_t ret = B_TRUE; 266 267 bzero(&mbs, sizeof (mbs)); 268 len = mbrtoc32(&out, NULL, 1, &mbs); 269 if (len != 0) { 270 warnx("partial mbrtoc32 returned %zu, not 0", len); 271 ret = B_FALSE; 272 } 273 274 if (out != exp) { 275 warnx("mbrtoc32 incorrectly wrote to char32_t value with " 276 "null string, found 0x%x not 0x%x", out, exp); 277 ret = B_FALSE; 278 } 279 280 return (ret); 281 } 282 283 static boolean_t 284 mbrtoc16_ascii(mbstate_t *mbs) 285 { 286 char16_t out; 287 size_t len; 288 boolean_t ret = B_TRUE; 289 290 if ((len = mbrtoc16(&out, uchar_hello, 5, mbs)) != 1) { 291 warnx("expected mbrtoc16 to return 1, returned %zu", len); 292 ret = B_FALSE; 293 } 294 295 if (out != 'h') { 296 warnx("got bad char16_t, expected 0x%x, found 0x%x\n", 'h', 297 out); 298 ret = B_FALSE; 299 } 300 301 if ((len = mbrtoc16(&out, uchar_hello + 1, 4, mbs)) != 1) { 302 warnx("expected mbrtoc16 to return 1, returned %zu", len); 303 ret = B_FALSE; 304 } 305 306 if (out != 'e') { 307 warnx("got bad char16_t, expected 0x%x, found 0x%x\n", 'h', 308 out); 309 ret = B_FALSE; 310 } 311 312 return (ret); 313 } 314 315 static boolean_t 316 mbrtoc16_ascii_internal(void) 317 { 318 return (mbrtoc16_ascii(NULL)); 319 } 320 321 static boolean_t 322 mbrtoc16_ascii_mbstate(void) 323 { 324 mbstate_t mbs; 325 326 bzero(&mbs, sizeof (mbs)); 327 return (mbrtoc16_ascii(&mbs)); 328 } 329 330 static boolean_t 331 mbrtoc16_null(void) 332 { 333 char16_t out = 0x1234, exp = 0x1234; 334 size_t len; 335 mbstate_t mbs; 336 boolean_t ret = B_TRUE; 337 338 bzero(&mbs, sizeof (mbs)); 339 len = mbrtoc16(&out, NULL, 1, &mbs); 340 if (len != 0) { 341 warnx("partial mbrtoc16 returned %zu, not 0", len); 342 ret = B_FALSE; 343 } 344 345 if (out != exp) { 346 warnx("mbrtoc16 incorrectly wrote to char16_t value with " 347 "null string, found 0x%x not 0x%x", out, exp); 348 ret = B_FALSE; 349 } 350 351 return (ret); 352 } 353 354 static boolean_t 355 mbrtoc16_zero(void) 356 { 357 char16_t out, exp = L'\0'; 358 size_t len; 359 mbstate_t mbs; 360 boolean_t ret = B_TRUE; 361 362 bzero(&mbs, sizeof (mbs)); 363 len = mbrtoc16(&out, "", 1, &mbs); 364 if (len != 0) { 365 warnx("partial mbrtoc16 returned %zu, not 0", len); 366 ret = B_FALSE; 367 } 368 369 if (out != exp) { 370 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 371 out, exp); 372 ret = B_FALSE; 373 } 374 375 return (ret); 376 } 377 378 static boolean_t 379 mbrtoc16_zero_len(void) 380 { 381 char16_t out = 0x5432, exp = 0x5432; 382 size_t len; 383 mbstate_t mbs; 384 boolean_t ret = B_TRUE; 385 386 bzero(&mbs, sizeof (mbs)); 387 len = mbrtoc16(&out, uchar_wide, 0, &mbs); 388 if (len != (size_t)-2) { 389 warnx("partial mbrtoc16 returned %zu, not -2", len); 390 ret = B_FALSE; 391 } 392 393 if (out != exp) { 394 warnx("mbrtoc16 incorrectly wrote to char16_t value with " 395 "zero length string, found 0x%x not 0x%x", out, exp); 396 ret = B_FALSE; 397 } 398 399 return (ret); 400 } 401 402 static boolean_t 403 mbrtoc16_roundtrip(void) 404 { 405 char16_t out; 406 size_t len, clen; 407 mbstate_t mbs; 408 char buf[MB_CUR_MAX]; 409 boolean_t ret = B_TRUE; 410 411 bzero(&mbs, sizeof (mbs)); 412 len = mbrtoc16(&out, uchar_wide, strlen(uchar_wide), &mbs); 413 if (len != 3) { 414 warnx("mbrtoc16 returned %zu, expected %u", len, 3); 415 ret = B_FALSE; 416 } 417 418 if (out != uchar_value) { 419 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 420 out, uchar_value); 421 ret = B_FALSE; 422 } 423 424 clen = c16rtomb(buf, out, &mbs); 425 if (clen != len) { 426 warnx("c16rtomb returned %d bytes, but we originally used %d", 427 clen, len); 428 ret = B_FALSE; 429 } 430 431 if (strncmp(buf, uchar_wide, len) != 0) { 432 warnx("round trip string comparison failed"); 433 ret = B_FALSE; 434 } 435 436 return (ret); 437 } 438 439 static boolean_t 440 mbrtoc16_partial(void) 441 { 442 char16_t out; 443 size_t len, i; 444 mbstate_t mbs; 445 boolean_t ret = B_TRUE; 446 447 bzero(&mbs, sizeof (mbs)); 448 for (i = 0; i < strlen(uchar_wide) - 1; i++) { 449 len = mbrtoc16(&out, uchar_wide + i, 1, &mbs); 450 if (len != (size_t)-2) { 451 warnx("partial mbrtoc16 returned %zu, not -2", len); 452 ret = B_FALSE; 453 } 454 } 455 456 len = mbrtoc16(&out, uchar_wide + i, 1, &mbs); 457 if (len != 1) { 458 warnx("partial mbrtoc16 returned %zu, not 1", len); 459 ret = B_FALSE; 460 } 461 462 if (out != uchar_value) { 463 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 464 out, uchar_value); 465 ret = B_FALSE; 466 } 467 468 return (ret); 469 } 470 471 static boolean_t 472 mbrtoc16_surrogate(void) 473 { 474 char16_t out0, out1; 475 size_t len, clen; 476 mbstate_t mbs; 477 const char *surrogate = "\xF0\x9F\x92\xA9"; 478 char16_t exp0 = 0xd83d, exp1 = 0xdca9; 479 size_t slen = strlen(surrogate); 480 boolean_t ret = B_TRUE; 481 char buf[MB_CUR_MAX]; 482 483 bzero(&mbs, sizeof (mbs)); 484 len = mbrtoc16(&out0, surrogate, slen, &mbs); 485 if (len != slen) { 486 warnx("mbrtoc16 returned %zu, expected %u", len, slen); 487 ret = B_FALSE; 488 } 489 490 if (out0 != exp0) { 491 warnx("mbrtoc16 converted character to 0x%x not 0x%x", 492 out0, exp0); 493 ret = B_FALSE; 494 } 495 496 if (mbsinit(&mbs) != 0) { 497 warnx("mb state with a surrogate character is somehow in the " 498 "initial state"); 499 ret = B_FALSE; 500 } 501 502 len = mbrtoc16(&out1, uchar_wide, strlen(uchar_wide), &mbs); 503 if (len != (size_t)-3) { 504 warnx("mbrtoc16 returned %zu, expected -3", len); 505 ret = B_FALSE; 506 } 507 508 if (mbsinit(&mbs) == 0) { 509 warnx("mb state with after both surrogate characters isn't " 510 "in initial state"); 511 ret = B_FALSE; 512 } 513 514 if (out1 != exp1) { 515 warnx("mbrtoc32 converted character to 0x%x not 0x%x", 516 out1, exp1); 517 ret = B_FALSE; 518 } 519 520 clen = c16rtomb(buf, out0, &mbs); 521 if (clen != 0) { 522 warnx("c16rtomb returned %d bytes, but expected zero for the " 523 "first surrogate", clen); 524 ret = B_FALSE; 525 } 526 527 if (mbsinit(&mbs) != 0) { 528 warnx("mb state with a surrogate character is somehow in the " 529 "initial state"); 530 ret = B_FALSE; 531 } 532 533 clen = c16rtomb(buf, out1, &mbs); 534 if (clen != slen) { 535 warnx("c16rtomb returned %zd, expected %u", len, slen); 536 ret = B_FALSE; 537 } 538 539 if (mbsinit(&mbs) == 0) { 540 warnx("mb state with after both surrogate characters isn't " 541 "in initial state"); 542 ret = B_FALSE; 543 } 544 545 if (strncmp(buf, surrogate, slen) != 0) { 546 warnx("round trip string comparison failed"); 547 ret = B_FALSE; 548 } 549 550 return (ret); 551 } 552 553 static boolean_t 554 c32rtomb_eilseq_iso8859(void) 555 { 556 char buf[MB_CUR_MAX]; 557 mbstate_t mbs; 558 size_t len; 559 boolean_t ret = B_TRUE; 560 561 bzero(&mbs, sizeof (mbs)); 562 len = c32rtomb(buf, uchar_value, &mbs); 563 if (len != (size_t)-1) { 564 warnx("c32rtomb returned %zd, expected -1\n", len); 565 ret = B_FALSE; 566 } 567 568 if (errno != EILSEQ) { 569 warnx("expected errno set to %d was %d", EILSEQ, errno); 570 ret = B_FALSE; 571 } 572 573 return (ret); 574 } 575 576 static boolean_t 577 c16rtomb_eilseq_iso8859(void) 578 { 579 char buf[MB_CUR_MAX]; 580 mbstate_t mbs; 581 size_t len; 582 boolean_t ret = B_TRUE; 583 584 bzero(&mbs, sizeof (mbs)); 585 len = c32rtomb(buf, (char16_t)uchar_value, &mbs); 586 if (len != (size_t)-1) { 587 warnx("c32rtomb returned %zd, expected -1\n", len); 588 ret = B_FALSE; 589 } 590 591 if (errno != EILSEQ) { 592 warnx("expected errno set to %d was %d", EILSEQ, errno); 593 ret = B_FALSE; 594 } 595 596 return (ret); 597 } 598 599 static boolean_t 600 c32rtomb_eilseq_utf8(void) 601 { 602 char buf[MB_CUR_MAX]; 603 mbstate_t mbs; 604 size_t len; 605 boolean_t ret = B_TRUE; 606 607 bzero(&mbs, sizeof (mbs)); 608 len = c32rtomb(buf, UINT32_MAX, &mbs); 609 if (len != (size_t)-1) { 610 warnx("c32rtomb returned %zd, expected -1\n", len); 611 ret = B_FALSE; 612 } 613 614 if (errno != EILSEQ) { 615 warnx("expected errno set to %d was %d", EILSEQ, errno); 616 ret = B_FALSE; 617 } 618 619 return (ret); 620 } 621 622 static boolean_t 623 c16rtomb_bad_first(void) 624 { 625 char buf[MB_CUR_MAX]; 626 mbstate_t mbs; 627 size_t len, i; 628 char16_t first = 0xd83d; 629 char16_t bad[] = { 0x0, 0xd7ff, 0xd83d, 0xd900, 0xffff }; 630 boolean_t ret = B_TRUE; 631 632 for (i = 0; i < ARRAY_SIZE(bad); i++) { 633 bzero(&mbs, sizeof (mbs)); 634 len = c16rtomb(buf, first, &mbs); 635 if (len != 0) { 636 warnx("c16rtomb returned %zd, expected 0\n", len); 637 ret = B_FALSE; 638 } 639 640 len = c16rtomb(buf, bad[i], &mbs); 641 if (len != (size_t)-1) { 642 warnx("c16rtomb surrogate %x returned %zd, expected " 643 "-1\n", bad[i], len); 644 ret = B_FALSE; 645 } 646 647 if (errno != EILSEQ) { 648 warnx("expected errno set to %d was %d", EILSEQ, errno); 649 ret = B_FALSE; 650 } 651 } 652 653 return (ret); 654 } 655 656 static boolean_t 657 c16rtomb_bad_second(void) 658 { 659 char buf[MB_CUR_MAX]; 660 mbstate_t mbs; 661 size_t len, i; 662 char16_t bad[] = { 0xdc00, 0xdd34, 0xdfff }; 663 boolean_t ret = B_TRUE; 664 665 for (i = 0; i < ARRAY_SIZE(bad); i++) { 666 bzero(&mbs, sizeof (mbs)); 667 len = c16rtomb(buf, bad[i], &mbs); 668 if (len != (size_t)-1) { 669 warnx("c16rtomb surrogate %x returned %zd, expected " 670 "-1\n", bad[i], len); 671 ret = B_FALSE; 672 } 673 674 if (errno != EILSEQ) { 675 warnx("expected errno set to %d was %d", EILSEQ, errno); 676 ret = B_FALSE; 677 } 678 } 679 680 return (ret); 681 } 682 683 static boolean_t 684 c32rtomb_null(void) 685 { 686 size_t len; 687 mbstate_t mbs; 688 boolean_t ret = B_TRUE; 689 690 bzero(&mbs, sizeof (mbs)); 691 len = c32rtomb(NULL, uchar_value, &mbs); 692 if (len != 1) { 693 warnx("c32rtomb returned %zd, expected %zd", len, 1); 694 ret = B_FALSE; 695 } 696 697 return (ret); 698 } 699 700 static boolean_t 701 c16rtomb_null(void) 702 { 703 size_t len; 704 mbstate_t mbs; 705 boolean_t ret = B_TRUE; 706 707 bzero(&mbs, sizeof (mbs)); 708 len = c16rtomb(NULL, uchar_value, &mbs); 709 if (len != 1) { 710 warnx("c16rtomb returned %zd, expected %zd", len, 1); 711 ret = B_FALSE; 712 } 713 714 return (ret); 715 } 716 717 typedef boolean_t (*uchar_test_f)(void); 718 719 typedef struct uchar_test { 720 uchar_test_f ut_func; 721 const char *ut_test; 722 const char *ut_locale; 723 } uchar_test_t; 724 725 static const uchar_test_t uchar_tests[] = { 726 { mbrtoc32_ascii_mbstate, "mbrtoc32: ascii conversion" }, 727 { mbrtoc32_ascii_internal, "mbrtoc32: ascii conversion (internal " 728 "mbstate_t)" }, 729 { mbrtoc32_badseq_utf8, "mbrtoc32: bad locale sequence (UTF-8)" }, 730 { mbrtoc32_roundtrip, "mbrtoc32: round trip conversion" }, 731 { mbrtoc32_partial, "mbrtoc32: correctly consume partial sequences" }, 732 { mbrtoc32_zero, "mbrtoc32: correctly handle L'\\0'" }, 733 { mbrtoc32_zero_len, "mbrtoc32: correctly handle length of zero" }, 734 { mbrtoc32_null, "mbrtoc32: correctly handle null string" }, 735 { mbrtoc16_ascii_mbstate, "mbrtoc16: ascii conversion" }, 736 { mbrtoc16_ascii_internal, "mbrtoc16: ascii conversion (internal " 737 "mbstate_t)" }, 738 { mbrtoc16_null, "mbrtoc16: correctly handle null string" }, 739 { mbrtoc16_zero, "mbrtoc16: correctly handle L'\\0'" }, 740 { mbrtoc16_zero_len, "mbrtoc16: correctly handle length of zero" }, 741 { mbrtoc16_roundtrip, "mbrtoc16: round trip conversion" }, 742 { mbrtoc16_partial, "mbrtoc16: correctly consume partial sequences" }, 743 { mbrtoc16_surrogate, "mbrtoc16: correctly generate surrogate pairs " 744 "and round trip conversion" }, 745 { c32rtomb_eilseq_iso8859, "c32rtomb: character outside of locale is " 746 "caught", "en_US.ISO8859-1" }, 747 { c16rtomb_eilseq_iso8859, "c16rtomb: character outside of locale is " 748 "caught", "en_US.ISO8859-1" }, 749 { c32rtomb_eilseq_utf8, "c32rtomb: character outside of locale is " 750 "caught" }, 751 { c16rtomb_bad_first, "c16rtomb: bad first surrogate pair" }, 752 { c16rtomb_bad_second, "c16rtomb: bad second surrogate pair" }, 753 { c32rtomb_null, "c32rtomb: correctly handle null buffer" }, 754 { c16rtomb_null, "c16rtomb: correctly handle null buffer" }, 755 }; 756 757 int 758 main(void) 759 { 760 uint_t i; 761 uint_t passes = 0; 762 uint_t ntests = ARRAY_SIZE(uchar_tests); 763 764 for (i = 0; i < ntests; i++) { 765 boolean_t r; 766 767 /* 768 * Default to a standard UTF-8 locale if none is requested by 769 * the test. 770 */ 771 if (uchar_tests[i].ut_locale != NULL) { 772 update_locale(uchar_tests[i].ut_locale); 773 } else { 774 update_locale("en_US.UTF-8"); 775 } 776 777 r = uchar_tests[i].ut_func(); 778 (void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED", 779 uchar_tests[i].ut_test); 780 if (r) { 781 passes++; 782 } 783 } 784 785 (void) printf("%d/%d test%s passed\n", passes, ntests, 786 passes > 1 ? "s" : ""); 787 return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE); 788 789 } 790