xref: /haiku/src/tests/system/libroot/posix/locale_test.cpp (revision 1c09002cbee8e797a0f8bbfc5678dfadd39ee1a7)
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 // #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; i < sizeof(coll) / sizeof(coll_data); ++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 	};
1512 	test_coll(0, "POSIX", coll_posix);
1513 	test_coll(1, "POSIX", coll_posix);
1514 
1515 	const coll_data coll_en[] = {
1516 		{ "", "", 0, 0 },
1517 		{ "test", "test", 0, 0 },
1518 		{ "tester", "test", 1, 0 },
1519 		{ "tEst", "test", 1, 0 },
1520 		{ "test", "tester", -1, 0 },
1521 		{ "täst", "täst", 0, 0 },
1522 		{ "tast", "täst", -1, 0 },
1523 		{ "tbst", "täst", 1, 0 },
1524 		{ "tbst", "tæst", 1, 0 },
1525 		{ "täst", "tÄst", -1, 0 },
1526 		{ "tBst", "tÄst", 1, 0 },
1527 		{ "tBst", "täst", 1, 0 },
1528 		{ "taest", "tæst", -1, 0 },
1529 		{ "tafst", "tæst", 1, 0 },
1530 		{ "taa", "täa", -1, 0 },
1531 		{ "tab", "täb", -1, 0 },
1532 		{ "tad", "täd", -1, 0 },
1533 		{ "tae", "täe", -1, 0 },
1534 		{ "taf", "täf", -1, 0 },
1535 		{ "cote", "coté", -1, 0 },
1536 		{ "coté", "côte", -1, 0 },
1537 		{ "côte", "côté", -1, 0 },
1538 	};
1539 	test_coll(0, "en_US.UTF-8", coll_en);
1540 	test_coll(1, "en_US.UTF-8", coll_en);
1541 
1542 	const coll_data coll_de[] = {
1543 		{ "", "", 0, 0 },
1544 		{ "test", "test", 0, 0 },
1545 		{ "tester", "test", 1, 0 },
1546 		{ "tEst", "test", 1, 0 },
1547 		{ "test", "tester", -1, 0 },
1548 		{ "täst", "täst", 0, 0 },
1549 		{ "tast", "täst", -1, 0 },
1550 		{ "tbst", "täst", 1, 0 },
1551 		{ "tbst", "tæst", 1, 0 },
1552 		{ "täst", "tÄst", -1, 0 },
1553 		{ "tBst", "tÄst", 1, 0 },
1554 		{ "tBst", "täst", 1, 0 },
1555 		{ "taest", "tæst", -1, 0 },
1556 		{ "tafst", "tæst", 1, 0 },
1557 		{ "taa", "tä", 1, 0 },
1558 		{ "tab", "tä", 1, 0 },
1559 		{ "tad", "tä", 1, 0 },
1560 		{ "tae", "tä", 1, 0 },
1561 		{ "taf", "tä", 1, 0 },
1562 		{ "cote", "coté", -1, 0 },
1563 		{ "coté", "côte", -1, 0 },
1564 		{ "côte", "côté", -1, 0 },
1565 	};
1566 	test_coll(0, "de_DE.UTF-8", coll_de);
1567 	test_coll(1, "de_DE.UTF-8", coll_de);
1568 
1569 	const coll_data coll_de_phonebook[] = {
1570 		{ "", "", 0, 0 },
1571 		{ "test", "test", 0, 0 },
1572 		{ "tester", "test", 1, 0 },
1573 		{ "tEst", "test", 1, 0 },
1574 		{ "test", "tester", -1, 0 },
1575 		{ "täst", "täst", 0, 0 },
1576 		{ "tast", "täst", 1, 0 },
1577 		{ "tbst", "täst", 1, 0 },
1578 		{ "tbst", "tæst", 1, 0 },
1579 		{ "täst", "tÄst", -1, 0 },
1580 		{ "tBst", "tÄst", 1, 0 },
1581 		{ "tBst", "täst", 1, 0 },
1582 		{ "taest", "tæst", -1, 0 },
1583 		{ "tafst", "tæst", 1, 0 },
1584 		{ "taa", "tä", -1, 0 },
1585 		{ "tab", "tä", -1, 0 },
1586 		{ "tad", "tä", -1, 0 },
1587 		{ "tae", "tä", -1, 0 },
1588 		{ "taf", "tä", 1, 0 },
1589 		{ "cote", "coté", -1, 0 },
1590 		{ "coté", "côte", -1, 0 },
1591 		{ "côte", "côté", -1, 0 },
1592 	};
1593 	test_coll(0, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1594 	test_coll(1, "de_DE.UTF-8@collation=phonebook", coll_de_phonebook);
1595 
1596 	const coll_data coll_fr[] = {
1597 		{ "", "", 0, 0 },
1598 		{ "test", "test", 0, 0 },
1599 		{ "tester", "test", 1, 0 },
1600 		{ "tEst", "test", 1, 0 },
1601 		{ "test", "tester", -1, 0 },
1602 		{ "täst", "täst", 0, 0 },
1603 		{ "tast", "täst", -1, 0 },
1604 		{ "tbst", "täst", 1, 0 },
1605 		{ "tbst", "tæst", 1, 0 },
1606 		{ "täst", "tÄst", -1, 0 },
1607 		{ "tBst", "tÄst", 1, 0 },
1608 		{ "tBst", "täst", 1, 0 },
1609 		{ "taest", "tæst", -1, 0 },
1610 		{ "tafst", "tæst", 1, 0 },
1611 		{ "taa", "tä", 1, 0 },
1612 		{ "tab", "tä", 1, 0 },
1613 		{ "tad", "tä", 1, 0 },
1614 		{ "tae", "tä", 1, 0 },
1615 		{ "taf", "tä", 1, 0 },
1616 		{ "cote", "coté", -1, 0 },
1617 		{ "coté", "côte", 1, 0 },
1618 		{ "côte", "côté", -1, 0 },
1619 	};
1620 	test_coll(0, "fr_FR.UTF-8", coll_fr);
1621 	test_coll(1, "fr_FR.UTF-8", coll_fr);
1622 }
1623 
1624 
1625 // #pragma mark - time conversion ----------------------------------------------
1626 
1627 
1628 void
1629 test_localtime(const char* tz, time_t nowSecs, const tm& expected)
1630 {
1631 	setenv("TZ", tz, 1);
1632 	printf("localtime for '%s'\n", tz);
1633 
1634 	tm now;
1635 	tm* result = localtime_r(&nowSecs, &now);
1636 	int problemCount = 0;
1637 	if (result == NULL) {
1638 		printf("\tPROBLEM: localtime(\"%ld\") = NULL\n", nowSecs);
1639 		problemCount++;
1640 	}
1641 	if (now.tm_year != expected.tm_year) {
1642 		printf("\tPROBLEM: localtime().tm_year = %d (expected %d)\n",
1643 			now.tm_year, expected.tm_year);
1644 		problemCount++;
1645 	}
1646 	if (now.tm_mon != expected.tm_mon) {
1647 		printf("\tPROBLEM: localtime().tm_mon = %d (expected %d)\n",
1648 			now.tm_mon, expected.tm_mon);
1649 		problemCount++;
1650 	}
1651 	if (now.tm_mday != expected.tm_mday) {
1652 		printf("\tPROBLEM: localtime().tm_mday = %d (expected %d)\n",
1653 			now.tm_mday, expected.tm_mday);
1654 		problemCount++;
1655 	}
1656 	if (now.tm_hour != expected.tm_hour) {
1657 		printf("\tPROBLEM: localtime().tm_hour = %d (expected %d)\n",
1658 			now.tm_hour, expected.tm_hour);
1659 		problemCount++;
1660 	}
1661 	if (now.tm_min != expected.tm_min) {
1662 		printf("\tPROBLEM: localtime().tm_min = %d (expected %d)\n",
1663 			now.tm_min, expected.tm_min);
1664 		problemCount++;
1665 	}
1666 	if (now.tm_sec != expected.tm_sec) {
1667 		printf("\tPROBLEM: localtime().tm_sec = %d (expected %d)\n",
1668 			now.tm_sec, expected.tm_sec);
1669 		problemCount++;
1670 	}
1671 	if (now.tm_wday != expected.tm_wday) {
1672 		printf("\tPROBLEM: localtime().tm_wday = %d (expected %d)\n",
1673 			now.tm_wday, expected.tm_wday);
1674 		problemCount++;
1675 	}
1676 	if (now.tm_yday != expected.tm_yday) {
1677 		printf("\tPROBLEM: localtime().tm_yday = %d (expected %d)\n",
1678 			now.tm_yday, expected.tm_yday);
1679 		problemCount++;
1680 	}
1681 	if (now.tm_isdst != expected.tm_isdst) {
1682 		printf("\tPROBLEM: localtime().tm_isdst = %d (expected %d)\n",
1683 			now.tm_isdst, expected.tm_isdst);
1684 		problemCount++;
1685 	}
1686 	if (now.tm_gmtoff != expected.tm_gmtoff) {
1687 		printf("\tPROBLEM: localtime().tm_gmtoff = %d (expected %d)\n",
1688 			now.tm_gmtoff, expected.tm_gmtoff);
1689 		problemCount++;
1690 	}
1691 	if (strcasecmp(now.tm_zone, expected.tm_zone) != 0) {
1692 		printf("\tPROBLEM: localtime().tm_zone = '%s' (expected '%s')\n",
1693 			now.tm_zone, expected.tm_zone);
1694 		problemCount++;
1695 	}
1696 	if (problemCount)
1697 		printf("\t%d problem(s) found!\n", problemCount);
1698 	else
1699 		printf("\tall fine\n");
1700 }
1701 
1702 
1703 void
1704 test_gmtime(const char* tz, time_t nowSecs, const tm& expected)
1705 {
1706 	setenv("TZ", tz, 1);
1707 	printf("gmtime for '%s'\n", tz);
1708 
1709 	tm now;
1710 	tm* result = gmtime_r(&nowSecs, &now);
1711 	int problemCount = 0;
1712 	if (result == NULL) {
1713 		printf("\tPROBLEM: localtime(\"%ld\") = NULL\n", nowSecs);
1714 		problemCount++;
1715 	}
1716 	if (now.tm_year != expected.tm_year) {
1717 		printf("\tPROBLEM: localtime().tm_year = %d (expected %d)\n",
1718 			now.tm_year, expected.tm_year);
1719 		problemCount++;
1720 	}
1721 	if (now.tm_mon != expected.tm_mon) {
1722 		printf("\tPROBLEM: localtime().tm_mon = %d (expected %d)\n",
1723 			now.tm_mon, expected.tm_mon);
1724 		problemCount++;
1725 	}
1726 	if (now.tm_mday != expected.tm_mday) {
1727 		printf("\tPROBLEM: localtime().tm_mday = %d (expected %d)\n",
1728 			now.tm_mday, expected.tm_mday);
1729 		problemCount++;
1730 	}
1731 	if (now.tm_hour != expected.tm_hour) {
1732 		printf("\tPROBLEM: localtime().tm_hour = %d (expected %d)\n",
1733 			now.tm_hour, expected.tm_hour);
1734 		problemCount++;
1735 	}
1736 	if (now.tm_min != expected.tm_min) {
1737 		printf("\tPROBLEM: localtime().tm_min = %d (expected %d)\n",
1738 			now.tm_min, expected.tm_min);
1739 		problemCount++;
1740 	}
1741 	if (now.tm_sec != expected.tm_sec) {
1742 		printf("\tPROBLEM: localtime().tm_sec = %d (expected %d)\n",
1743 			now.tm_sec, expected.tm_sec);
1744 		problemCount++;
1745 	}
1746 	if (now.tm_wday != expected.tm_wday) {
1747 		printf("\tPROBLEM: localtime().tm_wday = %d (expected %d)\n",
1748 			now.tm_wday, expected.tm_wday);
1749 		problemCount++;
1750 	}
1751 	if (now.tm_yday != expected.tm_yday) {
1752 		printf("\tPROBLEM: localtime().tm_yday = %d (expected %d)\n",
1753 			now.tm_yday, expected.tm_yday);
1754 		problemCount++;
1755 	}
1756 	if (now.tm_isdst != expected.tm_isdst) {
1757 		printf("\tPROBLEM: localtime().tm_isdst = %d (expected %d)\n",
1758 			now.tm_isdst, expected.tm_isdst);
1759 		problemCount++;
1760 	}
1761 	if (now.tm_gmtoff != expected.tm_gmtoff) {
1762 		printf("\tPROBLEM: localtime().tm_gmtoff = %d (expected %d)\n",
1763 			now.tm_gmtoff, expected.tm_gmtoff);
1764 		problemCount++;
1765 	}
1766 	if (strcasecmp(now.tm_zone, expected.tm_zone) != 0) {
1767 		printf("\tPROBLEM: localtime().tm_zone = '%s' (expected '%s')\n",
1768 			now.tm_zone, expected.tm_zone);
1769 		problemCount++;
1770 	}
1771 	if (problemCount)
1772 		printf("\t%d problem(s) found!\n", problemCount);
1773 	else
1774 		printf("\tall fine\n");
1775 }
1776 
1777 
1778 void
1779 test_mktime(const char* tz, tm& tm, time_t expected, int expectedWeekDay,
1780 	int expectedYearDay)
1781 {
1782 	setenv("TZ", tz, 1);
1783 	printf("mktime for '%s'\n", tz);
1784 
1785 	time_t result = mktime(&tm);
1786 	int problemCount = 0;
1787 	if (result != expected) {
1788 		printf("\tPROBLEM: mktime() = %ld (expected %ld)\n", result, expected);
1789 		problemCount++;
1790 	}
1791 	if (tm.tm_wday != expectedWeekDay) {
1792 		printf("\tPROBLEM: mktime().tm_wday = %d (expected %d)\n",
1793 			tm.tm_wday, expectedWeekDay);
1794 		problemCount++;
1795 	}
1796 	if (tm.tm_yday != expectedYearDay) {
1797 		printf("\tPROBLEM: mktime().tm_yday = %d (expected %d)\n",
1798 			tm.tm_yday, expectedYearDay);
1799 		problemCount++;
1800 	}
1801 	if (problemCount)
1802 		printf("\t%d problem(s) found!\n", problemCount);
1803 	else
1804 		printf("\tall fine\n");
1805 }
1806 
1807 
1808 void
1809 test_timeconversions()
1810 {
1811 	setlocale(LC_ALL, "");
1812 	{
1813 		time_t testTime = 1279391169;	// Sat Jul 17 18:26:09 GMT 2010
1814 		tm gtm = {
1815 			9, 26, 18, 17, 6, 110, 6, 197, 0, 0, (char*)"GMT"
1816 		};
1817 		test_localtime("GMT", testTime, gtm);
1818 		test_gmtime("GMT", testTime, gtm);
1819 		gtm.tm_wday = -1;
1820 		gtm.tm_yday = -1;
1821 		test_mktime("GMT", gtm, testTime, 6, 197);
1822 
1823 		tm gtmplus2 = {
1824 			9, 26, 16, 17, 6, 110, 6, 197, 0, -2 * 3600, (char*)"GMT+2"
1825 		};
1826 		test_localtime("GMT+2", testTime, gtmplus2);
1827 		test_gmtime("GMT+2", testTime, gtm);
1828 		gtmplus2.tm_wday = -1;
1829 		gtmplus2.tm_yday = -1;
1830 		test_mktime("GMT+2", gtmplus2, testTime, 6, 197);
1831 
1832 		tm gtmminus2 = {
1833 			9, 26, 20, 17, 6, 110, 6, 197, 0, 2 * 3600, (char*)"GMT-2"
1834 		};
1835 		test_localtime("GMT-2", testTime, gtmminus2);
1836 		test_gmtime("GMT-2", testTime, gtm);
1837 		gtmminus2.tm_wday = -1;
1838 		gtmminus2.tm_yday = -1;
1839 		test_mktime("GMT-2", gtmminus2, testTime, 6, 197);
1840 
1841 		tm btm = {
1842 			9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
1843 		};
1844 		test_localtime(":Europe/Berlin", testTime, btm);
1845 		test_gmtime(":Europe/Berlin", testTime, gtm);
1846 		btm.tm_wday = -1;
1847 		btm.tm_yday = -1;
1848 		test_mktime(":Europe/Berlin", btm, testTime, 6, 197);
1849 
1850 		tm ctm = {
1851 			9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
1852 		};
1853 		test_localtime("CET", testTime, ctm);
1854 		test_gmtime("CET", testTime, gtm);
1855 		ctm.tm_wday = -1;
1856 		ctm.tm_yday = -1;
1857 		test_mktime("CET", ctm, testTime, 6, 197);
1858 
1859 		tm latm = {
1860 			9, 26, 11, 17, 6, 110, 6, 197, 1, -7 * 3600, (char*)"PDT"
1861 		};
1862 		test_localtime(":America/Los_Angeles", testTime, latm);
1863 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1864 		latm.tm_wday = -1;
1865 		latm.tm_yday = -1;
1866 		test_mktime(":America/Los_Angeles", latm, testTime, 6, 197);
1867 
1868 		tm ttm = {
1869 			9, 26, 3, 18, 6, 110, 0, 198, 0, 9 * 3600, (char*)"JST"
1870 		};
1871 		test_localtime(":Asia/Tokyo", testTime, ttm);
1872 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1873 		ttm.tm_wday = -1;
1874 		ttm.tm_yday = -1;
1875 		test_mktime(":Asia/Tokyo", ttm, testTime, 0, 198);
1876 	}
1877 
1878 	{
1879 		time_t testTime = 1268159169;	// Tue Mar 9 18:26:09 GMT 2010
1880 		tm gtm = {
1881 			9, 26, 18, 9, 2, 110, 2, 67, 0, 0, (char*)"GMT"
1882 		};
1883 		test_localtime("GMT", testTime, gtm);
1884 		test_gmtime("GMT", testTime, gtm);
1885 		gtm.tm_wday = -1;
1886 		gtm.tm_yday = -1;
1887 		test_mktime("GMT", gtm, testTime, 2, 67);
1888 
1889 		tm btm = {
1890 			9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET"
1891 		};
1892 		test_localtime(":Europe/Berlin", testTime, btm);
1893 		test_gmtime(":Europe/Berlin", testTime, gtm);
1894 		btm.tm_wday = -1;
1895 		btm.tm_yday = -1;
1896 		test_mktime(":Europe/Berlin", btm, testTime, 2, 67);
1897 
1898 		tm ctm = {
1899 			9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET"
1900 		};
1901 		test_localtime("CET", testTime, ctm);
1902 		test_gmtime("CET", testTime, gtm);
1903 		ctm.tm_wday = -1;
1904 		ctm.tm_yday = -1;
1905 		test_mktime("CET", ctm, testTime, 2, 67);
1906 
1907 		tm latm = {
1908 			9, 26, 10, 9, 2, 110, 2, 67, 0, -8 * 3600, (char*)"PST"
1909 		};
1910 		test_localtime(":America/Los_Angeles", testTime, latm);
1911 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1912 		latm.tm_wday = -1;
1913 		latm.tm_yday = -1;
1914 		test_mktime(":America/Los_Angeles", latm, testTime, 2, 67);
1915 
1916 		tm ttm = {
1917 			9, 26, 3, 10, 2, 110, 3, 68, 0, 9 * 3600, (char*)"JST"
1918 		};
1919 		test_localtime(":Asia/Tokyo", testTime, ttm);
1920 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1921 		ttm.tm_wday = -1;
1922 		ttm.tm_yday = -1;
1923 		test_mktime(":Asia/Tokyo", ttm, testTime, 3, 68);
1924 	}
1925 
1926 	{
1927 		time_t testTime = 0;	// Thu Jan 1 00:00:00 GMT 1970
1928 		tm gtm = {
1929 			0, 0, 0, 1, 0, 70, 4, 0, 0, 0, (char*)"GMT"
1930 		};
1931 		test_localtime("GMT", testTime, gtm);
1932 		test_gmtime("GMT", testTime, gtm);
1933 		gtm.tm_wday = -1;
1934 		gtm.tm_yday = -1;
1935 		test_mktime("GMT", gtm, testTime, 4, 0);
1936 
1937 		tm btm = {
1938 			0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET"
1939 		};
1940 		test_localtime(":Europe/Berlin", testTime, btm);
1941 		test_gmtime(":Europe/Berlin", testTime, gtm);
1942 		btm.tm_wday = -1;
1943 		btm.tm_yday = -1;
1944 		test_mktime(":Europe/Berlin", btm, testTime, 4, 0);
1945 
1946 		tm ctm = {
1947 			0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET"
1948 		};
1949 		test_localtime("CET", testTime, ctm);
1950 		test_gmtime("CET", testTime, gtm);
1951 		ctm.tm_wday = -1;
1952 		ctm.tm_yday = -1;
1953 		test_mktime("CET", ctm, testTime, 4, 0);
1954 
1955 		tm latm = {
1956 			0, 0, 16, 31, 11, 69, 3, 364, 0, -8 * 3600, (char*)"PST"
1957 		};
1958 		test_localtime(":America/Los_Angeles", testTime, latm);
1959 		test_gmtime(":America/Los_Angeles", testTime, gtm);
1960 		latm.tm_wday = -1;
1961 		latm.tm_yday = -1;
1962 		test_mktime(":America/Los_Angeles", latm, testTime, 3, 364);
1963 
1964 		tm ttm = {
1965 			0, 0, 9, 1, 0, 70, 4, 0, 0, 9 * 3600, (char*)"JST"
1966 		};
1967 		test_localtime(":Asia/Tokyo", testTime, ttm);
1968 		test_gmtime(":Asia/Tokyo", testTime, gtm);
1969 		ttm.tm_wday = -1;
1970 		ttm.tm_yday = -1;
1971 		test_mktime(":Asia/Tokyo", ttm, testTime, 4, 0);
1972 	}
1973 }
1974 
1975 
1976 // #pragma mark - printf -------------------------------------------------------
1977 
1978 
1979 struct sprintf_data {
1980 	const char* format;
1981 	double value;
1982 	const char* result;
1983 };
1984 
1985 
1986 void
1987 test_sprintf(const char* locale, const sprintf_data data[])
1988 {
1989 	setlocale(LC_ALL, locale);
1990 	printf("sprintf for '%s'\n", locale);
1991 
1992 	int problemCount = 0;
1993 	for(int i = 0; data[i].format != NULL; ++i) {
1994 		char buf[100];
1995 		if (strchr(data[i].format, 'd') != NULL)
1996 			sprintf(buf, data[i].format, (int)data[i].value);
1997 		else if (strchr(data[i].format, 'f') != NULL)
1998 			sprintf(buf, data[i].format, data[i].value);
1999 		if (strcmp(buf, data[i].result) != 0) {
2000 			printf("\tPROBLEM: sprintf(\"%s\") = \"%s\" (expected \"%s\")\n",
2001 				data[i].format, buf, data[i].result);
2002 			problemCount++;
2003 		}
2004 	}
2005 	if (problemCount)
2006 		printf("\t%d problem(s) found!\n", problemCount);
2007 	else
2008 		printf("\tall fine\n");
2009 }
2010 
2011 
2012 void
2013 test_sprintf()
2014 {
2015 	const sprintf_data sprintf_posix[] = {
2016 		{ "%d", 123, "123" },
2017 		{ "%d", -123, "-123" },
2018 		{ "%d", 123456, "123456" },
2019 		{ "%'d", 123456, "123456" },
2020 		{ "%f", 123, "123.000000" },
2021 		{ "%f", -123, "-123.000000" },
2022 		{ "%.2f", 123456.789, "123456.79" },
2023 		{ "%'.2f", 123456.789, "123456.79" },
2024 		{ NULL, 0.0, NULL }
2025 	};
2026 	test_sprintf("POSIX", sprintf_posix);
2027 
2028 	const sprintf_data sprintf_de[] = {
2029 		{ "%d", 123, "123" },
2030 		{ "%d", -123, "-123" },
2031 		{ "%d", 123456, "123456" },
2032 		{ "%'d", 123456, "123.456" },
2033 		{ "%f", 123, "123,000000" },
2034 		{ "%f", -123, "-123,000000" },
2035 		{ "%.2f", 123456.789, "123456,79" },
2036 		{ "%'.2f", 123456.789, "123.456,79" },
2037 		{ NULL, 0.0, NULL }
2038 	};
2039 	test_sprintf("de_DE.UTF-8", sprintf_de);
2040 
2041 	const sprintf_data sprintf_gu[] = {
2042 		{ "%d", 123, "123" },
2043 		{ "%d", -123, "-123" },
2044 		{ "%d", 123456, "123456" },
2045 		{ "%'d", 123456, "123,456" },
2046 		{ "%f", 123, "123.000000" },
2047 		{ "%f", -123, "-123.000000" },
2048 		{ "%.2f", 123456.789, "123456.79" },
2049 		{ "%'.2f", 123456.789, "1,23,456.79" },
2050 		{ NULL, 0.0, NULL }
2051 	};
2052 	test_sprintf("gu_IN", sprintf_gu);
2053 
2054 	const sprintf_data sprintf_nb[] = {
2055 		{ "%d", 123, "123" },
2056 		{ "%d", -123, "-123" },
2057 		{ "%d", 123456, "123456" },
2058 		{ "%'d", 123456, "123 456" },
2059 		{ "%f", 123, "123,000000" },
2060 		{ "%f", -123, "-123,000000" },
2061 		{ "%.2f", 123456.789, "123456,79" },
2062 		{ "%'.2f", 123456.789, "123 456,79" },
2063 		{ NULL, 0.0, NULL }
2064 	};
2065 	test_sprintf("nb_NO", sprintf_nb);
2066 }
2067 
2068 
2069 // #pragma mark - main ---------------------------------------------------------
2070 
2071 
2072 /*
2073  * Test several different aspects of the POSIX locale and the functions
2074  * influenced by it.
2075  */
2076 int
2077 main(void)
2078 {
2079 	test_setlocale();
2080 	test_localeconv();
2081 	test_strftime();
2082 	test_ctype();
2083 	test_wctype();
2084 	test_wctrans();
2085 	test_wcwidth();
2086 	test_langinfo();
2087 	test_collation();
2088 	test_timeconversions();
2089 	test_sprintf();
2090 
2091 	return 0;
2092 }
2093