xref: /haiku/src/tests/system/libroot/posix/locale_test.cpp (revision d3ff06683af390a4c2e83b69177e0a2eb76679bc)
1 /*
2  * Copyright 2010, Oliver Tappe, zooey@hirschkaefer.de
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <ctype.h>
7 #include <errno.h>
8 #include <langinfo.h>
9 #include <locale.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <time.h>
14 #include <wctype.h>
15 
16 
17 // #pragma mark - setlocale ----------------------------------------------------
18 
19 
20 void
21 test_setlocale()
22 {
23 	const char* locales[] = {
24 		"POSIX",
25 		"C",
26 		"de_DE",
27 		"en_US",
28 		"en_US.US-ASCII",
29 		"hr_HR.ISO-8859-2",
30 		"nl_NL",
31 		"nb_NO",
32 		"fr_FR.UTF-8@collation=phonebook",
33 		"de_DE.iso8859-1",
34 		"De_dE.IsO8859-15",
35 		"de_DE.utf8",
36 		"de_DE.UTF-8",
37 		"de_DE@euro",
38 		"de_DE@EURO",
39 		"de_DE.utf-8@Euro",
40 		"POSIX",
41 		"C",
42 		NULL
43 	};
44 	const char* expectedLocales[] = {
45 		"POSIX",
46 		"POSIX",
47 		"de_DE",
48 		"en_US",
49 		"en_US.US-ASCII",
50 		"hr_HR.ISO-8859-2",
51 		"nl_NL",
52 		"nb_NO",
53 		"fr_FR.UTF-8@collation=phonebook",
54 		"de_DE.iso8859-1",
55 		"De_dE.IsO8859-15",
56 		"de_DE.utf8",
57 		"de_DE.UTF-8",
58 		"de_DE@euro",
59 		"de_DE@EURO",
60 		"de_DE.utf-8@Euro",
61 		"POSIX",
62 		"POSIX"
63 	};
64 	const char* categoryNames[] = {
65 		"LC_ALL",
66 		"LC_COLLATE",
67 		"LC_CTYPE",
68 		"LC_MONETARY",
69 		"LC_NUMERIC",
70 		"LC_TIME",
71 		"LC_MESSAGES"
72 	};
73 	printf("setlocale()\n");
74 
75 	int problemCount = 0;
76 	for (int i = 0; locales[i] != NULL; ++i) {
77 		char* result = setlocale(LC_ALL, locales[i]);
78 		if (!result || strcmp(result, expectedLocales[i]) != 0) {
79 			printf("\tPROBLEM: setlocale(LC_ALL, \"%s\") = \"%s\" "
80 					"(expected \"%s\")\n",
81 				locales[i], result, expectedLocales[i]);
82 			problemCount++;
83 		}
84 	}
85 
86 	for (int i = 1; i <= LC_LAST; ++i)
87 		setlocale(i, locales[i + 1]);
88 	char* result = setlocale(LC_ALL, NULL);
89 	const char* expectedResult
90 		= "LC_COLLATE=de_DE;LC_CTYPE=en_US;LC_MESSAGES=nb_NO;"
91 			"LC_MONETARY=en_US.US-ASCII;LC_NUMERIC=hr_HR.ISO-8859-2;"
92 			"LC_TIME=nl_NL";
93 	if (!result || strcmp(result, expectedResult) != 0) {
94 		printf("\tPROBLEM: setlocale(LC_ALL, NULL) = \"%s\" "
95 				"(expected \"%s\")\n", result, expectedResult);
96 		problemCount++;
97 	}
98 
99 	if (problemCount)
100 		printf("\t%d problem(s) found!\n", problemCount);
101 	else
102 		printf("\tall fine\n");
103 }
104 
105 
106 // #pragma mark - localeconv ---------------------------------------------------
107 
108 
109 void
110 dumpGrouping(const char* grouping, char* buf)
111 {
112 	for (char* bufPtr = buf; *grouping; ++grouping)
113 		bufPtr += sprintf(bufPtr, "\\x%02x", *grouping);
114 }
115 
116 
117 void
118 test_localeconv(const char* locale, const lconv* localeConv)
119 {
120 	setlocale(LC_MONETARY, locale);
121 	setlocale(LC_NUMERIC, locale);
122 	printf("localeconv for '%s'\n", locale);
123 
124 	int problemCount = 0;
125 	struct lconv* lc = localeconv();
126 	if (!lc)
127 		printf("not ok - got no result from localeconv()\n");
128 	else {
129 		if (strcmp(lc->decimal_point, localeConv->decimal_point) != 0) {
130 			printf("\tPROBLEM: lc.decimal_point = \"%s\" (expected \"%s\")\n",
131 				lc->decimal_point, localeConv->decimal_point);
132 			problemCount++;
133 		}
134 		if (strcmp(lc->thousands_sep, localeConv->thousands_sep) != 0) {
135 			printf("\tPROBLEM: lc.thousands_sep = \"%s\" (expected \"%s\")\n",
136 				lc->thousands_sep, localeConv->thousands_sep);
137 			problemCount++;
138 		}
139 		if (strcmp(lc->grouping, localeConv->grouping) != 0) {
140 			char gotGrouping[20], expectedGrouping[20];
141 			dumpGrouping(lc->grouping, gotGrouping);
142 			dumpGrouping(localeConv->grouping, expectedGrouping);
143 			printf("\tPROBLEM: lc.grouping = \"%s\" (expected \"%s\")\n",
144 				gotGrouping, expectedGrouping);
145 			problemCount++;
146 		}
147 		if (strcmp(lc->int_curr_symbol, localeConv->int_curr_symbol) != 0) {
148 			printf("\tPROBLEM: lc.int_curr_symbol = \"%s\" (expected \"%s\")\n",
149 				lc->int_curr_symbol, localeConv->int_curr_symbol);
150 			problemCount++;
151 		}
152 		if (strcmp(lc->currency_symbol, localeConv->currency_symbol) != 0) {
153 			printf("\tPROBLEM: lc.currency_symbol = \"%s\" (expected \"%s\")\n",
154 				lc->currency_symbol, localeConv->currency_symbol);
155 			problemCount++;
156 		}
157 		if (strcmp(lc->mon_decimal_point, localeConv->mon_decimal_point) != 0) {
158 			printf("\tPROBLEM: lc.mon_decimal_point = \"%s\" "
159 					"(expected \"%s\")\n",
160 				lc->mon_decimal_point, localeConv->mon_decimal_point);
161 			problemCount++;
162 		}
163 		if (strcmp(lc->mon_thousands_sep, localeConv->mon_thousands_sep) != 0) {
164 			printf("\tPROBLEM: lc.mon_thousands_sep = \"%s\" "
165 					"(expected \"%s\")\n",
166 				lc->mon_thousands_sep, localeConv->mon_thousands_sep);
167 			problemCount++;
168 		}
169 		if (strcmp(lc->mon_grouping, localeConv->mon_grouping) != 0) {
170 			char gotGrouping[20], expectedGrouping[20];
171 			dumpGrouping(lc->mon_grouping, gotGrouping);
172 			dumpGrouping(localeConv->mon_grouping, expectedGrouping);
173 			printf("\tPROBLEM: lc.mon_grouping: \"%s\" (expected \"%s\")\n",
174 				gotGrouping, expectedGrouping);
175 			problemCount++;
176 		}
177 		if (strcmp(lc->positive_sign, localeConv->positive_sign) != 0) {
178 			printf("\tPROBLEM: lc.positive_sign = \"%s\" (expected \"%s\")\n",
179 				lc->positive_sign, localeConv->positive_sign);
180 			problemCount++;
181 		}
182 		if (strcmp(lc->negative_sign, localeConv->negative_sign) != 0) {
183 			printf("\tPROBLEM: lc.negative_sign = \"%s\" (expected \"%s\")\n",
184 				lc->negative_sign, localeConv->negative_sign);
185 			problemCount++;
186 		}
187 		if (lc->frac_digits != localeConv->frac_digits) {
188 			printf("\tPROBLEM: lc.frac_digits = %d (expected %d)\n",
189 				lc->frac_digits, localeConv->frac_digits);
190 			problemCount++;
191 		}
192 		if (lc->int_frac_digits != localeConv->int_frac_digits) {
193 			printf("\tPROBLEM: lc.int_frac_digits = %d (expected %d)\n",
194 				lc->int_frac_digits, localeConv->int_frac_digits);
195 			problemCount++;
196 		}
197 		if (lc->p_cs_precedes != localeConv->p_cs_precedes) {
198 			printf("\tPROBLEM: lc.p_cs_precedes = %d (expected %d)\n",
199 				lc->p_cs_precedes, localeConv->p_cs_precedes);
200 			problemCount++;
201 		}
202 		if (lc->p_sep_by_space != localeConv->p_sep_by_space) {
203 			printf("\tPROBLEM: lc.p_sep_by_space = %d (expected %d)\n",
204 				lc->p_sep_by_space, localeConv->p_sep_by_space);
205 			problemCount++;
206 		}
207 		if (lc->p_sign_posn != localeConv->p_sign_posn) {
208 			printf("\tPROBLEM: lc.p_sign_posn = %d (expected %d)\n",
209 				lc->p_sign_posn, localeConv->p_sign_posn);
210 			problemCount++;
211 		}
212 		if (lc->n_cs_precedes != localeConv->n_cs_precedes) {
213 			printf("\tPROBLEM: lc.n_cs_precedes = %d (expected %d)\n",
214 				lc->n_cs_precedes, localeConv->n_cs_precedes);
215 			problemCount++;
216 		}
217 		if (lc->n_sep_by_space != localeConv->n_sep_by_space) {
218 			printf("\tPROBLEM: lc.n_sep_by_space = %d (expected %d)\n",
219 				lc->n_sep_by_space, localeConv->n_sep_by_space);
220 			problemCount++;
221 		}
222 		if (lc->n_sign_posn != localeConv->n_sign_posn) {
223 			printf("\tPROBLEM: lc.n_sign_posn = %d (expected %d)\n",
224 				lc->n_sign_posn, localeConv->n_sign_posn);
225 			problemCount++;
226 		}
227 		if (lc->int_p_cs_precedes != localeConv->int_p_cs_precedes) {
228 			printf("\tPROBLEM: lc.int_p_cs_precedes = %d (expected %d)\n",
229 				lc->int_p_cs_precedes, localeConv->int_p_cs_precedes);
230 			problemCount++;
231 		}
232 		if (lc->int_p_sep_by_space != localeConv->int_p_sep_by_space) {
233 			printf("\tPROBLEM: lc.int_p_sep_by_space = %d (expected %d)\n",
234 				lc->int_p_sep_by_space, localeConv->int_p_sep_by_space);
235 			problemCount++;
236 		}
237 		if (lc->int_p_sign_posn != localeConv->int_p_sign_posn) {
238 			printf("\tPROBLEM: lc.int_p_sign_posn = %d (expected %d)\n",
239 				lc->int_p_sign_posn, localeConv->int_p_sign_posn);
240 			problemCount++;
241 		}
242 		if (lc->int_n_cs_precedes != localeConv->int_n_cs_precedes) {
243 			printf("\tPROBLEM: lc.int_n_cs_precedes = %d (expected %d)\n",
244 				lc->int_n_cs_precedes, localeConv->int_n_cs_precedes);
245 			problemCount++;
246 		}
247 		if (lc->int_n_sep_by_space != localeConv->int_n_sep_by_space) {
248 			printf("\tPROBLEM: lc.int_n_sep_by_space = %d (expected %d)\n",
249 				lc->int_n_sep_by_space, localeConv->int_n_sep_by_space);
250 			problemCount++;
251 		}
252 		if (lc->int_n_sign_posn != localeConv->int_n_sign_posn) {
253 			printf("\tPROBLEM: lc.int_n_sign_posn = %d (expected %d)\n",
254 				lc->int_n_sign_posn, localeConv->int_n_sign_posn);
255 			problemCount++;
256 		}
257 	}
258 	if (problemCount)
259 		printf("\t%d problem(s) found!\n", problemCount);
260 	else
261 		printf("\tall fine\n");
262 }
263 
264 
265 void
266 test_localeconv()
267 {
268 	const lconv lconv_posix = {
269 		(char*)".",
270 		(char*)"",
271 		(char*)"",
272 		(char*)"",
273 		(char*)"",
274 		(char*)"",
275 		(char*)"",
276 		(char*)"",
277 		(char*)"",
278 		(char*)"",
279 		CHAR_MAX,
280 		CHAR_MAX,
281 		CHAR_MAX,
282 		CHAR_MAX,
283 		CHAR_MAX,
284 		CHAR_MAX,
285 		CHAR_MAX,
286 		CHAR_MAX,
287 		CHAR_MAX,
288 		CHAR_MAX,
289 		CHAR_MAX,
290 		CHAR_MAX,
291 		CHAR_MAX,
292 		CHAR_MAX
293 	};
294 	test_localeconv("POSIX", &lconv_posix);
295 
296 	const lconv lconv_de = {
297 		(char*)",",
298 		(char*)".",
299 		(char*)"\x03",
300 		(char*)"EUR ",
301 		(char*)"€",
302 		(char*)",",
303 		(char*)".",
304 		(char*)"\x03",
305 		(char*)"",
306 		(char*)"-",
307 		2,
308 		2,
309 		0,
310 		1,
311 		0,
312 		1,
313 		1,
314 		1,
315 		0,
316 		1,
317 		0,
318 		1,
319 		1,
320 		1
321 	};
322 	test_localeconv("de_DE", &lconv_de);
323 
324 	const lconv lconv_de_iso = {
325 		(char*)",",
326 		(char*)".",
327 		(char*)"\x03",
328 		(char*)"EUR ",
329 		(char*)"EUR",
330 		(char*)",",
331 		(char*)".",
332 		(char*)"\x03",
333 		(char*)"",
334 		(char*)"-",
335 		2,
336 		2,
337 		0,
338 		1,
339 		0,
340 		1,
341 		1,
342 		1,
343 		0,
344 		1,
345 		0,
346 		1,
347 		1,
348 		1
349 	};
350 	test_localeconv("de_DE.ISO8859-1", &lconv_de_iso);
351 
352 	const lconv lconv_hr = {
353 		(char*)",",
354 		(char*)".",
355 		(char*)"\x03",
356 		(char*)"HRK ",
357 		(char*)"kn",
358 		(char*)",",
359 		(char*)".",
360 		(char*)"\x03",
361 		(char*)"",
362 		(char*)"-",
363 		2,
364 		2,
365 		0,
366 		1,
367 		0,
368 		1,
369 		1,
370 		1,
371 		0,
372 		1,
373 		0,
374 		1,
375 		1,
376 		1
377 	};
378 	test_localeconv("hr_HR.ISO8859-2", &lconv_hr);
379 
380 	const lconv lconv_de_CH = {
381 		(char*)".",
382 		(char*)"'",
383 		(char*)"\x03",
384 		(char*)"CHF ",
385 		(char*)"CHF",
386 		(char*)".",
387 		(char*)"'",
388 		(char*)"\x03",
389 		(char*)"",
390 		(char*)"-",
391 		2,
392 		2,
393 		1,
394 		1,
395 		1,
396 		0,
397 		4,
398 		4,
399 		1,
400 		1,
401 		1,
402 		0,
403 		4,
404 		4
405 	};
406 	test_localeconv("de_CH", &lconv_de_CH);
407 
408 	const lconv lconv_gu_IN = {
409 		(char*)".",
410 		(char*)",",
411 		(char*)"\x03\x02",
412 		(char*)"INR ",
413 		(char*)"\xE2\x82\xB9",
414 		(char*)".",
415 		(char*)",",
416 		(char*)"\x03\x02",
417 		(char*)"",
418 		(char*)"-",
419 		2,
420 		2,
421 		1,
422 		1,
423 		1,
424 		1,
425 		1,
426 		1,
427 		1,
428 		1,
429 		1,
430 		1,
431 		1,
432 		1
433 	};
434 	test_localeconv("gu_IN", &lconv_gu_IN);
435 
436 	const lconv lconv_it = {
437 		(char*)",",
438 		(char*)".",
439 		(char*)"\x03",
440 		(char*)"EUR ",
441 		(char*)"€",
442 		(char*)",",
443 		(char*)".",
444 		(char*)"\x03",
445 		(char*)"",
446 		(char*)"-",
447 		2,
448 		2,
449 		1,
450 		1,
451 		1,
452 		1,
453 		1,
454 		1,
455 		1,
456 		1,
457 		1,
458 		1,
459 		1,
460 		1
461 	};
462 	test_localeconv("it_IT", &lconv_it);
463 
464 	const lconv lconv_nl = {
465 		(char*)",",
466 		(char*)".",
467 		(char*)"\x03",
468 		(char*)"EUR ",
469 		(char*)"€",
470 		(char*)",",
471 		(char*)".",
472 		(char*)"\x03",
473 		(char*)"",
474 		(char*)"-",
475 		2,
476 		2,
477 		1,
478 		1,
479 		1,
480 		1,
481 		2,
482 		2,
483 		1,
484 		1,
485 		1,
486 		1,
487 		2,
488 		2
489 	};
490 	test_localeconv("nl_NL", &lconv_nl);
491 
492 	const lconv lconv_nb = {
493 		(char*)",",
494 		(char*)" ",
495 		(char*)"\x03",
496 		(char*)"NOK ",
497 		(char*)"kr",
498 		(char*)",",
499 		(char*)" ",
500 		(char*)"\x03",
501 		(char*)"",
502 		(char*)"-",
503 		2,
504 		2,
505 		1,
506 		1,
507 		1,
508 		1,
509 		1,
510 		1,
511 		1,
512 		1,
513 		1,
514 		1,
515 		1,
516 		1
517 	};
518 	test_localeconv("nb_NO", &lconv_nb);
519 }
520 
521 
522 // #pragma mark - strftime -----------------------------------------------------
523 
524 
525 struct strftime_data {
526 	const char* format;
527 	const char* result;
528 };
529 
530 
531 void
532 test_strftime(const char* locale, const strftime_data data[])
533 {
534 	setlocale(LC_TIME, locale);
535 	printf("strftime for '%s'\n", locale);
536 
537 	time_t nowSecs = 1279391169;	// pure magic
538 	tm* now = localtime(&nowSecs);
539 	int problemCount = 0;
540 	for(int i = 0; data[i].format != NULL; ++i) {
541 		char buf[100];
542 		strftime(buf, 100, data[i].format, now);
543 		if (strcmp(buf, data[i].result) != 0) {
544 			printf("\tPROBLEM: strftime(\"%s\") = \"%s\" (expected \"%s\")\n",
545 				data[i].format, buf, data[i].result);
546 			problemCount++;
547 		}
548 	}
549 	if (problemCount)
550 		printf("\t%d problem(s) found!\n", problemCount);
551 	else
552 		printf("\tall fine\n");
553 }
554 
555 
556 void
557 test_strftime()
558 {
559 	setenv("TZ", "GMT", 1);
560 
561 	const strftime_data strftime_posix[] = {
562 		{ "%c", "Sat Jul 17 18:26:09 2010" },
563 		{ "%x", "07/17/10" },
564 		{ "%X", "18:26:09" },
565 		{ "%a", "Sat" },
566 		{ "%A", "Saturday" },
567 		{ "%b", "Jul" },
568 		{ "%B", "July" },
569 		{ NULL, NULL }
570 	};
571 	test_strftime("POSIX", strftime_posix);
572 
573 	const strftime_data strftime_de[] = {
574 		{ "%c", "Samstag, 17. Juli 2010 18:26:09 GMT" },
575 		{ "%x", "17.07.2010" },
576 		{ "%X", "18:26:09" },
577 		{ "%a", "Sa." },
578 		{ "%A", "Samstag" },
579 		{ "%b", "Jul" },
580 		{ "%B", "Juli" },
581 		{ NULL, NULL }
582 	};
583 	test_strftime("de_DE.UTF-8", strftime_de);
584 
585 	const strftime_data strftime_hr[] = {
586 		{ "%c", "subota, 17. srpnja 2010. 18:26:09 GMT" },
587 		{ "%x", "17. 07. 2010." },
588 		{ "%X", "18:26:09" },
589 		{ "%a", "sub" },
590 		{ "%A", "subota" },
591 		{ "%b", "srp" },
592 		{ "%B", "srpnja" },
593 		{ NULL, NULL }
594 	};
595 	test_strftime("hr_HR.ISO8859-2", strftime_hr);
596 
597 	const strftime_data strftime_gu[] = {
598 		{ "%c", "શનિવાર, 17 જુલાઈ, 2010 06:26:09 PM GMT" },
599 		{ "%x", "17 જુલાઈ, 2010" },
600 		{ "%X", "06:26:09 PM" },
601 		{ "%a", "શનિ" },
602 		{ "%A", "શનિવાર" },
603 		{ "%b", "જુલાઈ" },
604 		{ "%B", "જુલાઈ" },
605 		{ NULL, NULL }
606 	};
607 	test_strftime("gu_IN", strftime_gu);
608 
609 	const strftime_data strftime_it[] = {
610 		{ "%c", "sabato 17 luglio 2010 18:26:09 GMT" },
611 		{ "%x", "17/lug/2010" },
612 		{ "%X", "18:26:09" },
613 		{ "%a", "sab" },
614 		{ "%A", "sabato" },
615 		{ "%b", "lug" },
616 		{ "%B", "luglio" },
617 		{ NULL, NULL }
618 	};
619 	test_strftime("it_IT", strftime_it);
620 
621 	const strftime_data strftime_nl[] = {
622 		{ "%c", "zaterdag 17 juli 2010 18:26:09 GMT" },
623 		{ "%x", "17 jul. 2010" },
624 		{ "%X", "18:26:09" },
625 		{ "%a", "za" },
626 		{ "%A", "zaterdag" },
627 		{ "%b", "jul." },
628 		{ "%B", "juli" },
629 		{ NULL, NULL }
630 	};
631 	test_strftime("nl_NL", strftime_nl);
632 
633 	const strftime_data strftime_nb[] = {
634 		{ "%c", "kl. 18:26:09 GMT lørdag 17. juli 2010" },
635 		{ "%x", "17. juli 2010" },
636 		{ "%X", "18:26:09" },
637 		{ "%a", "lør." },
638 		{ "%A", "lørdag" },
639 		{ "%b", "juli" },
640 		{ "%B", "juli" },
641 		{ NULL, NULL }
642 	};
643 	test_strftime("nb_NO", strftime_nb);
644 }
645 
646 
647 // #pragma mark - ctype --------------------------------------------------------
648 
649 
650 unsigned short
651 determineFullClassInfo(int i)
652 {
653 	unsigned short classInfo = 0;
654 
655 	if (isblank(i))
656 		classInfo |= _ISblank;
657 	if (iscntrl(i))
658 		classInfo |= _IScntrl;
659 	if (ispunct(i))
660 		classInfo |= _ISpunct;
661 	if (isalnum(i))
662 		classInfo |= _ISalnum;
663 	if (isupper(i))
664 		classInfo |= _ISupper;
665 	if (islower(i))
666 		classInfo |= _ISlower;
667 	if (isalpha(i))
668 		classInfo |= _ISalpha;
669 	if (isdigit(i))
670 		classInfo |= _ISdigit;
671 	if (isxdigit(i))
672 		classInfo |= _ISxdigit;
673 	if (isspace(i))
674 		classInfo |= _ISspace;
675 	if (isprint(i))
676 		classInfo |= _ISprint;
677 	if (isgraph(i))
678 		classInfo |= _ISgraph;
679 
680 	return classInfo;
681 }
682 
683 
684 void
685 test_ctype(const char* locale, const unsigned short int classInfos[],
686 	const int toLowerMap[], const int toUpperMap[])
687 {
688 	setlocale(LC_CTYPE, locale);
689 	printf("ctype of %s locale\n", locale);
690 
691 	int problemCount = 0;
692 	for (int i = -1; i < 256; ++i) {
693 		unsigned short classInfo = determineFullClassInfo(i);
694 
695 		if (i < 255) {
696 			char iAsChar = (char)i;
697 			unsigned short classInfoFromChar = determineFullClassInfo(iAsChar);
698 
699 			if (classInfo != classInfoFromChar) {
700 				printf("\tPROBLEM: ctype((int)%d)=%x, but ctype((char)%d)=%x\n",
701 					i, classInfo, i, classInfoFromChar);
702 				problemCount++;
703 			}
704 		}
705 		if (classInfo != classInfos[i + 1]) {
706 			printf("\tPROBLEM: ctype(%d) = %x (expected %x)\n", i, classInfo,
707 				classInfos[i + 1]);
708 			problemCount++;
709 		}
710 		int lower = tolower(i);
711 		if (lower != toLowerMap[i + 1]) {
712 			printf("\tPROBLEM: tolower(%d) = %x (expected %x)\n", i, lower,
713 				toLowerMap[i + 1]);
714 			problemCount++;
715 		}
716 		int upper = toupper(i);
717 		if (upper != toUpperMap[i + 1]) {
718 			printf("\tPROBLEM: toupper(%d) = %x (expected %x)\n", i, upper,
719 				toUpperMap[i + 1]);
720 			problemCount++;
721 		}
722 	}
723 	if (problemCount)
724 		printf("\t%d problem(s) found!\n", problemCount);
725 	else
726 		printf("\tall fine\n");
727 }
728 
729 
730 void
731 test_ctype()
732 {
733 	const unsigned short int classInfos_posix[257] = {
734 		/*  -1 */   0,	// neutral value
735 		/*   0 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
736 		/*   8 */	_IScntrl, _ISblank|_IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl, _IScntrl,
737 		/*  16 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
738 		/*  24 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
739 		/*  32 */	_ISblank|_ISspace|_ISprint, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
740 		/*  40 */	_ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
741 		/*  48 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph,
742 		/*  56 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
743 		/*  64 */	_ISpunct|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
744 		/*  72 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
745 		/*  80 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
746 		/*  88 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
747 		/*  96 */	_ISpunct|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
748 		/* 104 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
749 		/* 112 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
750 		/* 120 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _IScntrl,
751 		/* 128 */	0, 0, 0, 0, 0, 0, 0, 0,
752 		/* 136 */	0, 0, 0, 0, 0, 0, 0, 0,
753 		/* 144 */	0, 0, 0, 0, 0, 0, 0, 0,
754 		/* 152 */	0, 0, 0, 0, 0, 0, 0, 0,
755 		/* 160 */	0, 0, 0, 0, 0, 0, 0, 0,
756 		/* 168 */	0, 0, 0, 0, 0, 0, 0, 0,
757 		/* 176 */	0, 0, 0, 0, 0, 0, 0, 0,
758 		/* 184 */	0, 0, 0, 0, 0, 0, 0, 0,
759 		/* 192 */	0, 0, 0, 0, 0, 0, 0, 0,
760 		/* 200 */	0, 0, 0, 0, 0, 0, 0, 0,
761 		/* 208 */	0, 0, 0, 0, 0, 0, 0, 0,
762 		/* 216 */	0, 0, 0, 0, 0, 0, 0, 0,
763 		/* 224 */	0, 0, 0, 0, 0, 0, 0, 0,
764 		/* 232 */	0, 0, 0, 0, 0, 0, 0, 0,
765 		/* 240 */	0, 0, 0, 0, 0, 0, 0, 0,
766 		/* 248 */	0, 0, 0, 0, 0, 0, 0, 0,
767 	};
768 	const int toLowerMap_posix[257] = {
769 		/*  -1 */    -1,	// identity value
770 		/*   0 */	  0,   1,   2,   3,   4,   5,   6,   7,
771 		/*   8 */	  8,   9,  10,  11,  12,  13,  14,  15,
772 		/*  16 */	 16,  17,  18,  19,  20,  21,  22,  23,
773 		/*  24 */	 24,  25,  26,  27,  28,  29,  30,  31,
774 		/*  32 */	 32,  33,  34,  35,  36,  37,  38,  39,
775 		/*  40 */	 40,  41,  42,  43,  44,  45,  46,  47,
776 		/*  48 */	'0', '1', '2', '3', '4', '5', '6', '7',
777 		/*  56 */	'8', '9',  58,  59,  60,  61,  62,  63,
778 		/*  64 */	 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
779 		/*  72 */	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
780 		/*  80 */	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
781 		/*  88 */	'x', 'y', 'z',  91,  92,  93,  94,  95,
782 		/*  96 */	 96, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
783 		/* 104 */	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
784 		/* 112 */	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
785 		/* 120 */	'x', 'y', 'z', 123, 124, 125, 126, 127,
786 		/* 128 */	128, 129, 130, 131, 132, 133, 134, 135,
787 		/* 136 */	136, 137, 138, 139, 140, 141, 142, 143,
788 		/* 144 */	144, 145, 146, 147, 148, 149, 150, 151,
789 		/* 152 */	152, 153, 154, 155, 156, 157, 158, 159,
790 		/* 160 */	160, 161, 162, 163, 164, 165, 166, 167,
791 		/* 168 */	168, 169, 170, 171, 172, 173, 174, 175,
792 		/* 176 */	176, 177, 178, 179, 180, 181, 182, 183,
793 		/* 184 */	184, 185, 186, 187, 188, 189, 190, 191,
794 		/* 192 */	192, 193, 194, 195, 196, 197, 198, 199,
795 		/* 200 */	200, 201, 202, 203, 204, 205, 206, 207,
796 		/* 208 */	208, 209, 210, 211, 212, 213, 214, 215,
797 		/* 216 */	216, 217, 218, 219, 220, 221, 222, 223,
798 		/* 224 */	224, 225, 226, 227, 228, 229, 230, 231,
799 		/* 232 */	232, 233, 234, 235, 236, 237, 238, 239,
800 		/* 240 */	240, 241, 242, 243, 244, 245, 246, 247,
801 		/* 248 */	248, 249, 250, 251, 252, 253, 254, 255,
802 	};
803 	const int toUpperMap_posix[257] = {
804 		/*  -1 */    -1,	// identity value
805 		/*   0 */	  0,   1,   2,   3,   4,   5,   6,   7,
806 		/*   8 */	  8,   9,  10,  11,  12,  13,  14,  15,
807 		/*  16 */	 16,  17,  18,  19,  20,  21,  22,  23,
808 		/*  24 */	 24,  25,  26,  27,  28,  29,  30,  31,
809 		/*  32 */	 32,  33,  34,  35,  36,  37,  38,  39,
810 		/*  40 */	 40,  41,  42,  43,  44,  45,  46,  47,
811 		/*  48 */	'0', '1', '2', '3', '4', '5', '6', '7',
812 		/*  56 */	'8', '9',  58,  59,  60,  61,  62,  63,
813 		/*  64 */	 64, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
814 		/*  72 */	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
815 		/*  80 */	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
816 		/*  88 */	'X', 'Y', 'Z',  91,  92,  93,  94,  95,
817 		/*  96 */	 96, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
818 		/* 104 */	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
819 		/* 112 */	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
820 		/* 120 */	'X', 'Y', 'Z', 123, 124, 125, 126, 127,
821 		/* 128 */	128, 129, 130, 131, 132, 133, 134, 135,
822 		/* 136 */	136, 137, 138, 139, 140, 141, 142, 143,
823 		/* 144 */	144, 145, 146, 147, 148, 149, 150, 151,
824 		/* 152 */	152, 153, 154, 155, 156, 157, 158, 159,
825 		/* 160 */	160, 161, 162, 163, 164, 165, 166, 167,
826 		/* 168 */	168, 169, 170, 171, 172, 173, 174, 175,
827 		/* 176 */	176, 177, 178, 179, 180, 181, 182, 183,
828 		/* 184 */	184, 185, 186, 187, 188, 189, 190, 191,
829 		/* 192 */	192, 193, 194, 195, 196, 197, 198, 199,
830 		/* 200 */	200, 201, 202, 203, 204, 205, 206, 207,
831 		/* 208 */	208, 209, 210, 211, 212, 213, 214, 215,
832 		/* 216 */	216, 217, 218, 219, 220, 221, 222, 223,
833 		/* 224 */	224, 225, 226, 227, 228, 229, 230, 231,
834 		/* 232 */	232, 233, 234, 235, 236, 237, 238, 239,
835 		/* 240 */	240, 241, 242, 243, 244, 245, 246, 247,
836 		/* 248 */	248, 249, 250, 251, 252, 253, 254, 255,
837 	};
838 	test_ctype("POSIX", classInfos_posix, toLowerMap_posix, toUpperMap_posix);
839 
840 	const unsigned short int classInfos_de[257] = {
841 		/*  -1 */   0,	// neutral value
842 		/*   0 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
843 		/*   8 */	_IScntrl, _ISblank|_IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl, _IScntrl,
844 		/*  16 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
845 		/*  24 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
846 		/*  32 */	_ISblank|_ISspace|_ISprint, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
847 		/*  40 */	_ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
848 		/*  48 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph,
849 		/*  56 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
850 		/*  64 */	_ISpunct|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
851 		/*  72 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
852 		/*  80 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
853 		/*  88 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
854 		/*  96 */	_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
855 		/* 104 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
856 		/* 112 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
857 		/* 120 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _IScntrl,
858 		/* 128 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl|_ISspace, _IScntrl, _IScntrl,
859 		/* 136 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
860 		/* 144 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
861 		/* 152 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
862 		/* 160 */	_ISprint|_ISspace|_ISblank, _ISprint|_ISgraph|_ISpunct, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph,
863 		/* 168 */	_ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISpunct, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph,
864 		/* 176 */	_ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph, _ISprint|_ISgraph|_ISpunct,
865 		/* 184 */	_ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISpunct, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph|_ISpunct,
866 		/* 192 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper,
867 		/* 200 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper,
868 		/* 208 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph,
869 		/* 216 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISupper, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower,
870 		/* 224 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower,
871 		/* 232 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower,
872 		/* 240 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph,
873 		/* 248 */	_ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower, _ISprint|_ISgraph|_ISalnum|_ISalpha|_ISlower,
874 	};
875 	const int toLowerMap_de[257] = {
876 		/*  -1 */    -1,	// identity value
877 		/*   0 */	  0,   1,   2,   3,   4,   5,   6,   7,
878 		/*   8 */	  8,   9,  10,  11,  12,  13,  14,  15,
879 		/*  16 */	 16,  17,  18,  19,  20,  21,  22,  23,
880 		/*  24 */	 24,  25,  26,  27,  28,  29,  30,  31,
881 		/*  32 */	 32,  33,  34,  35,  36,  37,  38,  39,
882 		/*  40 */	 40,  41,  42,  43,  44,  45,  46,  47,
883 		/*  48 */	'0', '1', '2', '3', '4', '5', '6', '7',
884 		/*  56 */	'8', '9',  58,  59,  60,  61,  62,  63,
885 		/*  64 */	 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
886 		/*  72 */	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
887 		/*  80 */	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
888 		/*  88 */	'x', 'y', 'z',  91,  92,  93,  94,  95,
889 		/*  96 */	 96, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
890 		/* 104 */	'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
891 		/* 112 */	'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
892 		/* 120 */	'x', 'y', 'z', 123, 124, 125, 126, 127,
893 		/* 128 */	128, 129, 130, 131, 132, 133, 134, 135,
894 		/* 136 */	136, 137, 138, 139, 140, 141, 142, 143,
895 		/* 144 */	144, 145, 146, 147, 148, 149, 150, 151,
896 		/* 152 */	152, 153, 154, 155, 156, 157, 158, 159,
897 		/* 160 */	160, 161, 162, 163, 164, 165, 166, 167,
898 		/* 168 */	168, 169, 170, 171, 172, 173, 174, 175,
899 		/* 176 */	176, 177, 178, 179, 180, 181, 182, 183,
900 		/* 184 */	184, 185, 186, 187, 188, 189, 190, 191,
901 		/* 192 */	224, 225, 226, 227, 228, 229, 230, 231,
902 		/* 200 */	232, 233, 234, 235, 236, 237, 238, 239,
903 		/* 208 */	240, 241, 242, 243, 244, 245, 246, 215,
904 		/* 216 */	248, 249, 250, 251, 252, 253, 254, 223,
905 		/* 224 */	224, 225, 226, 227, 228, 229, 230, 231,
906 		/* 232 */	232, 233, 234, 235, 236, 237, 238, 239,
907 		/* 240 */	240, 241, 242, 243, 244, 245, 246, 247,
908 		/* 248 */	248, 249, 250, 251, 252, 253, 254, 255,
909 	};
910 	const int toUpperMap_de[257] = {
911 		/*  -1 */    -1,	// identity value
912 		/*   0 */	  0,   1,   2,   3,   4,   5,   6,   7,
913 		/*   8 */	  8,   9,  10,  11,  12,  13,  14,  15,
914 		/*  16 */	 16,  17,  18,  19,  20,  21,  22,  23,
915 		/*  24 */	 24,  25,  26,  27,  28,  29,  30,  31,
916 		/*  32 */	 32,  33,  34,  35,  36,  37,  38,  39,
917 		/*  40 */	 40,  41,  42,  43,  44,  45,  46,  47,
918 		/*  48 */	'0', '1', '2', '3', '4', '5', '6', '7',
919 		/*  56 */	'8', '9',  58,  59,  60,  61,  62,  63,
920 		/*  64 */	 64, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
921 		/*  72 */	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
922 		/*  80 */	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
923 		/*  88 */	'X', 'Y', 'Z',  91,  92,  93,  94,  95,
924 		/*  96 */	 96, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
925 		/* 104 */	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
926 		/* 112 */	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
927 		/* 120 */	'X', 'Y', 'Z', 123, 124, 125, 126, 127,
928 		/* 128 */	128, 129, 130, 131, 132, 133, 134, 135,
929 		/* 136 */	136, 137, 138, 139, 140, 141, 142, 143,
930 		/* 144 */	144, 145, 146, 147, 148, 149, 150, 151,
931 		/* 152 */	152, 153, 154, 155, 156, 157, 158, 159,
932 		/* 160 */	160, 161, 162, 163, 164, 165, 166, 167,
933 		/* 168 */	168, 169, 170, 171, 172, 173, 174, 175,
934 		/* 176 */	176, 177, 178, 179, 180, 181, 182, 183,
935 		/* 184 */	184, 185, 186, 187, 188, 189, 190, 191,
936 		/* 192 */	192, 193, 194, 195, 196, 197, 198, 199,
937 		/* 200 */	200, 201, 202, 203, 204, 205, 206, 207,
938 		/* 208 */	208, 209, 210, 211, 212, 213, 214, 215,
939 		/* 216 */	216, 217, 218, 219, 220, 221, 222, 223,
940 		/* 224 */	192, 193, 194, 195, 196, 197, 198, 199,
941 		/* 232 */	200, 201, 202, 203, 204, 205, 206, 207,
942 		/* 240 */	208, 209, 210, 211, 212, 213, 214, 247,
943 		/* 248 */	216, 217, 218, 219, 220, 221, 222, 255,
944 	};
945 	test_ctype("de_DE.ISO8859-1", classInfos_de, toLowerMap_de, toUpperMap_de);
946 
947 	const unsigned short int classInfos_utf8[257] = {
948 		/*  -1 */   0,	// neutral value
949 		/*   0 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
950 		/*   8 */	_IScntrl, _ISblank|_IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl|_ISspace, _IScntrl, _IScntrl,
951 		/*  16 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
952 		/*  24 */	_IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl, _IScntrl,
953 		/*  32 */	_ISblank|_ISspace|_ISprint, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
954 		/*  40 */	_ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
955 		/*  48 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph,
956 		/*  56 */	_ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISdigit|_ISxdigit|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
957 		/*  64 */	_ISpunct|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
958 		/*  72 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
959 		/*  80 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph,
960 		/*  88 */	_ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISupper|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph,
961 		/*  96 */	_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISxdigit|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
962 		/* 104 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
963 		/* 112 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph,
964 		/* 120 */	_ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISalnum|_ISlower|_ISalpha|_ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _ISpunct|_ISprint|_ISgraph, _ISprint|_ISgraph, _IScntrl,
965 		/* 128 */	0, 0, 0, 0, 0, 0, 0, 0,
966 		/* 136 */	0, 0, 0, 0, 0, 0, 0, 0,
967 		/* 144 */	0, 0, 0, 0, 0, 0, 0, 0,
968 		/* 152 */	0, 0, 0, 0, 0, 0, 0, 0,
969 		/* 160 */	0, 0, 0, 0, 0, 0, 0, 0,
970 		/* 168 */	0, 0, 0, 0, 0, 0, 0, 0,
971 		/* 176 */	0, 0, 0, 0, 0, 0, 0, 0,
972 		/* 184 */	0, 0, 0, 0, 0, 0, 0, 0,
973 		/* 192 */	0, 0, 0, 0, 0, 0, 0, 0,
974 		/* 200 */	0, 0, 0, 0, 0, 0, 0, 0,
975 		/* 208 */	0, 0, 0, 0, 0, 0, 0, 0,
976 		/* 216 */	0, 0, 0, 0, 0, 0, 0, 0,
977 		/* 224 */	0, 0, 0, 0, 0, 0, 0, 0,
978 		/* 232 */	0, 0, 0, 0, 0, 0, 0, 0,
979 		/* 240 */	0, 0, 0, 0, 0, 0, 0, 0,
980 		/* 248 */	0, 0, 0, 0, 0, 0, 0, 0,
981 	};
982 	test_ctype("de_DE.UTF-8", classInfos_utf8, toLowerMap_posix,
983 		toUpperMap_posix);
984 }
985 
986 
987 // #pragma mark - wctype -------------------------------------------------------
988 
989 
990 unsigned short
991 determineWideFullClassInfo(int i)
992 {
993 	unsigned short classInfo = 0;
994 
995 	if (iswblank(i))
996 		classInfo |= _ISblank;
997 	if (iswcntrl(i))
998 		classInfo |= _IScntrl;
999 	if (iswpunct(i))
1000 		classInfo |= _ISpunct;
1001 	if (iswalnum(i))
1002 		classInfo |= _ISalnum;
1003 	if (iswupper(i))
1004 		classInfo |= _ISupper;
1005 	if (iswlower(i))
1006 		classInfo |= _ISlower;
1007 	if (iswalpha(i))
1008 		classInfo |= _ISalpha;
1009 	if (iswdigit(i))
1010 		classInfo |= _ISdigit;
1011 	if (iswxdigit(i))
1012 		classInfo |= _ISxdigit;
1013 	if (iswspace(i))
1014 		classInfo |= _ISspace;
1015 	if (iswprint(i))
1016 		classInfo |= _ISprint;
1017 	if (iswgraph(i))
1018 		classInfo |= _ISgraph;
1019 
1020 	return classInfo;
1021 }
1022 
1023 
1024 void
1025 test_wctype(const char* locale, const wchar_t* text,
1026 	const unsigned short int wcs[], const unsigned short int classInfos[])
1027 {
1028 	setlocale(LC_CTYPE, locale);
1029 	printf("wctype of %s locale\n", locale);
1030 
1031 	int problemCount = 0;
1032 	unsigned short classInfo = determineWideFullClassInfo(WEOF);
1033 	if (classInfo != 0) {
1034 		printf("\tPROBLEM: classinfo for WEOF = %x (expected 0)\n", classInfo);
1035 		problemCount++;
1036 	}
1037 	wint_t wc = *text;
1038 	for (int i = 0; i < 48; wc = *++text, ++i) {
1039 		classInfo = determineWideFullClassInfo(wc);
1040 		if (wc != wcs[i]) {
1041 			printf("\tPROBLEM: wc for char #%d = %x (expected %x)\n", i, wc,
1042 				wcs[i]);
1043 			problemCount++;
1044 		}
1045 
1046 		if (classInfo != classInfos[i]) {
1047 			printf("\tPROBLEM: classinfo for #%d = %x (expected %x)\n", i,
1048 				classInfo, classInfos[i]);
1049 			problemCount++;
1050 		}
1051 	}
1052 	if (problemCount)
1053 		printf("\t%d problem(s) found!\n", problemCount);
1054 	else
1055 		printf("\tall fine\n");
1056 }
1057 
1058 
1059 void
1060 test_wctype()
1061 {
1062 	// haiku wide chars are always in UTF32, so nothing should change between
1063 	// different locales
1064 
1065 	const wchar_t* text = L"Hi there, how do you do? (äÜößáéúíó€'¤¹²$%#@) 12";
1066 
1067 	const unsigned short int wcs[48] = {
1068 		0x48, 0x69, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65,
1069 		0x2c, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x64, 0x6f,
1070 		0x20, 0x79, 0x6f, 0x75, 0x20, 0x64, 0x6f, 0x3f,
1071 		0x20, 0x28, 0xe4, 0xdc, 0xf6, 0xdf, 0xe1, 0xe9,
1072 		0xfa, 0xed, 0xf3, 0x20ac, 0x27, 0xa4, 0xb9, 0xb2,
1073 		0x24, 0x25, 0x23, 0x40, 0x29, 0x20, 0x31, 0x32
1074 	};
1075 	const unsigned short int classInfos[48] = {
1076 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISupper,
1077 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1078 		_ISspace|_ISprint|_ISblank,
1079 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1080 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1081 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower|_ISxdigit,
1082 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1083 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower|_ISxdigit,
1084 		_ISprint|_ISgraph|_ISpunct,
1085 		_ISspace|_ISprint|_ISblank,
1086 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1087 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1088 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1089 		_ISspace|_ISprint|_ISblank,
1090 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower|_ISxdigit,
1091 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1092 		_ISspace|_ISprint|_ISblank,
1093 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1094 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1095 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1096 		_ISspace|_ISprint|_ISblank,
1097 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower|_ISxdigit,
1098 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1099 		_ISprint|_ISgraph|_ISpunct,
1100 		_ISspace|_ISprint|_ISblank,
1101 		_ISprint|_ISgraph|_ISpunct,
1102 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1103 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISupper,
1104 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1105 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1106 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1107 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1108 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1109 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1110 		_ISprint|_ISgraph|_ISalpha|_ISalnum|_ISlower,
1111 		_ISprint|_ISgraph,
1112 		_ISprint|_ISgraph|_ISpunct,
1113 		_ISprint|_ISgraph,
1114 		_ISprint|_ISgraph,
1115 		_ISprint|_ISgraph,
1116 		_ISprint|_ISgraph,
1117 		_ISpunct|_ISprint|_ISgraph,
1118 		_ISpunct|_ISprint|_ISgraph,
1119 		_ISpunct|_ISprint|_ISgraph,
1120 		_ISpunct|_ISprint|_ISgraph,
1121 		_ISspace|_ISprint|_ISblank,
1122 		_ISprint|_ISgraph|_ISalnum|_ISdigit|_ISxdigit,
1123 		_ISprint|_ISgraph|_ISalnum|_ISdigit|_ISxdigit
1124 	};
1125 
1126 	test_wctype("POSIX", text, wcs, classInfos);
1127 	test_wctype("de_DE.ISO8859-1", text, wcs, classInfos);
1128 	test_wctype("de_DE.ISO8859-15", text, wcs, classInfos);
1129 	test_wctype("de_DE.UTF-8", text, wcs, classInfos);
1130 }
1131 
1132 
1133 // #pragma mark - wctrans ------------------------------------------------------
1134 
1135 
1136 void
1137 test_wctrans(const char* locale, const wchar_t* text, wctrans_t transition,
1138 	const wchar_t* expectedResult)
1139 {
1140 	setlocale(LC_CTYPE, locale);
1141 	printf("towctrans(%s) of %s locale\n",
1142 		transition == _ISlower ? "tolower" : "toupper", locale);
1143 
1144 	int problemCount = 0;
1145 	wint_t wc = *text;
1146 	for (int i = 0; wc != 0; wc = *++text, ++i) {
1147 		errno = 0;
1148 		wint_t result = towctrans(wc, transition);
1149 		if (result != expectedResult[i] || errno != 0) {
1150 			printf("\tPROBLEM: result for char #%d = %x (expected %x), "
1151 					"errno = %x (expected %x)\n",
1152 				i, result, expectedResult[i], errno, 0);
1153 			problemCount++;
1154 		}
1155 	}
1156 	if (problemCount)
1157 		printf("\t%d problem(s) found!\n", problemCount);
1158 	else
1159 		printf("\tall fine\n");
1160 }
1161 
1162 
1163 void
1164 test_wctrans()
1165 {
1166 	// haiku wide chars are always in UTF32, so nothing should change between
1167 	// different locales
1168 
1169 	setlocale(LC_CTYPE, "POSIX");
1170 	printf("wctrans setup\n");
1171 
1172 	int problemCount = 0;
1173 	errno = 0;
1174 	wctrans_t toU = wctrans("toupper");
1175 	if (errno != 0 || toU != _ISupper) {
1176 		printf("\tPROBLEM: wctrans(\"upper\") = %x (expected %x), "
1177 				"errno=%x (expected %x)\n",
1178 			toU, _ISupper, errno, 0);
1179 		problemCount++;
1180 	}
1181 	errno = 0;
1182 	wctrans_t toL = wctrans("tolower");
1183 	if (errno != 0 || toL != _ISlower) {
1184 		printf("\tPROBLEM: wctrans(\"lower\") = %x (expected %x), "
1185 				"errno=%x (expected %x)\n",
1186 			toL, _ISlower, errno, 0);
1187 		problemCount++;
1188 	}
1189 	errno = 0;
1190 	wctrans_t invalid1 = wctrans(NULL);
1191 	if (errno != EINVAL || invalid1 != 0) {
1192 		printf("\tPROBLEM: wctrans(NULL) = %x (expected %x), "
1193 				"errno=%x (expected %x)\n",
1194 			invalid1, 0, errno, EINVAL);
1195 		problemCount++;
1196 	}
1197 	errno = 0;
1198 	wctrans_t invalid2 = wctrans("invalid");
1199 	if (errno != EINVAL || invalid2 != 0) {
1200 		printf("\tPROBLEM: wctrans(\"invalid\") = %x (expected %x), "
1201 				"errno=%x (expected %x)\n",
1202 			invalid2, 0, errno, EINVAL);
1203 		problemCount++;
1204 	}
1205 	if (problemCount)
1206 		printf("\t%d problem(s) found!\n", problemCount);
1207 	else
1208 		printf("\tall fine\n");
1209 
1210 	const wchar_t* text = L"Hi there, how do you do? (äÜößáéúíó€'¤¹²$%#@) 12";
1211 	const wchar_t* textU = L"HI THERE, HOW DO YOU DO? (ÄÜÖßÁÉÚÍÓ€'¤¹²$%#@) 12";
1212 	const wchar_t* textL = L"hi there, how do you do? (äüößáéúíó€'¤¹²$%#@) 12";
1213 
1214 	test_wctrans("POSIX", text, toU, textU);
1215 	test_wctrans("de_DE.ISO8859-1", text, toU, textU);
1216 	test_wctrans("de_DE.ISO8859-15", text, toU, textU);
1217 	test_wctrans("de_DE.UTF-8", text, toU, textU);
1218 	test_wctrans("fr_Fr", text, toU, textU);
1219 
1220 	test_wctrans("POSIX", text, toL, textL);
1221 	test_wctrans("de_DE.ISO8859-1", text, toL, textL);
1222 	test_wctrans("de_DE.ISO8859-15", text, toL, textL);
1223 	test_wctrans("de_DE.UTF-8", text, toL, textL);
1224 	test_wctrans("fr_Fr", text, toL, textL);
1225 }
1226 
1227 
1228 // #pragma mark - wcwidth ------------------------------------------------------
1229 
1230 
1231 void
1232 test_wcwidth()
1233 {
1234 	setlocale(LC_ALL, "fr_FR.UTF-8");
1235 	printf("wcwidth()\n");
1236 
1237 	/* many of the following tests have been copied from gnulib */
1238 
1239 	int problemCount = 0;
1240 	int result = 0;
1241 
1242 	/* Test width of ASCII characters.  */
1243 	for (wchar_t wc = 0x20; wc < 0x7F; wc++) {
1244 		result = wcwidth(wc);
1245 		if (result != 1) {
1246 			printf("\tPROBLEM: wcwidth(%x)=%x (expected %x)\n", wc, result, 1);
1247 			problemCount++;
1248 		}
1249 	}
1250 
1251 	struct {
1252 		wchar_t wc;
1253 		int result;
1254 	} data[] = {
1255 		{ 0x0, 0 },
1256 		{ 0x1, -1 },
1257 		{ 0x1F, -1 },
1258 		{ 0x80, -1 },
1259 		{ 0x9F, -1 },
1260 		{ 0xA0, 1 },
1261 		{ 0x0301, 0 },
1262 		{ 0x05B0, 0 },
1263 		{ 0x200E, 0 },
1264 		{ 0x2060, 0 },
1265 		{ 0xE0001, 0 },
1266 		{ 0xE0044, 0 },
1267 		{ 0x200B, 0 },
1268 		{ 0xFEFF, 0 },
1269 		{ 0x3000, 2 },
1270 		{ 0xB250, 2 },
1271 		{ 0xFF1A, 2 },
1272 		{ 0x20369, 2 },
1273 		{ 0x2F876, 2 },
1274 		{ 0x0, 0 },
1275 	};
1276 	for (int i = 0; data[i].wc != 0 || i == 0; i++) {
1277 		result = wcwidth(data[i].wc);
1278 		if (result != data[i].result) {
1279 			printf("\tPROBLEM: wcwidth(%x)=%x (expected %x)\n", data[i].wc,
1280 				result, data[i].result);
1281 			problemCount++;
1282 		}
1283 	}
1284 
1285 	if (problemCount)
1286 		printf("\t%d problem(s) found!\n", problemCount);
1287 	else
1288 		printf("\tall fine\n");
1289 }
1290 
1291 
1292 // #pragma mark - nl_langinfo --------------------------------------------------
1293 
1294 
1295 void
1296 test_langinfo(const char* locale, const char* langinfos[])
1297 {
1298 	setlocale(LC_ALL, locale);
1299 	printf("langinfo of %s locale\n", locale);
1300 
1301 	int problemCount = 0;
1302 	for (int i = -1; langinfos[i + 1] != NULL; ++i) {
1303 		const char* langinfo = nl_langinfo(i);
1304 		if (strcmp(langinfo, langinfos[i + 1]) != 0) {
1305 			printf("\tPROBLEM: langinfo for #%d = '%s' (expected '%s')\n", i,
1306 				langinfo, langinfos[i + 1]);
1307 			problemCount++;
1308 		}
1309 	}
1310 	if (problemCount)
1311 		printf("\t%d problem(s) found!\n", problemCount);
1312 	else
1313 		printf("\tall fine\n");
1314 }
1315 
1316 
1317 void
1318 test_langinfo()
1319 {
1320 	const char* li_posix[] = {
1321 		"", 	// out of bounds
1322 		"US-ASCII",
1323 		"%a %b %e %H:%M:%S %Y",
1324 		"%m/%d/%y",
1325 		"%H:%M:%S",
1326 		"%I:%M:%S %p",
1327 		"AM",
1328 		"PM",
1329 
1330 		"Sunday", "Monday", "Tuesday", "Wednesday",	"Thursday", "Friday",
1331 		"Saturday",
1332 
1333 		"Sun", "Mon", "Tue", "Wed",	"Thu", "Fri", "Sat",
1334 
1335 		"January", "February", "March", "April", "May", "June",
1336 		"July", "August", "September", "October", "November", "December",
1337 
1338 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1339 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
1340 
1341 		"%EC, %Ey, %EY",
1342 		"%Ex",
1343 		"%Ec",
1344 		"%EX",
1345 		"%O",
1346 
1347 		".",
1348 		"",
1349 
1350 		"^[yY]",
1351 		"^[nN]",
1352 
1353 		"",
1354 
1355 		"", 	// out of bounds
1356 		NULL
1357 	};
1358 	test_langinfo("POSIX", li_posix);
1359 
1360 	const char* li_de[] = {
1361 		"", 	// out of bounds
1362 		"UTF-8",
1363 		"%A, %e. %B %Y %H:%M:%S %Z",
1364 		"%d.%m.%Y",
1365 		"%H:%M:%S",
1366 		"%I:%M:%S %p",
1367 		"vorm.",
1368 		"nachm.",
1369 
1370 		"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag",
1371 		"Samstag",
1372 
1373 		"So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.",
1374 
1375 		"Januar", "Februar", "März", "April", "Mai", "Juni",
1376 		"Juli", "August", "September", "Oktober", "November", "Dezember",
1377 
1378 		"Jan", "Feb", "Mär", "Apr", "Mai", "Jun",
1379 		"Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
1380 
1381 		"%EC, %Ey, %EY",
1382 		"%Ex",
1383 		"%Ec",
1384 		"%EX",
1385 		"%O",
1386 
1387 		",",
1388 		".",
1389 
1390 		"^[yY]",
1391 		"^[nN]",
1392 
1393 		"€",
1394 
1395 		"", 	// out of bounds
1396 		NULL,
1397 	};
1398 	test_langinfo("de_DE.UTF-8", li_de);
1399 
1400 	const char* li_de_iso[] = {
1401 		"", 	// out of bounds
1402 		"ISO8859-15",
1403 		"%A, %e. %B %Y %H:%M:%S %Z",
1404 		"%d.%m.%Y",
1405 		"%H:%M:%S",
1406 		"%I:%M:%S %p",
1407 		"vorm.",
1408 		"nachm.",
1409 
1410 		"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag",
1411 		"Samstag",
1412 
1413 		"So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.",
1414 
1415 		"Januar", "Februar", "M\xE4rz", "April", "Mai", "Juni",
1416 		"Juli", "August", "September", "Oktober", "November", "Dezember",
1417 
1418 		"Jan", "Feb", "M\xE4r", "Apr", "Mai", "Jun",
1419 		"Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
1420 
1421 		"%EC, %Ey, %EY",
1422 		"%Ex",
1423 		"%Ec",
1424 		"%EX",
1425 		"%O",
1426 
1427 		",",
1428 		".",
1429 
1430 		"^[yY]",
1431 		"^[nN]",
1432 
1433 		"\xA4",
1434 
1435 		"", 	// out of bounds
1436 		NULL
1437 	};
1438 	test_langinfo("de_DE.ISO8859-15", li_de_iso);
1439 }
1440 
1441 
1442 // #pragma mark - collation ----------------------------------------------------
1443 
1444 
1445 struct coll_data {
1446 	const char* a;
1447 	const char* b;
1448 	int result;
1449 	int err;
1450 };
1451 
1452 
1453 static int sign (int a)
1454 {
1455 	if (a < 0)
1456 		return -1;
1457 	if (a > 0)
1458 		return 1;
1459 	return 0;
1460 }
1461 
1462 
1463 void
1464 test_coll(bool useStrxfrm, const char* locale, const coll_data* coll)
1465 {
1466 	setlocale(LC_COLLATE, locale);
1467 	printf("%s in %s locale\n", useStrxfrm ? "strxfrm" : "strcoll", locale);
1468 
1469 	int problemCount = 0;
1470 	for (unsigned int i = 0; coll[i].a != NULL; ++i) {
1471 		errno = 0;
1472 		int result;
1473 		char funcCall[100];
1474 		if (useStrxfrm) {
1475 			char sortKeyA[100], sortKeyB[100];
1476 			strxfrm(sortKeyA, coll[i].a, 100);
1477 			strxfrm(sortKeyB, coll[i].b, 100);
1478 			result = sign(strcmp(sortKeyA, sortKeyB));
1479 			sprintf(funcCall, "strcmp(strxfrm(\"%s\"), strxfrm(\"%s\"))",
1480 				coll[i].a, coll[i].b);
1481 		} else {
1482 			result = sign(strcoll(coll[i].a, coll[i].b));
1483 			sprintf(funcCall, "strcoll(\"%s\", \"%s\")", coll[i].a, coll[i].b);
1484 		}
1485 
1486 		if (result != coll[i].result || errno != coll[i].err) {
1487 			printf(
1488 				"\tPROBLEM: %s = %d (expected %d), errno = %x (expected %x)\n",
1489 				funcCall, result, coll[i].result, errno, coll[i].err);
1490 			problemCount++;
1491 		}
1492 	}
1493 	if (problemCount)
1494 		printf("\t%d problem(s) found!\n", problemCount);
1495 	else
1496 		printf("\tall fine\n");
1497 }
1498 
1499 
1500 void
1501 test_collation()
1502 {
1503 	const coll_data coll_posix[] = {
1504 		{ "", "", 0, 0 },
1505 		{ "test", "test", 0, 0 },
1506 		{ "tester", "test", 1, 0 },
1507 		{ "tEst", "teSt", -1, 0 },
1508 		{ "test", "tester", -1, 0 },
1509 		{ "tast", "täst", -1, EINVAL },
1510 		{ "tæst", "test", 1, EINVAL },
1511 		{ NULL, NULL, 0, 0 },
1512 	};
1513 	test_coll(0, "POSIX", coll_posix);
1514 	test_coll(1, "POSIX", coll_posix);
1515 
1516 	const coll_data coll_en[] = {
1517 		{ "", "", 0, 0 },
1518 		{ "test", "test", 0, 0 },
1519 		{ "tester", "test", 1, 0 },
1520 		{ "tEst", "test", 1, 0 },
1521 		{ "test", "tester", -1, 0 },
1522 		{ "täst", "täst", 0, 0 },
1523 		{ "tast", "täst", -1, 0 },
1524 		{ "tbst", "täst", 1, 0 },
1525 		{ "tbst", "tæst", 1, 0 },
1526 		{ "täst", "tÄst", -1, 0 },
1527 		{ "tBst", "tÄst", 1, 0 },
1528 		{ "tBst", "täst", 1, 0 },
1529 		{ "taest", "tæst", -1, 0 },
1530 		{ "tafst", "tæst", 1, 0 },
1531 		{ "taa", "täa", -1, 0 },
1532 		{ "tab", "täb", -1, 0 },
1533 		{ "tad", "täd", -1, 0 },
1534 		{ "tae", "täe", -1, 0 },
1535 		{ "taf", "täf", -1, 0 },
1536 		{ "cote", "coté", -1, 0 },
1537 		{ "coté", "côte", -1, 0 },
1538 		{ "côte", "côté", -1, 0 },
1539 		{ NULL, NULL, 0, 0 },
1540 	};
1541 	test_coll(0, "en_US.UTF-8", coll_en);
1542 	test_coll(1, "en_US.UTF-8", coll_en);
1543 
1544 	const coll_data coll_de[] = {
1545 		{ "", "", 0, 0 },
1546 		{ "test", "test", 0, 0 },
1547 		{ "tester", "test", 1, 0 },
1548 		{ "tEst", "test", 1, 0 },
1549 		{ "test", "tester", -1, 0 },
1550 		{ "täst", "täst", 0, 0 },
1551 		{ "tast", "täst", -1, 0 },
1552 		{ "tbst", "täst", 1, 0 },
1553 		{ "tbst", "tæst", 1, 0 },
1554 		{ "täst", "tÄst", -1, 0 },
1555 		{ "tBst", "tÄst", 1, 0 },
1556 		{ "tBst", "täst", 1, 0 },
1557 		{ "taest", "tæst", -1, 0 },
1558 		{ "tafst", "tæst", 1, 0 },
1559 		{ "taa", "tä", 1, 0 },
1560 		{ "tab", "tä", 1, 0 },
1561 		{ "tad", "tä", 1, 0 },
1562 		{ "tae", "tä", 1, 0 },
1563 		{ "taf", "tä", 1, 0 },
1564 		{ "cote", "coté", -1, 0 },
1565 		{ "coté", "côte", -1, 0 },
1566 		{ "côte", "côté", -1, 0 },
1567 		{ NULL, NULL, 0, 0 },
1568 	};
1569 	test_coll(0, "de_DE.UTF-8", coll_de);
1570 	test_coll(1, "de_DE.UTF-8", coll_de);
1571 
1572 	const coll_data coll_de_phonebook[] = {
1573 		{ "", "", 0, 0 },
1574 		{ "test", "test", 0, 0 },
1575 		{ "tester", "test", 1, 0 },
1576 		{ "tEst", "test", 1, 0 },
1577 		{ "test", "tester", -1, 0 },
1578 		{ "täst", "täst", 0, 0 },
1579 		{ "tast", "täst", 1, 0 },
1580 		{ "tbst", "täst", 1, 0 },
1581 		{ "tbst", "tæst", 1, 0 },
1582 		{ "täst", "tÄst", -1, 0 },
1583 		{ "tBst", "tÄst", 1, 0 },
1584 		{ "tBst", "täst", 1, 0 },
1585 		{ "taest", "tæst", -1, 0 },
1586 		{ "tafst", "tæst", 1, 0 },
1587 		{ "taa", "tä", -1, 0 },
1588 		{ "tab", "tä", -1, 0 },
1589 		{ "tad", "tä", -1, 0 },
1590 		{ "tae", "tä", -1, 0 },
1591 		{ "taf", "tä", 1, 0 },
1592 		{ "cote", "coté", -1, 0 },
1593 		{ "coté", "côte", -1, 0 },
1594 		{ "côte", "côté", -1, 0 },
1595 		{ NULL, NULL, 0, 0 },
1596 	};
1597 	test_coll(0, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1598 	test_coll(1, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1599 
1600 	const coll_data coll_fr[] = {
1601 		{ "", "", 0, 0 },
1602 		{ "test", "test", 0, 0 },
1603 		{ "tester", "test", 1, 0 },
1604 		{ "tEst", "test", 1, 0 },
1605 		{ "test", "tester", -1, 0 },
1606 		{ "täst", "täst", 0, 0 },
1607 		{ "tast", "täst", -1, 0 },
1608 		{ "tbst", "täst", 1, 0 },
1609 		{ "tbst", "tæst", 1, 0 },
1610 		{ "täst", "tÄst", -1, 0 },
1611 		{ "tBst", "tÄst", 1, 0 },
1612 		{ "tBst", "täst", 1, 0 },
1613 		{ "taest", "tæst", -1, 0 },
1614 		{ "tafst", "tæst", 1, 0 },
1615 		{ "taa", "tä", 1, 0 },
1616 		{ "tab", "tä", 1, 0 },
1617 		{ "tad", "tä", 1, 0 },
1618 		{ "tae", "tä", 1, 0 },
1619 		{ "taf", "tä", 1, 0 },
1620 		{ "cote", "coté", -1, 0 },
1621 		{ "coté", "côte", 1, 0 },
1622 		{ "côte", "côté", -1, 0 },
1623 		{ NULL, NULL, 0, 0 },
1624 	};
1625 	// CLDR-1.9 has adjusted the defaults of fr_FR to no longer do reverse
1626 	// ordering of secondary differences (accents), but fr_CA still does that
1627 	// by default
1628 	test_coll(0, "fr_CA.UTF-8", coll_fr);
1629 	test_coll(1, "fr_CA.UTF-8", coll_fr);
1630 }
1631 
1632 
1633 // #pragma mark - time conversion ----------------------------------------------
1634 
1635 
1636 void
1637 test_localtime(const char* tz, time_t nowSecs, const tm& expected)
1638 {
1639 	setenv("TZ", tz, 1);
1640 	printf("localtime for '%s'\n", tz);
1641 
1642 	tm now;
1643 	tm* result = localtime_r(&nowSecs, &now);
1644 	int problemCount = 0;
1645 	if (result == NULL) {
1646 		printf("\tPROBLEM: localtime(\"%ld\") = NULL\n", nowSecs);
1647 		problemCount++;
1648 	}
1649 	if (now.tm_year != expected.tm_year) {
1650 		printf("\tPROBLEM: localtime().tm_year = %d (expected %d)\n",
1651 			now.tm_year, expected.tm_year);
1652 		problemCount++;
1653 	}
1654 	if (now.tm_mon != expected.tm_mon) {
1655 		printf("\tPROBLEM: localtime().tm_mon = %d (expected %d)\n",
1656 			now.tm_mon, expected.tm_mon);
1657 		problemCount++;
1658 	}
1659 	if (now.tm_mday != expected.tm_mday) {
1660 		printf("\tPROBLEM: localtime().tm_mday = %d (expected %d)\n",
1661 			now.tm_mday, expected.tm_mday);
1662 		problemCount++;
1663 	}
1664 	if (now.tm_hour != expected.tm_hour) {
1665 		printf("\tPROBLEM: localtime().tm_hour = %d (expected %d)\n",
1666 			now.tm_hour, expected.tm_hour);
1667 		problemCount++;
1668 	}
1669 	if (now.tm_min != expected.tm_min) {
1670 		printf("\tPROBLEM: localtime().tm_min = %d (expected %d)\n",
1671 			now.tm_min, expected.tm_min);
1672 		problemCount++;
1673 	}
1674 	if (now.tm_sec != expected.tm_sec) {
1675 		printf("\tPROBLEM: localtime().tm_sec = %d (expected %d)\n",
1676 			now.tm_sec, expected.tm_sec);
1677 		problemCount++;
1678 	}
1679 	if (now.tm_wday != expected.tm_wday) {
1680 		printf("\tPROBLEM: localtime().tm_wday = %d (expected %d)\n",
1681 			now.tm_wday, expected.tm_wday);
1682 		problemCount++;
1683 	}
1684 	if (now.tm_yday != expected.tm_yday) {
1685 		printf("\tPROBLEM: localtime().tm_yday = %d (expected %d)\n",
1686 			now.tm_yday, expected.tm_yday);
1687 		problemCount++;
1688 	}
1689 	if (now.tm_isdst != expected.tm_isdst) {
1690 		printf("\tPROBLEM: localtime().tm_isdst = %d (expected %d)\n",
1691 			now.tm_isdst, expected.tm_isdst);
1692 		problemCount++;
1693 	}
1694 	if (now.tm_gmtoff != expected.tm_gmtoff) {
1695 		printf("\tPROBLEM: localtime().tm_gmtoff = %d (expected %d)\n",
1696 			now.tm_gmtoff, expected.tm_gmtoff);
1697 		problemCount++;
1698 	}
1699 	if (strcasecmp(now.tm_zone, expected.tm_zone) != 0) {
1700 		printf("\tPROBLEM: localtime().tm_zone = '%s' (expected '%s')\n",
1701 			now.tm_zone, expected.tm_zone);
1702 		problemCount++;
1703 	}
1704 	if (problemCount)
1705 		printf("\t%d problem(s) found!\n", problemCount);
1706 	else
1707 		printf("\tall fine\n");
1708 }
1709 
1710 
1711 void
1712 test_gmtime(const char* tz, time_t nowSecs, const tm& expected)
1713 {
1714 	setenv("TZ", tz, 1);
1715 	printf("gmtime for '%s'\n", tz);
1716 
1717 	tm now;
1718 	tm* result = gmtime_r(&nowSecs, &now);
1719 	int problemCount = 0;
1720 	if (result == NULL) {
1721 		printf("\tPROBLEM: localtime(\"%ld\") = NULL\n", nowSecs);
1722 		problemCount++;
1723 	}
1724 	if (now.tm_year != expected.tm_year) {
1725 		printf("\tPROBLEM: localtime().tm_year = %d (expected %d)\n",
1726 			now.tm_year, expected.tm_year);
1727 		problemCount++;
1728 	}
1729 	if (now.tm_mon != expected.tm_mon) {
1730 		printf("\tPROBLEM: localtime().tm_mon = %d (expected %d)\n",
1731 			now.tm_mon, expected.tm_mon);
1732 		problemCount++;
1733 	}
1734 	if (now.tm_mday != expected.tm_mday) {
1735 		printf("\tPROBLEM: localtime().tm_mday = %d (expected %d)\n",
1736 			now.tm_mday, expected.tm_mday);
1737 		problemCount++;
1738 	}
1739 	if (now.tm_hour != expected.tm_hour) {
1740 		printf("\tPROBLEM: localtime().tm_hour = %d (expected %d)\n",
1741 			now.tm_hour, expected.tm_hour);
1742 		problemCount++;
1743 	}
1744 	if (now.tm_min != expected.tm_min) {
1745 		printf("\tPROBLEM: localtime().tm_min = %d (expected %d)\n",
1746 			now.tm_min, expected.tm_min);
1747 		problemCount++;
1748 	}
1749 	if (now.tm_sec != expected.tm_sec) {
1750 		printf("\tPROBLEM: localtime().tm_sec = %d (expected %d)\n",
1751 			now.tm_sec, expected.tm_sec);
1752 		problemCount++;
1753 	}
1754 	if (now.tm_wday != expected.tm_wday) {
1755 		printf("\tPROBLEM: localtime().tm_wday = %d (expected %d)\n",
1756 			now.tm_wday, expected.tm_wday);
1757 		problemCount++;
1758 	}
1759 	if (now.tm_yday != expected.tm_yday) {
1760 		printf("\tPROBLEM: localtime().tm_yday = %d (expected %d)\n",
1761 			now.tm_yday, expected.tm_yday);
1762 		problemCount++;
1763 	}
1764 	if (now.tm_isdst != expected.tm_isdst) {
1765 		printf("\tPROBLEM: localtime().tm_isdst = %d (expected %d)\n",
1766 			now.tm_isdst, expected.tm_isdst);
1767 		problemCount++;
1768 	}
1769 	if (now.tm_gmtoff != expected.tm_gmtoff) {
1770 		printf("\tPROBLEM: localtime().tm_gmtoff = %d (expected %d)\n",
1771 			now.tm_gmtoff, expected.tm_gmtoff);
1772 		problemCount++;
1773 	}
1774 	if (strcasecmp(now.tm_zone, expected.tm_zone) != 0) {
1775 		printf("\tPROBLEM: localtime().tm_zone = '%s' (expected '%s')\n",
1776 			now.tm_zone, expected.tm_zone);
1777 		problemCount++;
1778 	}
1779 	if (problemCount)
1780 		printf("\t%d problem(s) found!\n", problemCount);
1781 	else
1782 		printf("\tall fine\n");
1783 }
1784 
1785 
1786 void
1787 test_mktime(const char* tz, tm& tm, time_t expected, int expectedWeekDay,
1788 	int expectedYearDay)
1789 {
1790 	setenv("TZ", tz, 1);
1791 	printf("mktime for '%s'\n", tz);
1792 
1793 	time_t result = mktime(&tm);
1794 	int problemCount = 0;
1795 	if (result != expected) {
1796 		printf("\tPROBLEM: mktime() = %ld (expected %ld)\n", result, expected);
1797 		problemCount++;
1798 	}
1799 	if (tm.tm_wday != expectedWeekDay) {
1800 		printf("\tPROBLEM: mktime().tm_wday = %d (expected %d)\n",
1801 			tm.tm_wday, expectedWeekDay);
1802 		problemCount++;
1803 	}
1804 	if (tm.tm_yday != expectedYearDay) {
1805 		printf("\tPROBLEM: mktime().tm_yday = %d (expected %d)\n",
1806 			tm.tm_yday, expectedYearDay);
1807 		problemCount++;
1808 	}
1809 	if (problemCount)
1810 		printf("\t%d problem(s) found!\n", problemCount);
1811 	else
1812 		printf("\tall fine\n");
1813 }
1814 
1815 
1816 void
1817 test_timeconversions()
1818 {
1819 	setlocale(LC_ALL, "en_US");
1820 	{
1821 		time_t testTime = 1279391169;	// Sat Jul 17 18:26:09 GMT 2010
1822 		tm gtm = {
1823 			9, 26, 18, 17, 6, 110, 6, 197, 0, 0, (char*)"GMT"
1824 		};
1825 		test_localtime("GMT", testTime, gtm);
1826 		test_gmtime("GMT", testTime, gtm);
1827 		gtm.tm_wday = -1;
1828 		gtm.tm_yday = -1;
1829 		test_mktime("GMT", gtm, testTime, 6, 197);
1830 
1831 		tm gtmplus2 = {
1832 			9, 26, 16, 17, 6, 110, 6, 197, 0, -2 * 3600, (char*)"GMT+2"
1833 		};
1834 		test_localtime("GMT+2", testTime, gtmplus2);
1835 		test_gmtime("GMT+2", testTime, gtm);
1836 		gtmplus2.tm_wday = -1;
1837 		gtmplus2.tm_yday = -1;
1838 		test_mktime("GMT+2", gtmplus2, testTime, 6, 197);
1839 
1840 		tm gtmminus2 = {
1841 			9, 26, 20, 17, 6, 110, 6, 197, 0, 2 * 3600, (char*)"GMT-2"
1842 		};
1843 		test_localtime("GMT-2", testTime, gtmminus2);
1844 		test_gmtime("GMT-2", testTime, gtm);
1845 		gtmminus2.tm_wday = -1;
1846 		gtmminus2.tm_yday = -1;
1847 		test_mktime("GMT-2", gtmminus2, testTime, 6, 197);
1848 
1849 		tm btm = {
1850 			9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
1851 		};
1852 		test_localtime(":Europe/Berlin", testTime, btm);
1853 		test_gmtime(":Europe/Berlin", testTime, gtm);
1854 		btm.tm_wday = -1;
1855 		btm.tm_yday = -1;
1856 		test_mktime(":Europe/Berlin", btm, testTime, 6, 197);
1857 
1858 		tm ctm = {
1859 			9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
1860 		};
1861 		test_localtime("CET", testTime, ctm);
1862 		test_gmtime("CET", testTime, gtm);
1863 		ctm.tm_wday = -1;
1864 		ctm.tm_yday = -1;
1865 		test_mktime("CET", ctm, testTime, 6, 197);
1866 
1867 		tm latm = {
1868 			9, 26, 11, 17, 6, 110, 6, 197, 1, -7 * 3600, (char*)"PDT"
1869 		};
1870 		test_localtime(":America/Los_Angeles", testTime, latm);
1871 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1872 		latm.tm_wday = -1;
1873 		latm.tm_yday = -1;
1874 		test_mktime(":America/Los_Angeles", latm, testTime, 6, 197);
1875 
1876 		tm ttm = {
1877 			9, 26, 3, 18, 6, 110, 0, 198, 0, 9 * 3600, (char*)"GMT+09:00"
1878 		};
1879 		test_localtime(":Asia/Tokyo", testTime, ttm);
1880 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1881 		ttm.tm_wday = -1;
1882 		ttm.tm_yday = -1;
1883 		test_mktime(":Asia/Tokyo", ttm, testTime, 0, 198);
1884 	}
1885 
1886 	{
1887 		time_t testTime = 1268159169;	// Tue Mar 9 18:26:09 GMT 2010
1888 		tm gtm = {
1889 			9, 26, 18, 9, 2, 110, 2, 67, 0, 0, (char*)"GMT"
1890 		};
1891 		test_localtime("GMT", testTime, gtm);
1892 		test_gmtime("GMT", testTime, gtm);
1893 		gtm.tm_wday = -1;
1894 		gtm.tm_yday = -1;
1895 		test_mktime("GMT", gtm, testTime, 2, 67);
1896 
1897 		tm btm = {
1898 			9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET"
1899 		};
1900 		test_localtime(":Europe/Berlin", testTime, btm);
1901 		test_gmtime(":Europe/Berlin", testTime, gtm);
1902 		btm.tm_wday = -1;
1903 		btm.tm_yday = -1;
1904 		test_mktime(":Europe/Berlin", btm, testTime, 2, 67);
1905 
1906 		tm ctm = {
1907 			9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET"
1908 		};
1909 		test_localtime("CET", testTime, ctm);
1910 		test_gmtime("CET", testTime, gtm);
1911 		ctm.tm_wday = -1;
1912 		ctm.tm_yday = -1;
1913 		test_mktime("CET", ctm, testTime, 2, 67);
1914 
1915 		tm latm = {
1916 			9, 26, 10, 9, 2, 110, 2, 67, 0, -8 * 3600, (char*)"PST"
1917 		};
1918 		test_localtime(":America/Los_Angeles", testTime, latm);
1919 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1920 		latm.tm_wday = -1;
1921 		latm.tm_yday = -1;
1922 		test_mktime(":America/Los_Angeles", latm, testTime, 2, 67);
1923 
1924 		tm ttm = {
1925 			9, 26, 3, 10, 2, 110, 3, 68, 0, 9 * 3600, (char*)"GMT+09:00"
1926 		};
1927 		test_localtime(":Asia/Tokyo", testTime, ttm);
1928 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1929 		ttm.tm_wday = -1;
1930 		ttm.tm_yday = -1;
1931 		test_mktime(":Asia/Tokyo", ttm, testTime, 3, 68);
1932 	}
1933 
1934 	{
1935 		time_t testTime = 0;	// Thu Jan 1 00:00:00 GMT 1970
1936 		tm gtm = {
1937 			0, 0, 0, 1, 0, 70, 4, 0, 0, 0, (char*)"GMT"
1938 		};
1939 		test_localtime("GMT", testTime, gtm);
1940 		test_gmtime("GMT", testTime, gtm);
1941 		gtm.tm_wday = -1;
1942 		gtm.tm_yday = -1;
1943 		test_mktime("GMT", gtm, testTime, 4, 0);
1944 
1945 		tm btm = {
1946 			0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET"
1947 		};
1948 		test_localtime(":Europe/Berlin", testTime, btm);
1949 		test_gmtime(":Europe/Berlin", testTime, gtm);
1950 		btm.tm_wday = -1;
1951 		btm.tm_yday = -1;
1952 		test_mktime(":Europe/Berlin", btm, testTime, 4, 0);
1953 
1954 		tm ctm = {
1955 			0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET"
1956 		};
1957 		test_localtime("CET", testTime, ctm);
1958 		test_gmtime("CET", testTime, gtm);
1959 		ctm.tm_wday = -1;
1960 		ctm.tm_yday = -1;
1961 		test_mktime("CET", ctm, testTime, 4, 0);
1962 
1963 		tm latm = {
1964 			0, 0, 16, 31, 11, 69, 3, 364, 0, -8 * 3600, (char*)"PST"
1965 		};
1966 		test_localtime(":America/Los_Angeles", testTime, latm);
1967 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1968 		latm.tm_wday = -1;
1969 		latm.tm_yday = -1;
1970 		test_mktime(":America/Los_Angeles", latm, testTime, 3, 364);
1971 
1972 		tm ttm = {
1973 			0, 0, 9, 1, 0, 70, 4, 0, 0, 9 * 3600, (char*)"GMT+09:00"
1974 		};
1975 		test_localtime(":Asia/Tokyo", testTime, ttm);
1976 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1977 		ttm.tm_wday = -1;
1978 		ttm.tm_yday = -1;
1979 		test_mktime(":Asia/Tokyo", ttm, testTime, 4, 0);
1980 	}
1981 }
1982 
1983 
1984 // #pragma mark - printf -------------------------------------------------------
1985 
1986 
1987 struct sprintf_data {
1988 	const char* format;
1989 	double value;
1990 	const char* result;
1991 };
1992 
1993 
1994 void
1995 test_sprintf(const char* locale, const sprintf_data data[])
1996 {
1997 	setlocale(LC_ALL, locale);
1998 	printf("sprintf for '%s'\n", locale);
1999 
2000 	int problemCount = 0;
2001 	for(int i = 0; data[i].format != NULL; ++i) {
2002 		char buf[100];
2003 		if (strchr(data[i].format, 'd') != NULL)
2004 			sprintf(buf, data[i].format, (int)data[i].value);
2005 		else if (strchr(data[i].format, 'f') != NULL)
2006 			sprintf(buf, data[i].format, data[i].value);
2007 		if (strcmp(buf, data[i].result) != 0) {
2008 			printf("\tPROBLEM: sprintf(\"%s\") = \"%s\" (expected \"%s\")\n",
2009 				data[i].format, buf, data[i].result);
2010 			problemCount++;
2011 		}
2012 	}
2013 	if (problemCount)
2014 		printf("\t%d problem(s) found!\n", problemCount);
2015 	else
2016 		printf("\tall fine\n");
2017 }
2018 
2019 
2020 void
2021 test_sprintf()
2022 {
2023 	const sprintf_data sprintf_posix[] = {
2024 		{ "%d", 123, "123" },
2025 		{ "%d", -123, "-123" },
2026 		{ "%d", 123456, "123456" },
2027 		{ "%'d", 123456, "123456" },
2028 		{ "%f", 123, "123.000000" },
2029 		{ "%f", -123, "-123.000000" },
2030 		{ "%.2f", 123456.789, "123456.79" },
2031 		{ "%'.2f", 123456.789, "123456.79" },
2032 		{ NULL, 0.0, NULL }
2033 	};
2034 	test_sprintf("POSIX", sprintf_posix);
2035 
2036 	const sprintf_data sprintf_de[] = {
2037 		{ "%d", 123, "123" },
2038 		{ "%d", -123, "-123" },
2039 		{ "%d", 123456, "123456" },
2040 		{ "%'d", 123456, "123.456" },
2041 		{ "%f", 123, "123,000000" },
2042 		{ "%f", -123, "-123,000000" },
2043 		{ "%.2f", 123456.789, "123456,79" },
2044 		{ "%'.2f", 123456.789, "123.456,79" },
2045 		{ NULL, 0.0, NULL }
2046 	};
2047 	test_sprintf("de_DE.UTF-8", sprintf_de);
2048 
2049 	const sprintf_data sprintf_gu[] = {
2050 		{ "%d", 123, "123" },
2051 		{ "%d", -123, "-123" },
2052 		{ "%d", 123456, "123456" },
2053 		{ "%'d", 123456, "123,456" },
2054 		{ "%f", 123, "123.000000" },
2055 		{ "%f", -123, "-123.000000" },
2056 		{ "%.2f", 123456.789, "123456.79" },
2057 		{ "%'.2f", 123456.789, "1,23,456.79" },
2058 		{ NULL, 0.0, NULL }
2059 	};
2060 	test_sprintf("gu_IN", sprintf_gu);
2061 
2062 	const sprintf_data sprintf_nb[] = {
2063 		{ "%d", 123, "123" },
2064 		{ "%d", -123, "-123" },
2065 		{ "%d", 123456, "123456" },
2066 		{ "%'d", 123456, "123 456" },
2067 		{ "%f", 123, "123,000000" },
2068 		{ "%f", -123, "-123,000000" },
2069 		{ "%.2f", 123456.789, "123456,79" },
2070 		{ "%'.2f", 123456.789, "123 456,79" },
2071 		{ NULL, 0.0, NULL }
2072 	};
2073 	test_sprintf("nb_NO", sprintf_nb);
2074 }
2075 
2076 
2077 // #pragma mark - main ---------------------------------------------------------
2078 
2079 
2080 /*
2081  * Test several different aspects of the POSIX locale and the functions
2082  * influenced by it.
2083  */
2084 int
2085 main(void)
2086 {
2087 	test_setlocale();
2088 	test_localeconv();
2089 	test_strftime();
2090 	test_ctype();
2091 	test_wctype();
2092 	test_wctrans();
2093 	test_wcwidth();
2094 	test_langinfo();
2095 	test_collation();
2096 	test_timeconversions();
2097 	test_sprintf();
2098 
2099 	return 0;
2100 }
2101