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