xref: /illumos-gate/usr/src/test/libc-tests/tests/getlocname.c (revision 004345e48064ccd168d15f66eba2031c6090ccee)
1*004345e4SRobert Mustacchi /*
2*004345e4SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*004345e4SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*004345e4SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*004345e4SRobert Mustacchi  * 1.0 of the CDDL.
6*004345e4SRobert Mustacchi  *
7*004345e4SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*004345e4SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*004345e4SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*004345e4SRobert Mustacchi  */
11*004345e4SRobert Mustacchi 
12*004345e4SRobert Mustacchi /*
13*004345e4SRobert Mustacchi  * Copyright 2025 Oxide Computer Company
14*004345e4SRobert Mustacchi  */
15*004345e4SRobert Mustacchi 
16*004345e4SRobert Mustacchi /*
17*004345e4SRobert Mustacchi  * Tests for getlocalename_l(3C). The libc tests depends on locale/ar,
18*004345e4SRobert Mustacchi  * locale/de, locale/en, and locale/ja. We limit ourselves to these locales,
19*004345e4SRobert Mustacchi  * plus C.UTF-8. We use a combination of global locales set via setlocale(),
20*004345e4SRobert Mustacchi  * locales set with uselocale(), and composite locale.
21*004345e4SRobert Mustacchi  */
22*004345e4SRobert Mustacchi 
23*004345e4SRobert Mustacchi #include <err.h>
24*004345e4SRobert Mustacchi #include <stdlib.h>
25*004345e4SRobert Mustacchi #include <locale.h>
26*004345e4SRobert Mustacchi #include <sys/sysmacros.h>
27*004345e4SRobert Mustacchi #include <sys/debug.h>
28*004345e4SRobert Mustacchi #include <stdbool.h>
29*004345e4SRobert Mustacchi #include <string.h>
30*004345e4SRobert Mustacchi 
31*004345e4SRobert Mustacchi typedef struct {
32*004345e4SRobert Mustacchi 	int lc_cat;
33*004345e4SRobert Mustacchi 	const char *lc_name;
34*004345e4SRobert Mustacchi } lc_cat_t;
35*004345e4SRobert Mustacchi 
36*004345e4SRobert Mustacchi static const lc_cat_t lc_cats[] = {
37*004345e4SRobert Mustacchi 	{ LC_CTYPE, "LC_CTYPE" },
38*004345e4SRobert Mustacchi 	{ LC_NUMERIC, "LC_NUMERIC" },
39*004345e4SRobert Mustacchi 	{ LC_TIME, "LC_TIME" },
40*004345e4SRobert Mustacchi 	{ LC_COLLATE, "LC_COLLATE" },
41*004345e4SRobert Mustacchi 	{ LC_MONETARY, "LC_MONETARY" },
42*004345e4SRobert Mustacchi 	{ LC_MESSAGES, "LC_MESSAGES" },
43*004345e4SRobert Mustacchi 	{ LC_ALL, "LC_ALL" }
44*004345e4SRobert Mustacchi };
45*004345e4SRobert Mustacchi CTASSERT(ARRAY_SIZE(lc_cats) == LC_ALL + 1);
46*004345e4SRobert Mustacchi 
47*004345e4SRobert Mustacchi typedef struct {
48*004345e4SRobert Mustacchi 	int lc_cat;
49*004345e4SRobert Mustacchi 	int lc_mask;
50*004345e4SRobert Mustacchi 	const char *lc_loc;
51*004345e4SRobert Mustacchi } lc_comp_t;
52*004345e4SRobert Mustacchi 
53*004345e4SRobert Mustacchi static const lc_comp_t composite[] = {
54*004345e4SRobert Mustacchi 	{ LC_CTYPE, LC_CTYPE_MASK, "en_US.UTF-8" },
55*004345e4SRobert Mustacchi 	{ LC_NUMERIC, LC_NUMERIC_MASK, "en_GB.UTF-8" },
56*004345e4SRobert Mustacchi 	{ LC_TIME, LC_TIME_MASK, "de_DE.UTF-8" },
57*004345e4SRobert Mustacchi 	{ LC_COLLATE, LC_COLLATE_MASK, "ar_EG.UTF-8" },
58*004345e4SRobert Mustacchi 	{ LC_MONETARY, LC_MONETARY_MASK, "ja_JP.UTF-8" },
59*004345e4SRobert Mustacchi 	{ LC_MESSAGES, LC_MESSAGES_MASK, "C.UTF-8" }
60*004345e4SRobert Mustacchi };
61*004345e4SRobert Mustacchi 
62*004345e4SRobert Mustacchi static const char *locales[] = { "C.UTF-8", "ja_JP.UTF-8", "de_DE.UTF-8",
63*004345e4SRobert Mustacchi 	"en_US.UTF-8", "en_GB.UTF-8" };
64*004345e4SRobert Mustacchi 
65*004345e4SRobert Mustacchi /*
66*004345e4SRobert Mustacchi  * Check each category of a locale. These are ordered in exp by their LC_ type
67*004345e4SRobert Mustacchi  * category.
68*004345e4SRobert Mustacchi  */
69*004345e4SRobert Mustacchi static bool
locname_check(const char * desc,locale_t loc,const char ** exp_names)70*004345e4SRobert Mustacchi locname_check(const char *desc, locale_t loc, const char **exp_names)
71*004345e4SRobert Mustacchi {
72*004345e4SRobert Mustacchi 	bool ret = true;
73*004345e4SRobert Mustacchi 
74*004345e4SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(lc_cats); i++) {
75*004345e4SRobert Mustacchi 		const char *catname = lc_cats[i].lc_name;
76*004345e4SRobert Mustacchi 		const char *exp = exp_names[lc_cats[i].lc_cat];
77*004345e4SRobert Mustacchi 		const char *name = getlocalename_l(lc_cats[i].lc_cat, loc);
78*004345e4SRobert Mustacchi 		if (name == NULL) {
79*004345e4SRobert Mustacchi 			warnx("TEST FAILED: %s: failed to get locale name for "
80*004345e4SRobert Mustacchi 			    "category %s: expected %s", desc, catname, exp);
81*004345e4SRobert Mustacchi 			ret = false;
82*004345e4SRobert Mustacchi 			continue;
83*004345e4SRobert Mustacchi 		}
84*004345e4SRobert Mustacchi 
85*004345e4SRobert Mustacchi 		if (strcmp(name, exp) != 0) {
86*004345e4SRobert Mustacchi 			warnx("TEST FAILED: %s: got incorrect value for "
87*004345e4SRobert Mustacchi 			    "category %s: expected %s, but got %s", desc,
88*004345e4SRobert Mustacchi 			    catname, exp, name);
89*004345e4SRobert Mustacchi 			ret = false;
90*004345e4SRobert Mustacchi 		} else {
91*004345e4SRobert Mustacchi 			(void) printf("TEST PASSED: %s: category %s\n", desc,
92*004345e4SRobert Mustacchi 			    catname);
93*004345e4SRobert Mustacchi 		}
94*004345e4SRobert Mustacchi 	}
95*004345e4SRobert Mustacchi 
96*004345e4SRobert Mustacchi 	return (ret);
97*004345e4SRobert Mustacchi }
98*004345e4SRobert Mustacchi 
99*004345e4SRobert Mustacchi /*
100*004345e4SRobert Mustacchi  * A small wrapper for names that are uniform.
101*004345e4SRobert Mustacchi  */
102*004345e4SRobert Mustacchi static bool
locname_check_all(const char * desc,locale_t loc,const char * exp)103*004345e4SRobert Mustacchi locname_check_all(const char *desc, locale_t loc, const char *exp)
104*004345e4SRobert Mustacchi {
105*004345e4SRobert Mustacchi 	const char *names[LC_ALL + 1] = { exp, exp, exp, exp, exp, exp, exp };
106*004345e4SRobert Mustacchi 
107*004345e4SRobert Mustacchi 	return (locname_check(desc, loc, names));
108*004345e4SRobert Mustacchi }
109*004345e4SRobert Mustacchi 
110*004345e4SRobert Mustacchi /*
111*004345e4SRobert Mustacchi  * Change each locale category one at a time and ensure that we get the expected
112*004345e4SRobert Mustacchi  * locale. Then set it as the global locale and ensure that this is what we
113*004345e4SRobert Mustacchi  * expect. We start from C. We track the names based on an array indexed by the
114*004345e4SRobert Mustacchi  * different categories and use the fact that they're ordered to know what to
115*004345e4SRobert Mustacchi  * check against.
116*004345e4SRobert Mustacchi  *
117*004345e4SRobert Mustacchi  * We also test setting the global locale to the result of this to ensure that
118*004345e4SRobert Mustacchi  * the results of composite locales are usable.
119*004345e4SRobert Mustacchi  */
120*004345e4SRobert Mustacchi static bool
locname_composite(void)121*004345e4SRobert Mustacchi locname_composite(void)
122*004345e4SRobert Mustacchi {
123*004345e4SRobert Mustacchi 	bool ret = true;
124*004345e4SRobert Mustacchi 	const char *names[LC_ALL + 1] = { "C", "C", "C", "C", "C", "C", NULL };
125*004345e4SRobert Mustacchi 	locale_t loc = newlocale(LC_ALL_MASK, "C", NULL);
126*004345e4SRobert Mustacchi 
127*004345e4SRobert Mustacchi 	if (loc == NULL) {
128*004345e4SRobert Mustacchi 		errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to create "
129*004345e4SRobert Mustacchi 		    "base C locale for composite tests");
130*004345e4SRobert Mustacchi 	}
131*004345e4SRobert Mustacchi 
132*004345e4SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(composite); i++) {
133*004345e4SRobert Mustacchi 		char cname[1024], desc[1024];
134*004345e4SRobert Mustacchi 
135*004345e4SRobert Mustacchi 		names[composite[i].lc_cat] = composite[i].lc_loc;
136*004345e4SRobert Mustacchi 		(void) snprintf(cname, sizeof (cname), "%s/%s/%s/%s/%s/%s",
137*004345e4SRobert Mustacchi 		    names[0], names[1], names[2], names[3], names[4], names[5]);
138*004345e4SRobert Mustacchi 		names[LC_ALL] = cname;
139*004345e4SRobert Mustacchi 
140*004345e4SRobert Mustacchi 		loc = newlocale(composite[i].lc_mask, composite[i].lc_loc,
141*004345e4SRobert Mustacchi 		    loc);
142*004345e4SRobert Mustacchi 		if (loc == NULL) {
143*004345e4SRobert Mustacchi 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
144*004345e4SRobert Mustacchi 			    "evolve composite locale to %s", cname);
145*004345e4SRobert Mustacchi 		}
146*004345e4SRobert Mustacchi 
147*004345e4SRobert Mustacchi 		(void) snprintf(desc, sizeof (desc), "composite %s (%d)",
148*004345e4SRobert Mustacchi 		    composite[i].lc_loc, composite[i].lc_cat);
149*004345e4SRobert Mustacchi 		if (!locname_check(desc, loc, names))
150*004345e4SRobert Mustacchi 			ret = false;
151*004345e4SRobert Mustacchi 
152*004345e4SRobert Mustacchi 		if (setlocale(LC_ALL, getlocalename_l(LC_ALL, loc)) == NULL) {
153*004345e4SRobert Mustacchi 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
154*004345e4SRobert Mustacchi 			    "set global locale to composite %s", cname);
155*004345e4SRobert Mustacchi 		}
156*004345e4SRobert Mustacchi 
157*004345e4SRobert Mustacchi 		(void) snprintf(desc, sizeof (desc), "global composite %s (%d)",
158*004345e4SRobert Mustacchi 		    composite[i].lc_loc, composite[i].lc_cat);
159*004345e4SRobert Mustacchi 		if (!locname_check(desc, loc, names))
160*004345e4SRobert Mustacchi 			ret = false;
161*004345e4SRobert Mustacchi 	}
162*004345e4SRobert Mustacchi 	freelocale(loc);
163*004345e4SRobert Mustacchi 
164*004345e4SRobert Mustacchi 	return (ret);
165*004345e4SRobert Mustacchi }
166*004345e4SRobert Mustacchi 
167*004345e4SRobert Mustacchi static bool
locname_invalid(void)168*004345e4SRobert Mustacchi locname_invalid(void)
169*004345e4SRobert Mustacchi {
170*004345e4SRobert Mustacchi 	bool ret = true;
171*004345e4SRobert Mustacchi 
172*004345e4SRobert Mustacchi 	if (getlocalename_l(LC_ALL, NULL) != NULL) {
173*004345e4SRobert Mustacchi 		ret = false;
174*004345e4SRobert Mustacchi 		warnx("TEST FAILED: passing invalid locale: string incorrectly "
175*004345e4SRobert Mustacchi 		    "returned");
176*004345e4SRobert Mustacchi 	} else {
177*004345e4SRobert Mustacchi 		(void) printf("TEST PASSED: invalid locale\n");
178*004345e4SRobert Mustacchi 	}
179*004345e4SRobert Mustacchi 
180*004345e4SRobert Mustacchi 	if (getlocalename_l(12345, LC_GLOBAL_LOCALE) != NULL) {
181*004345e4SRobert Mustacchi 		ret = false;
182*004345e4SRobert Mustacchi 		warnx("TEST FAILED: passing invalid category (1): string "
183*004345e4SRobert Mustacchi 		    "incorrectly returned");
184*004345e4SRobert Mustacchi 	} else {
185*004345e4SRobert Mustacchi 		(void) printf("TEST PASSED: invalid category (1)\n");
186*004345e4SRobert Mustacchi 	}
187*004345e4SRobert Mustacchi 
188*004345e4SRobert Mustacchi 	if (getlocalename_l(0x7777, uselocale(NULL)) != NULL) {
189*004345e4SRobert Mustacchi 		ret = false;
190*004345e4SRobert Mustacchi 		warnx("TEST FAILED: passing invalid category (2): string "
191*004345e4SRobert Mustacchi 		    "incorrectly returned");
192*004345e4SRobert Mustacchi 	} else {
193*004345e4SRobert Mustacchi 		(void) printf("TEST PASSED: invalid category (2)\n");
194*004345e4SRobert Mustacchi 	}
195*004345e4SRobert Mustacchi 
196*004345e4SRobert Mustacchi 	return (ret);
197*004345e4SRobert Mustacchi }
198*004345e4SRobert Mustacchi 
199*004345e4SRobert Mustacchi int
main(void)200*004345e4SRobert Mustacchi main(void)
201*004345e4SRobert Mustacchi {
202*004345e4SRobert Mustacchi 	int ret = EXIT_SUCCESS;
203*004345e4SRobert Mustacchi 
204*004345e4SRobert Mustacchi 	/*
205*004345e4SRobert Mustacchi 	 * The default locale should be C.
206*004345e4SRobert Mustacchi 	 */
207*004345e4SRobert Mustacchi 	if (!locname_check_all("global locale: default", LC_GLOBAL_LOCALE, "C"))
208*004345e4SRobert Mustacchi 		ret = EXIT_FAILURE;
209*004345e4SRobert Mustacchi 
210*004345e4SRobert Mustacchi 	/*
211*004345e4SRobert Mustacchi 	 * Test non-composite locales. We always do the thread-specific locale
212*004345e4SRobert Mustacchi 	 * first so that way we can catch an erroneous locale.
213*004345e4SRobert Mustacchi 	 */
214*004345e4SRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(locales); i++) {
215*004345e4SRobert Mustacchi 		char desc[1024];
216*004345e4SRobert Mustacchi 		locale_t loc;
217*004345e4SRobert Mustacchi 
218*004345e4SRobert Mustacchi 		loc = newlocale(LC_ALL_MASK, locales[i], NULL);
219*004345e4SRobert Mustacchi 		if (loc == NULL) {
220*004345e4SRobert Mustacchi 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
221*004345e4SRobert Mustacchi 			    "construct locale %s", locales[i]);
222*004345e4SRobert Mustacchi 		}
223*004345e4SRobert Mustacchi 
224*004345e4SRobert Mustacchi 		(void) snprintf(desc, sizeof (desc),
225*004345e4SRobert Mustacchi 		    "%s: newlocale", locales[i]);
226*004345e4SRobert Mustacchi 		if (!locname_check_all(desc, loc, locales[i]))
227*004345e4SRobert Mustacchi 			ret = EXIT_FAILURE;
228*004345e4SRobert Mustacchi 
229*004345e4SRobert Mustacchi 		(void) snprintf(desc, sizeof (desc),
230*004345e4SRobert Mustacchi 		    "%s: thread locale", locales[i]);
231*004345e4SRobert Mustacchi 		if (uselocale(loc) == NULL) {
232*004345e4SRobert Mustacchi 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
233*004345e4SRobert Mustacchi 			    "transition thread to thread-specific locale %s, "
234*004345e4SRobert Mustacchi 			    "cannot continue", locales[i]);
235*004345e4SRobert Mustacchi 		}
236*004345e4SRobert Mustacchi 		if (!locname_check_all(desc, uselocale(NULL), locales[i]))
237*004345e4SRobert Mustacchi 			ret = EXIT_FAILURE;
238*004345e4SRobert Mustacchi 		if (uselocale(LC_GLOBAL_LOCALE) == NULL) {
239*004345e4SRobert Mustacchi 			err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
240*004345e4SRobert Mustacchi 			    "transition thread back to global locale, cannot "
241*004345e4SRobert Mustacchi 			    "continue");
242*004345e4SRobert Mustacchi 		}
243*004345e4SRobert Mustacchi 		freelocale(loc);
244*004345e4SRobert Mustacchi 
245*004345e4SRobert Mustacchi 		(void) snprintf(desc, sizeof (desc),
246*004345e4SRobert Mustacchi 		    "%s: global locale", locales[i]);
247*004345e4SRobert Mustacchi 		if (setlocale(LC_ALL, locales[i]) == NULL) {
248*004345e4SRobert Mustacchi 			errx(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to "
249*004345e4SRobert Mustacchi 			    "set global locale to %s", locales[i]);
250*004345e4SRobert Mustacchi 		}
251*004345e4SRobert Mustacchi 		if (!locname_check_all(desc, LC_GLOBAL_LOCALE, locales[i]))
252*004345e4SRobert Mustacchi 			ret = EXIT_FAILURE;
253*004345e4SRobert Mustacchi 	}
254*004345e4SRobert Mustacchi 
255*004345e4SRobert Mustacchi 	if (!locname_composite())
256*004345e4SRobert Mustacchi 		ret = EXIT_FAILURE;
257*004345e4SRobert Mustacchi 
258*004345e4SRobert Mustacchi 	if (!locname_invalid())
259*004345e4SRobert Mustacchi 		ret = EXIT_FAILURE;
260*004345e4SRobert Mustacchi 
261*004345e4SRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
262*004345e4SRobert Mustacchi 		(void) printf("All tests completed successfully\n");
263*004345e4SRobert Mustacchi 	}
264*004345e4SRobert Mustacchi 
265*004345e4SRobert Mustacchi 	return (ret);
266*004345e4SRobert Mustacchi }
267