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 2025 Oxide Computer Company 14 * Copyright 2025 Bill Sommerfeld 15 */ 16 17 /* 18 * Tests for locale interaction with strto{f,d,ld}(3C) and 19 * strto{f,d,ld}_l(3C). The libc tests depend on locale/ar, locale/de, 20 * locale/en, and locale/ja. We limit ourselves to these locales, plus 21 * C.UTF-8. 22 */ 23 24 #include <err.h> 25 #include <stdlib.h> 26 #include <xlocale.h> 27 #include <locale.h> 28 #include <sys/sysmacros.h> 29 #include <sys/debug.h> 30 #include <stdbool.h> 31 #include <string.h> 32 33 /* 34 * We use a value with a fractional part that can be represented exactly 35 * as a binary floating point number 36 */ 37 38 static struct test_locale { 39 char *name; 40 char *number; 41 } locales[] = { 42 { "C.UTF-8", "1.5"}, 43 {"ja_JP.UTF-8", "1.5"}, 44 {"de_DE.UTF-8", "1,5"}, 45 {"en_US.UTF-8", "1.5"}, 46 {"en_GB.UTF-8", "1.5" }, 47 }; 48 49 /* 50 * Test that the correct decimal point is recognized. 51 */ 52 bool 53 test_locale(char *name, char *number) 54 { 55 bool result_ok = true; 56 const float expected_f = 1.5; 57 const double expected_d = 1.5; 58 const long double expected_ld = 1.5; 59 char *expected_end = number + strlen(number); 60 char *actual_end; 61 float actual_f; 62 double actual_d; 63 long double actual_ld; 64 65 locale_t loc = newlocale(LC_ALL_MASK, name, NULL); 66 67 if (loc == NULL) { 68 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 69 "construct locale %s", name); 70 } 71 72 #define CHECK_END(fn) \ 73 if (actual_end != expected_end) { \ 74 warn("Locale %s, function %s: consumed %td characters" \ 75 " (%td expected)", name, #fn, \ 76 actual_end - number, expected_end - number); \ 77 result_ok = false; \ 78 } 79 80 actual_f = strtof_l(number, &actual_end, loc); 81 if (actual_f != expected_f) { 82 result_ok = false; 83 warn("Locale %s: strtof_l: mismatched value %f vs %f", 84 name, actual_f, expected_f); 85 } 86 CHECK_END(strtof_l); 87 88 actual_d = strtod_l(number, &actual_end, loc); 89 if (actual_d != expected_d) { 90 result_ok = false; 91 warn("Locale %s: strtod_l: mismatched value %f vs %f", 92 name, actual_d, expected_d); 93 } 94 CHECK_END(strtod_l); 95 96 actual_ld = strtold_l(number, &actual_end, loc); 97 if (actual_ld != expected_ld) { 98 result_ok = false; 99 warn("Locale %s: strtold_l: mismatched value %Lf vs %Lf", 100 name, actual_ld, expected_ld); 101 } 102 CHECK_END(strtold_l); 103 104 if (uselocale(loc) == NULL) { 105 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 106 "set locale %s", name); 107 } 108 109 actual_f = strtod(number, &actual_end); 110 if (actual_f != expected_f) { 111 result_ok = false; 112 warn("Locale %s: strtof: mismatched value %f vs %f", 113 name, actual_f, expected_f); 114 } 115 CHECK_END(strtof); 116 117 actual_d = strtod(number, &actual_end); 118 if (actual_d != expected_d) { 119 result_ok = false; 120 warn("Locale %s: strtod: mismatched value %f vs %f", 121 name, actual_d, expected_d); 122 } 123 CHECK_END(strtod); 124 125 actual_ld = strtold(number, &actual_end); 126 if (actual_ld != expected_ld) { 127 result_ok = false; 128 warn("Locale %s: strtold: mismatched value %Lf vs %Lf", 129 name, actual_ld, expected_ld); 130 } 131 CHECK_END(strtold); 132 133 return (result_ok); 134 } 135 136 137 int 138 main(void) 139 { 140 int ret = EXIT_SUCCESS; 141 142 for (size_t i = 0; i < ARRAY_SIZE(locales); i++) { 143 if (!test_locale(locales[i].name, locales[i].number)) 144 ret = EXIT_FAILURE; 145 } 146 147 if (ret == EXIT_SUCCESS) { 148 (void) printf("All tests completed successfully\n"); 149 } 150 151 return (ret); 152 } 153