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
test_locale(char * name,char * number)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
main(void)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