xref: /haiku/src/tests/system/libroot/posix/locale_test.cpp (revision 4a0b7d1bd05e33ecd6b92a8e0e318af759b156d1)
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*)"રુ",
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", "lørdag 17. juli 2010 kl. 18:26:09 GMT" },
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 void
1134 test_wctrans(const char* locale, const wchar_t* text, wctrans_t transition,
1135 	const wchar_t* expectedResult)
1136 {
1137 	setlocale(LC_CTYPE, locale);
1138 	printf("towctrans(%s) of %s locale\n",
1139 		transition == _ISlower ? "tolower" : "toupper", locale);
1140 
1141 	int problemCount = 0;
1142 	wint_t wc = *text;
1143 	for (int i = 0; wc != 0; wc = *++text, ++i) {
1144 		errno = 0;
1145 		wint_t result = towctrans(wc, transition);
1146 		if (result != expectedResult[i] || errno != 0) {
1147 			printf("\tPROBLEM: result for char #%d = %x (expected %x), "
1148 					"errno = %x (expected %x)\n",
1149 				i, result, expectedResult[i], errno, 0);
1150 			problemCount++;
1151 		}
1152 	}
1153 	if (problemCount)
1154 		printf("\t%d problem(s) found!\n", problemCount);
1155 	else
1156 		printf("\tall fine\n");
1157 }
1158 
1159 
1160 void
1161 test_wctrans()
1162 {
1163 	// haiku wide chars are always in UTF32, so nothing should change between
1164 	// different locales
1165 
1166 	setlocale(LC_CTYPE, "POSIX");
1167 	printf("wctrans setup\n");
1168 
1169 	int problemCount = 0;
1170 	errno = 0;
1171 	wctrans_t toU = wctrans("toupper");
1172 	if (errno != 0 || toU != _ISupper) {
1173 		printf("\tPROBLEM: wctrans(\"upper\") = %x (expected %x), "
1174 				"errno=%x (expected %x)\n",
1175 			toU, _ISupper, errno, 0);
1176 		problemCount++;
1177 	}
1178 	errno = 0;
1179 	wctrans_t toL = wctrans("tolower");
1180 	if (errno != 0 || toL != _ISlower) {
1181 		printf("\tPROBLEM: wctrans(\"lower\") = %x (expected %x), "
1182 				"errno=%x (expected %x)\n",
1183 			toL, _ISlower, errno, 0);
1184 		problemCount++;
1185 	}
1186 	errno = 0;
1187 	wctrans_t invalid1 = wctrans(NULL);
1188 	if (errno != EINVAL || invalid1 != 0) {
1189 		printf("\tPROBLEM: wctrans(NULL) = %x (expected %x), "
1190 				"errno=%x (expected %x)\n",
1191 			invalid1, 0, errno, EINVAL);
1192 		problemCount++;
1193 	}
1194 	errno = 0;
1195 	wctrans_t invalid2 = wctrans("invalid");
1196 	if (errno != EINVAL || invalid2 != 0) {
1197 		printf("\tPROBLEM: wctrans(\"invalid\") = %x (expected %x), "
1198 				"errno=%x (expected %x)\n",
1199 			invalid2, 0, errno, EINVAL);
1200 		problemCount++;
1201 	}
1202 	if (problemCount)
1203 		printf("\t%d problem(s) found!\n", problemCount);
1204 	else
1205 		printf("\tall fine\n");
1206 
1207 	const wchar_t* text = L"Hi there, how do you do? (äÜößáéúíó€'¤¹²$%#@) 12";
1208 	const wchar_t* textU = L"HI THERE, HOW DO YOU DO? (ÄÜÖßÁÉÚÍÓ€'¤¹²$%#@) 12";
1209 	const wchar_t* textL = L"hi there, how do you do? (äüößáéúíó€'¤¹²$%#@) 12";
1210 
1211 	test_wctrans("POSIX", text, toU, textU);
1212 	test_wctrans("de_DE.ISO8859-1", text, toU, textU);
1213 	test_wctrans("de_DE.ISO8859-15", text, toU, textU);
1214 	test_wctrans("de_DE.UTF-8", text, toU, textU);
1215 	test_wctrans("fr_Fr", text, toU, textU);
1216 
1217 	test_wctrans("POSIX", text, toL, textL);
1218 	test_wctrans("de_DE.ISO8859-1", text, toL, textL);
1219 	test_wctrans("de_DE.ISO8859-15", text, toL, textL);
1220 	test_wctrans("de_DE.UTF-8", text, toL, textL);
1221 	test_wctrans("fr_Fr", text, toL, textL);
1222 }
1223 
1224 
1225 // #pragma mark - nl_langinfo --------------------------------------------------
1226 
1227 
1228 void
1229 test_langinfo(const char* locale, const char* langinfos[])
1230 {
1231 	setlocale(LC_ALL, locale);
1232 	printf("langinfo of %s locale\n", locale);
1233 
1234 	int problemCount = 0;
1235 	for (int i = -1; langinfos[i + 1] != NULL; ++i) {
1236 		const char* langinfo = nl_langinfo(i);
1237 		if (strcmp(langinfo, langinfos[i + 1]) != 0) {
1238 			printf("\tPROBLEM: langinfo for #%d = '%s' (expected '%s')\n", i,
1239 				langinfo, langinfos[i + 1]);
1240 			problemCount++;
1241 		}
1242 	}
1243 	if (problemCount)
1244 		printf("\t%d problem(s) found!\n", problemCount);
1245 	else
1246 		printf("\tall fine\n");
1247 }
1248 
1249 
1250 void
1251 test_langinfo()
1252 {
1253 	const char* li_posix[] = {
1254 		"", 	// out of bounds
1255 		"US-ASCII",
1256 		"%a %b %e %H:%M:%S %Y",
1257 		"%m/%d/%y",
1258 		"%H:%M:%S",
1259 		"%I:%M:%S %p",
1260 		"AM",
1261 		"PM",
1262 
1263 		"Sunday", "Monday", "Tuesday", "Wednesday",	"Thursday", "Friday",
1264 		"Saturday",
1265 
1266 		"Sun", "Mon", "Tue", "Wed",	"Thu", "Fri", "Sat",
1267 
1268 		"January", "February", "March", "April", "May", "June",
1269 		"July", "August", "September", "October", "November", "December",
1270 
1271 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1272 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
1273 
1274 		"%EC, %Ey, %EY",
1275 		"%Ex",
1276 		"%Ec",
1277 		"%EX",
1278 		"%O",
1279 
1280 		".",
1281 		"",
1282 
1283 		"^[yY]",
1284 		"^[nN]",
1285 
1286 		"",
1287 
1288 		"", 	// out of bounds
1289 		NULL
1290 	};
1291 	test_langinfo("POSIX", li_posix);
1292 
1293 	const char* li_de[] = {
1294 		"", 	// out of bounds
1295 		"UTF-8",
1296 		"%A, %e. %B %Y %H:%M:%S %Z",
1297 		"%d.%m.%Y",
1298 		"%H:%M:%S",
1299 		"%I:%M:%S %p",
1300 		"vorm.",
1301 		"nachm.",
1302 
1303 		"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag",
1304 		"Samstag",
1305 
1306 		"So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.",
1307 
1308 		"Januar", "Februar", "März", "April", "Mai", "Juni",
1309 		"Juli", "August", "September", "Oktober", "November", "Dezember",
1310 
1311 		"Jan", "Feb", "Mär", "Apr", "Mai", "Jun",
1312 		"Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
1313 
1314 		"%EC, %Ey, %EY",
1315 		"%Ex",
1316 		"%Ec",
1317 		"%EX",
1318 		"%O",
1319 
1320 		",",
1321 		".",
1322 
1323 		"^[yY]",
1324 		"^[nN]",
1325 
1326 		"€",
1327 
1328 		"", 	// out of bounds
1329 		NULL,
1330 	};
1331 	test_langinfo("de_DE.UTF-8", li_de);
1332 
1333 	const char* li_de_iso[] = {
1334 		"", 	// out of bounds
1335 		"ISO8859-15",
1336 		"%A, %e. %B %Y %H:%M:%S %Z",
1337 		"%d.%m.%Y",
1338 		"%H:%M:%S",
1339 		"%I:%M:%S %p",
1340 		"vorm.",
1341 		"nachm.",
1342 
1343 		"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag",
1344 		"Samstag",
1345 
1346 		"So.", "Mo.", "Di.", "Mi.", "Do.", "Fr.", "Sa.",
1347 
1348 		"Januar", "Februar", "M\xE4rz", "April", "Mai", "Juni",
1349 		"Juli", "August", "September", "Oktober", "November", "Dezember",
1350 
1351 		"Jan", "Feb", "M\xE4r", "Apr", "Mai", "Jun",
1352 		"Jul", "Aug", "Sep", "Okt", "Nov", "Dez",
1353 
1354 		"%EC, %Ey, %EY",
1355 		"%Ex",
1356 		"%Ec",
1357 		"%EX",
1358 		"%O",
1359 
1360 		",",
1361 		".",
1362 
1363 		"^[yY]",
1364 		"^[nN]",
1365 
1366 		"\xA4",
1367 
1368 		"", 	// out of bounds
1369 		NULL
1370 	};
1371 	test_langinfo("de_DE.ISO8859-15", li_de_iso);
1372 }
1373 
1374 
1375 // #pragma mark - collation ----------------------------------------------------
1376 
1377 
1378 struct coll_data {
1379 	const char* a;
1380 	const char* b;
1381 	int result;
1382 	int err;
1383 };
1384 
1385 
1386 static int sign (int a)
1387 {
1388 	if (a < 0)
1389 		return -1;
1390 	if (a > 0)
1391 		return 1;
1392 	return 0;
1393 }
1394 
1395 
1396 void
1397 test_coll(bool useStrxfrm, const char* locale, const coll_data coll[])
1398 {
1399 	setlocale(LC_COLLATE, locale);
1400 	printf("%s in %s locale\n", useStrxfrm ? "strxfrm" : "strcoll", locale);
1401 
1402 	int problemCount = 0;
1403 	for (unsigned int i = 0; i < sizeof(coll) / sizeof(coll_data); ++i) {
1404 		errno = 0;
1405 		int result;
1406 		char funcCall[100];
1407 		if (useStrxfrm) {
1408 			char sortKeyA[100], sortKeyB[100];
1409 			strxfrm(sortKeyA, coll[i].a, 100);
1410 			strxfrm(sortKeyB, coll[i].b, 100);
1411 			result = sign(strcmp(sortKeyA, sortKeyB));
1412 			sprintf(funcCall, "strcmp(strxfrm(\"%s\"), strxfrm(\"%s\"))",
1413 				coll[i].a, coll[i].b);
1414 		} else {
1415 			result = sign(strcoll(coll[i].a, coll[i].b));
1416 			sprintf(funcCall, "strcoll(\"%s\", \"%s\")", coll[i].a, coll[i].b);
1417 		}
1418 
1419 		if (result != coll[i].result || errno != coll[i].err) {
1420 			printf(
1421 				"\tPROBLEM: %s = %d (expected %d), errno = %x (expected %x)\n",
1422 				funcCall, result, coll[i].result, errno, coll[i].err);
1423 			problemCount++;
1424 		}
1425 	}
1426 	if (problemCount)
1427 		printf("\t%d problem(s) found!\n", problemCount);
1428 	else
1429 		printf("\tall fine\n");
1430 }
1431 
1432 
1433 void
1434 test_collation()
1435 {
1436 	const coll_data coll_posix[] = {
1437 		{ "", "", 0, 0 },
1438 		{ "test", "test", 0, 0 },
1439 		{ "tester", "test", 1, 0 },
1440 		{ "tEst", "teSt", -1, 0 },
1441 		{ "test", "tester", -1, 0 },
1442 		{ "tast", "täst", -1, EINVAL },
1443 		{ "tæst", "test", 1, EINVAL },
1444 	};
1445 	test_coll(0, "POSIX", coll_posix);
1446 	test_coll(1, "POSIX", coll_posix);
1447 
1448 	const coll_data coll_en[] = {
1449 		{ "", "", 0, 0 },
1450 		{ "test", "test", 0, 0 },
1451 		{ "tester", "test", 1, 0 },
1452 		{ "tEst", "test", 1, 0 },
1453 		{ "test", "tester", -1, 0 },
1454 		{ "täst", "täst", 0, 0 },
1455 		{ "tast", "täst", -1, 0 },
1456 		{ "tbst", "täst", 1, 0 },
1457 		{ "tbst", "tæst", 1, 0 },
1458 		{ "täst", "tÄst", -1, 0 },
1459 		{ "tBst", "tÄst", 1, 0 },
1460 		{ "tBst", "täst", 1, 0 },
1461 		{ "taest", "tæst", -1, 0 },
1462 		{ "tafst", "tæst", 1, 0 },
1463 		{ "taa", "täa", -1, 0 },
1464 		{ "tab", "täb", -1, 0 },
1465 		{ "tad", "täd", -1, 0 },
1466 		{ "tae", "täe", -1, 0 },
1467 		{ "taf", "täf", -1, 0 },
1468 		{ "cote", "coté", -1, 0 },
1469 		{ "coté", "côte", -1, 0 },
1470 		{ "côte", "côté", -1, 0 },
1471 	};
1472 	test_coll(0, "en_US.UTF-8", coll_en);
1473 	test_coll(1, "en_US.UTF-8", coll_en);
1474 
1475 	const coll_data coll_de[] = {
1476 		{ "", "", 0, 0 },
1477 		{ "test", "test", 0, 0 },
1478 		{ "tester", "test", 1, 0 },
1479 		{ "tEst", "test", 1, 0 },
1480 		{ "test", "tester", -1, 0 },
1481 		{ "täst", "täst", 0, 0 },
1482 		{ "tast", "täst", -1, 0 },
1483 		{ "tbst", "täst", 1, 0 },
1484 		{ "tbst", "tæst", 1, 0 },
1485 		{ "täst", "tÄst", -1, 0 },
1486 		{ "tBst", "tÄst", 1, 0 },
1487 		{ "tBst", "täst", 1, 0 },
1488 		{ "taest", "tæst", -1, 0 },
1489 		{ "tafst", "tæst", 1, 0 },
1490 		{ "taa", "tä", 1, 0 },
1491 		{ "tab", "tä", 1, 0 },
1492 		{ "tad", "tä", 1, 0 },
1493 		{ "tae", "tä", 1, 0 },
1494 		{ "taf", "tä", 1, 0 },
1495 		{ "cote", "coté", -1, 0 },
1496 		{ "coté", "côte", -1, 0 },
1497 		{ "côte", "côté", -1, 0 },
1498 	};
1499 	test_coll(0, "de_DE.UTF-8", coll_de);
1500 	test_coll(1, "de_DE.UTF-8", coll_de);
1501 
1502 	const coll_data coll_de_phonebook[] = {
1503 		{ "", "", 0, 0 },
1504 		{ "test", "test", 0, 0 },
1505 		{ "tester", "test", 1, 0 },
1506 		{ "tEst", "test", 1, 0 },
1507 		{ "test", "tester", -1, 0 },
1508 		{ "täst", "täst", 0, 0 },
1509 		{ "tast", "täst", 1, 0 },
1510 		{ "tbst", "täst", 1, 0 },
1511 		{ "tbst", "tæst", 1, 0 },
1512 		{ "täst", "tÄst", -1, 0 },
1513 		{ "tBst", "tÄst", 1, 0 },
1514 		{ "tBst", "täst", 1, 0 },
1515 		{ "taest", "tæst", -1, 0 },
1516 		{ "tafst", "tæst", 1, 0 },
1517 		{ "taa", "tä", -1, 0 },
1518 		{ "tab", "tä", -1, 0 },
1519 		{ "tad", "tä", -1, 0 },
1520 		{ "tae", "tä", -1, 0 },
1521 		{ "taf", "tä", 1, 0 },
1522 		{ "cote", "coté", -1, 0 },
1523 		{ "coté", "côte", -1, 0 },
1524 		{ "côte", "côté", -1, 0 },
1525 	};
1526 	test_coll(0, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1527 	test_coll(1, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1528 
1529 	const coll_data coll_fr[] = {
1530 		{ "", "", 0, 0 },
1531 		{ "test", "test", 0, 0 },
1532 		{ "tester", "test", 1, 0 },
1533 		{ "tEst", "test", 1, 0 },
1534 		{ "test", "tester", -1, 0 },
1535 		{ "täst", "täst", 0, 0 },
1536 		{ "tast", "täst", -1, 0 },
1537 		{ "tbst", "täst", 1, 0 },
1538 		{ "tbst", "tæst", 1, 0 },
1539 		{ "täst", "tÄst", -1, 0 },
1540 		{ "tBst", "tÄst", 1, 0 },
1541 		{ "tBst", "täst", 1, 0 },
1542 		{ "taest", "tæst", -1, 0 },
1543 		{ "tafst", "tæst", 1, 0 },
1544 		{ "taa", "tä", 1, 0 },
1545 		{ "tab", "tä", 1, 0 },
1546 		{ "tad", "tä", 1, 0 },
1547 		{ "tae", "tä", 1, 0 },
1548 		{ "taf", "tä", 1, 0 },
1549 		{ "cote", "coté", -1, 0 },
1550 		{ "coté", "côte", 1, 0 },
1551 		{ "côte", "côté", -1, 0 },
1552 	};
1553 	test_coll(0, "fr_FR.UTF-8", coll_fr);
1554 	test_coll(1, "fr_FR.UTF-8", coll_fr);
1555 }
1556 
1557 
1558 // #pragma mark - time conversion ----------------------------------------------
1559 
1560 
1561 void
1562 test_localtime(const char* tz, time_t nowSecs, const tm& expected)
1563 {
1564 	setenv("TZ", tz, 1);
1565 	printf("localtime for '%s'\n", tz);
1566 
1567 	tm now;
1568 	tm* result = localtime_r(&nowSecs, &now);
1569 	int problemCount = 0;
1570 	if (result == NULL) {
1571 		printf("\tPROBLEM: localtime(\"%ld\") = NULL\n", nowSecs);
1572 		problemCount++;
1573 	}
1574 	if (now.tm_year != expected.tm_year) {
1575 		printf("\tPROBLEM: localtime().tm_year = %d (expected %d)\n",
1576 			now.tm_year, expected.tm_year);
1577 		problemCount++;
1578 	}
1579 	if (now.tm_mon != expected.tm_mon) {
1580 		printf("\tPROBLEM: localtime().tm_mon = %d (expected %d)\n",
1581 			now.tm_mon, expected.tm_mon);
1582 		problemCount++;
1583 	}
1584 	if (now.tm_mday != expected.tm_mday) {
1585 		printf("\tPROBLEM: localtime().tm_mday = %d (expected %d)\n",
1586 			now.tm_mday, expected.tm_mday);
1587 		problemCount++;
1588 	}
1589 	if (now.tm_hour != expected.tm_hour) {
1590 		printf("\tPROBLEM: localtime().tm_hour = %d (expected %d)\n",
1591 			now.tm_hour, expected.tm_hour);
1592 		problemCount++;
1593 	}
1594 	if (now.tm_min != expected.tm_min) {
1595 		printf("\tPROBLEM: localtime().tm_min = %d (expected %d)\n",
1596 			now.tm_min, expected.tm_min);
1597 		problemCount++;
1598 	}
1599 	if (now.tm_sec != expected.tm_sec) {
1600 		printf("\tPROBLEM: localtime().tm_sec = %d (expected %d)\n",
1601 			now.tm_sec, expected.tm_sec);
1602 		problemCount++;
1603 	}
1604 	if (now.tm_wday != expected.tm_wday) {
1605 		printf("\tPROBLEM: localtime().tm_wday = %d (expected %d)\n",
1606 			now.tm_wday, expected.tm_wday);
1607 		problemCount++;
1608 	}
1609 	if (now.tm_yday != expected.tm_yday) {
1610 		printf("\tPROBLEM: localtime().tm_yday = %d (expected %d)\n",
1611 			now.tm_yday, expected.tm_yday);
1612 		problemCount++;
1613 	}
1614 	if (now.tm_isdst != expected.tm_isdst) {
1615 		printf("\tPROBLEM: localtime().tm_isdst = %d (expected %d)\n",
1616 			now.tm_isdst, expected.tm_isdst);
1617 		problemCount++;
1618 	}
1619 	if (now.tm_gmtoff != expected.tm_gmtoff) {
1620 		printf("\tPROBLEM: localtime().tm_gmtoff = %d (expected %d)\n",
1621 			now.tm_gmtoff, expected.tm_gmtoff);
1622 		problemCount++;
1623 	}
1624 	if (strcasecmp(now.tm_zone, expected.tm_zone) != 0) {
1625 		printf("\tPROBLEM: localtime().tm_zone = '%s' (expected '%s')\n",
1626 			now.tm_zone, expected.tm_zone);
1627 		problemCount++;
1628 	}
1629 	if (problemCount)
1630 		printf("\t%d problem(s) found!\n", problemCount);
1631 	else
1632 		printf("\tall fine\n");
1633 }
1634 
1635 
1636 void
1637 test_gmtime(const char* tz, time_t nowSecs, const tm& expected)
1638 {
1639 	setenv("TZ", tz, 1);
1640 	printf("gmtime for '%s'\n", tz);
1641 
1642 	tm now;
1643 	tm* result = gmtime_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_mktime(const char* tz, tm& tm, time_t expected, int expectedWeekDay,
1713 	int expectedYearDay)
1714 {
1715 	setenv("TZ", tz, 1);
1716 	printf("mktime for '%s'\n", tz);
1717 
1718 	time_t result = mktime(&tm);
1719 	int problemCount = 0;
1720 	if (result != expected) {
1721 		printf("\tPROBLEM: mktime() = %ld (expected %ld)\n", result, expected);
1722 		problemCount++;
1723 	}
1724 	if (tm.tm_wday != expectedWeekDay) {
1725 		printf("\tPROBLEM: mktime().tm_wday = %d (expected %d)\n",
1726 			tm.tm_wday, expectedWeekDay);
1727 		problemCount++;
1728 	}
1729 	if (tm.tm_yday != expectedYearDay) {
1730 		printf("\tPROBLEM: mktime().tm_yday = %d (expected %d)\n",
1731 			tm.tm_yday, expectedYearDay);
1732 		problemCount++;
1733 	}
1734 	if (problemCount)
1735 		printf("\t%d problem(s) found!\n", problemCount);
1736 	else
1737 		printf("\tall fine\n");
1738 }
1739 
1740 
1741 void
1742 test_timeconversions()
1743 {
1744 	setlocale(LC_ALL, "");
1745 	{
1746 		time_t testTime = 1279391169;	// Sat Jul 17 18:26:09 GMT 2010
1747 		tm gtm = {
1748 			9, 26, 18, 17, 6, 110, 6, 197, 0, 0, (char*)"GMT"
1749 		};
1750 		test_localtime("GMT", testTime, gtm);
1751 		test_gmtime("GMT", testTime, gtm);
1752 		gtm.tm_wday = -1;
1753 		gtm.tm_yday = -1;
1754 		test_mktime("GMT", gtm, testTime, 6, 197);
1755 		tm btm = {
1756 			9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
1757 		};
1758 		test_localtime("Europe/Berlin", testTime, btm);
1759 		test_gmtime("Europe/Berlin", testTime, gtm);
1760 		btm.tm_wday = -1;
1761 		btm.tm_yday = -1;
1762 		test_mktime("Europe/Berlin", btm, testTime, 6, 197);
1763 		tm latm = {
1764 			9, 26, 11, 17, 6, 110, 6, 197, 1, -7 * 3600, (char*)"PDT"
1765 		};
1766 		test_localtime("America/Los_Angeles", testTime, latm);
1767 		test_gmtime("America/Los_Angeles", testTime, gtm);
1768 		latm.tm_wday = -1;
1769 		latm.tm_yday = -1;
1770 		test_mktime("America/Los_Angeles", latm, testTime, 6, 197);
1771 		tm ttm = {
1772 			9, 26, 3, 18, 6, 110, 0, 198, 0, 9 * 3600, (char*)"JST"
1773 		};
1774 		test_localtime("Asia/Tokyo", testTime, ttm);
1775 		test_gmtime("Asia/Tokyo", testTime, gtm);
1776 		ttm.tm_wday = -1;
1777 		ttm.tm_yday = -1;
1778 		test_mktime("Asia/Tokyo", ttm, testTime, 0, 198);
1779 	}
1780 
1781 	{
1782 		time_t testTime = 1268159169;	// Tue Mar 9 18:26:09 GMT 2010
1783 		tm gtm = {
1784 			9, 26, 18, 9, 2, 110, 2, 67, 0, 0, (char*)"GMT"
1785 		};
1786 		test_localtime("GMT", testTime, gtm);
1787 		test_gmtime("GMT", testTime, gtm);
1788 		gtm.tm_wday = -1;
1789 		gtm.tm_yday = -1;
1790 		test_mktime("GMT", gtm, testTime, 2, 67);
1791 		tm btm = {
1792 			9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET"
1793 		};
1794 		test_localtime("Europe/Berlin", testTime, btm);
1795 		test_gmtime("Europe/Berlin", testTime, gtm);
1796 		btm.tm_wday = -1;
1797 		btm.tm_yday = -1;
1798 		test_mktime("Europe/Berlin", btm, testTime, 2, 67);
1799 		tm latm = {
1800 			9, 26, 10, 9, 2, 110, 2, 67, 0, -8 * 3600, (char*)"PST"
1801 		};
1802 		test_localtime("America/Los_Angeles", testTime, latm);
1803 		test_gmtime("America/Los_Angeles", testTime, gtm);
1804 		latm.tm_wday = -1;
1805 		latm.tm_yday = -1;
1806 		test_mktime("America/Los_Angeles", latm, testTime, 2, 67);
1807 		tm ttm = {
1808 			9, 26, 3, 10, 2, 110, 3, 68, 0, 9 * 3600, (char*)"JST"
1809 		};
1810 		test_localtime("Asia/Tokyo", testTime, ttm);
1811 		test_gmtime("Asia/Tokyo", testTime, gtm);
1812 		ttm.tm_wday = -1;
1813 		ttm.tm_yday = -1;
1814 		test_mktime("Asia/Tokyo", ttm, testTime, 3, 68);
1815 	}
1816 
1817 	{
1818 		time_t testTime = 0;	// Thu Jan 1 00:00:00 GMT 1970
1819 		tm gtm = {
1820 			0, 0, 0, 1, 0, 70, 4, 0, 0, 0, (char*)"GMT"
1821 		};
1822 		test_localtime("GMT", testTime, gtm);
1823 		test_gmtime("GMT", testTime, gtm);
1824 		gtm.tm_wday = -1;
1825 		gtm.tm_yday = -1;
1826 		test_mktime("GMT", gtm, testTime, 4, 0);
1827 		tm btm = {
1828 			0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET"
1829 		};
1830 		test_localtime("Europe/Berlin", testTime, btm);
1831 		test_gmtime("Europe/Berlin", testTime, gtm);
1832 		btm.tm_wday = -1;
1833 		btm.tm_yday = -1;
1834 		test_mktime("Europe/Berlin", btm, testTime, 4, 0);
1835 		tm latm = {
1836 			0, 0, 16, 31, 11, 69, 3, 364, 0, -8 * 3600, (char*)"PST"
1837 		};
1838 		test_localtime("America/Los_Angeles", testTime, latm);
1839 		test_gmtime("America/Los_Angeles", testTime, gtm);
1840 		latm.tm_wday = -1;
1841 		latm.tm_yday = -1;
1842 		test_mktime("America/Los_Angeles", latm, testTime, 3, 364);
1843 		tm ttm = {
1844 			0, 0, 9, 1, 0, 70, 4, 0, 0, 9 * 3600, (char*)"JST"
1845 		};
1846 		test_localtime("Asia/Tokyo", testTime, ttm);
1847 		test_gmtime("Asia/Tokyo", testTime, gtm);
1848 		ttm.tm_wday = -1;
1849 		ttm.tm_yday = -1;
1850 		test_mktime("Asia/Tokyo", ttm, testTime, 4, 0);
1851 	}
1852 }
1853 
1854 
1855 // #pragma mark - printf -------------------------------------------------------
1856 
1857 
1858 struct sprintf_data {
1859 	const char* format;
1860 	double value;
1861 	const char* result;
1862 };
1863 
1864 
1865 void
1866 test_sprintf(const char* locale, const sprintf_data data[])
1867 {
1868 	setlocale(LC_ALL, locale);
1869 	printf("sprintf for '%s'\n", locale);
1870 
1871 	int problemCount = 0;
1872 	for(int i = 0; data[i].format != NULL; ++i) {
1873 		char buf[100];
1874 		if (strchr(data[i].format, 'd') != NULL)
1875 			sprintf(buf, data[i].format, (int)data[i].value);
1876 		else if (strchr(data[i].format, 'f') != NULL)
1877 			sprintf(buf, data[i].format, data[i].value);
1878 		if (strcmp(buf, data[i].result) != 0) {
1879 			printf("\tPROBLEM: sprintf(\"%s\") = \"%s\" (expected \"%s\")\n",
1880 				data[i].format, buf, data[i].result);
1881 			problemCount++;
1882 		}
1883 	}
1884 	if (problemCount)
1885 		printf("\t%d problem(s) found!\n", problemCount);
1886 	else
1887 		printf("\tall fine\n");
1888 }
1889 
1890 
1891 void
1892 test_sprintf()
1893 {
1894 	const sprintf_data sprintf_posix[] = {
1895 		{ "%d", 123, "123" },
1896 		{ "%d", -123, "-123" },
1897 		{ "%d", 123456, "123456" },
1898 		{ "%'d", 123456, "123456" },
1899 		{ "%f", 123, "123.000000" },
1900 		{ "%f", -123, "-123.000000" },
1901 		{ "%.2f", 123456.789, "123456.79" },
1902 		{ "%'.2f", 123456.789, "123456.79" },
1903 		{ NULL, 0.0, NULL }
1904 	};
1905 	test_sprintf("POSIX", sprintf_posix);
1906 
1907 	const sprintf_data sprintf_de[] = {
1908 		{ "%d", 123, "123" },
1909 		{ "%d", -123, "-123" },
1910 		{ "%d", 123456, "123456" },
1911 		{ "%'d", 123456, "123.456" },
1912 		{ "%f", 123, "123,000000" },
1913 		{ "%f", -123, "-123,000000" },
1914 		{ "%.2f", 123456.789, "123456,79" },
1915 		{ "%'.2f", 123456.789, "123.456,79" },
1916 		{ NULL, 0.0, NULL }
1917 	};
1918 	test_sprintf("de_DE.UTF-8", sprintf_de);
1919 
1920 	const sprintf_data sprintf_gu[] = {
1921 		{ "%d", 123, "123" },
1922 		{ "%d", -123, "-123" },
1923 		{ "%d", 123456, "123456" },
1924 		{ "%'d", 123456, "123,456" },
1925 		{ "%f", 123, "123.000000" },
1926 		{ "%f", -123, "-123.000000" },
1927 		{ "%.2f", 123456.789, "123456.79" },
1928 		{ "%'.2f", 123456.789, "1,23,456.79" },
1929 		{ NULL, 0.0, NULL }
1930 	};
1931 	test_sprintf("gu_IN", sprintf_gu);
1932 
1933 	const sprintf_data sprintf_nb[] = {
1934 		{ "%d", 123, "123" },
1935 		{ "%d", -123, "-123" },
1936 		{ "%d", 123456, "123456" },
1937 		{ "%'d", 123456, "123 456" },
1938 		{ "%f", 123, "123,000000" },
1939 		{ "%f", -123, "-123,000000" },
1940 		{ "%.2f", 123456.789, "123456,79" },
1941 		{ "%'.2f", 123456.789, "123 456,79" },
1942 		{ NULL, 0.0, NULL }
1943 	};
1944 	test_sprintf("nb_NO", sprintf_nb);
1945 }
1946 
1947 
1948 // #pragma mark - main ---------------------------------------------------------
1949 
1950 
1951 /*
1952  * Test several different aspects of the POSIX locale and the functions
1953  * influenced by it.
1954  */
1955 int
1956 main(void)
1957 {
1958 	test_setlocale();
1959 	test_localeconv();
1960 	test_strftime();
1961 	test_ctype();
1962 	test_wctype();
1963 	test_wctrans();
1964 	test_langinfo();
1965 	test_collation();
1966 	test_timeconversions();
1967 	test_sprintf();
1968 
1969 	return 0;
1970 }
1971