2b39b0988c595a6066429d6001a6cd07177fbd96
[reactos.git] / dll / 3rdparty / libxslt / xsltlocale.c
1 /*
2 * xsltlocale.c: locale handling
3 *
4 * Reference:
5 * RFC 3066: Tags for the Identification of Languages
6 * http://www.ietf.org/rfc/rfc3066.txt
7 * ISO 639-1, ISO 3166-1
8 *
9 * Author: Nick Wellnhofer
10 * winapi port: Roumen Petrov
11 */
12
13 #include "precomp.h"
14
15 #include "xsltlocale.h"
16
17 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 2
18 #define newlocale __newlocale
19 #define freelocale __freelocale
20 #define strxfrm_l __strxfrm_l
21 #define LC_COLLATE_MASK (1 << LC_COLLATE)
22 #endif
23
24 #define TOUPPER(c) (c & ~0x20)
25 #define TOLOWER(c) (c | 0x20)
26 #define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26)
27
28 /*without terminating null character*/
29 #define XSLTMAX_ISO639LANGLEN 8
30 #define XSLTMAX_ISO3166CNTRYLEN 8
31 /* <lang>-<cntry> */
32 #define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN)
33
34 static const xmlChar* xsltDefaultRegion(const xmlChar *localeName);
35
36 #ifdef XSLT_LOCALE_WINAPI
37 xmlRMutexPtr xsltLocaleMutex = NULL;
38
39 struct xsltRFC1766Info_s {
40 /*note typedef unsigned char xmlChar !*/
41 xmlChar tag[XSLTMAX_LANGTAGLEN+1];
42 /*note typedef LCID xsltLocale !*/
43 xsltLocale lcid;
44 };
45 typedef struct xsltRFC1766Info_s xsltRFC1766Info;
46
47 static int xsltLocaleListSize = 0;
48 static xsltRFC1766Info *xsltLocaleList = NULL;
49
50
51 static xsltLocale
52 xslt_locale_WINAPI(const xmlChar *languageTag) {
53 int k;
54 xsltRFC1766Info *p = xsltLocaleList;
55
56 for (k=0; k<xsltLocaleListSize; k++, p++)
57 if (xmlStrcmp(p->tag, languageTag) == 0) return p->lcid;
58 return((xsltLocale)0);
59 }
60
61 static void xsltEnumSupportedLocales(void);
62 #endif
63
64 /**
65 * xsltFreeLocales:
66 *
67 * Cleanup function for the locale support on shutdown
68 */
69 void
70 xsltFreeLocales(void) {
71 #ifdef XSLT_LOCALE_WINAPI
72 xmlRMutexLock(xsltLocaleMutex);
73 xmlFree(xsltLocaleList);
74 xsltLocaleList = NULL;
75 xmlRMutexUnlock(xsltLocaleMutex);
76 #endif
77 }
78
79 /**
80 * xsltNewLocale:
81 * @languageTag: RFC 3066 language tag
82 *
83 * Creates a new locale of an opaque system dependent type based on the
84 * language tag.
85 *
86 * Returns the locale or NULL on error or if no matching locale was found
87 */
88 xsltLocale
89 xsltNewLocale(const xmlChar *languageTag) {
90 #ifdef XSLT_LOCALE_XLOCALE
91 xsltLocale locale;
92 char localeName[XSLTMAX_LANGTAGLEN+6]; /* 6 chars for ".utf8\0" */
93 const xmlChar *p = languageTag;
94 const char *region = NULL;
95 char *q = localeName;
96 int i, llen;
97
98 /* Convert something like "pt-br" to "pt_BR.utf8" */
99
100 if (languageTag == NULL)
101 return(NULL);
102
103 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
104 *q++ = TOLOWER(*p++);
105
106 if (i == 0)
107 return(NULL);
108
109 llen = i;
110
111 if (*p) {
112 if (*p++ != '-')
113 return(NULL);
114 *q++ = '_';
115
116 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
117 *q++ = TOUPPER(*p++);
118
119 if (i == 0 || *p)
120 return(NULL);
121
122 memcpy(q, ".utf8", 6);
123 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
124 if (locale != NULL)
125 return(locale);
126
127 /* Continue without using country code */
128
129 q = localeName + llen;
130 }
131
132 /* Try locale without territory, e.g. for Esperanto (eo) */
133
134 memcpy(q, ".utf8", 6);
135 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
136 if (locale != NULL)
137 return(locale);
138
139 /* Try to find most common country for language */
140
141 if (llen != 2)
142 return(NULL);
143
144 region = (char *)xsltDefaultRegion((xmlChar *)localeName);
145 if (region == NULL)
146 return(NULL);
147
148 q = localeName + llen;
149 *q++ = '_';
150 *q++ = region[0];
151 *q++ = region[1];
152 memcpy(q, ".utf8", 6);
153 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
154
155 return(locale);
156 #endif
157
158 #ifdef XSLT_LOCALE_WINAPI
159 {
160 xsltLocale locale = (xsltLocale)0;
161 xmlChar localeName[XSLTMAX_LANGTAGLEN+1];
162 xmlChar *q = localeName;
163 const xmlChar *p = languageTag;
164 int i, llen;
165 const xmlChar *region = NULL;
166
167 if (languageTag == NULL) goto end;
168
169 xsltEnumSupportedLocales();
170
171 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
172 *q++ = TOLOWER(*p++);
173 if (i == 0) goto end;
174
175 llen = i;
176 *q++ = '-';
177 if (*p) { /*if country tag is given*/
178 if (*p++ != '-') goto end;
179
180 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
181 *q++ = TOUPPER(*p++);
182 if (i == 0 || *p) goto end;
183
184 *q = '\0';
185 locale = xslt_locale_WINAPI(localeName);
186 if (locale != (xsltLocale)0) goto end;
187 }
188 /* Try to find most common country for language */
189 region = xsltDefaultRegion(localeName);
190 if (region == NULL) goto end;
191
192 strcpy(localeName + llen + 1, region);
193 locale = xslt_locale_WINAPI(localeName);
194 end:
195 return(locale);
196 }
197 #endif
198
199 #ifdef XSLT_LOCALE_NONE
200 return(NULL);
201 #endif
202 }
203
204 static const xmlChar*
205 xsltDefaultRegion(const xmlChar *localeName) {
206 xmlChar c;
207 /* region should be xmlChar, but gcc warns on all string assignments */
208 const char *region = NULL;
209
210 c = localeName[1];
211 /* This is based on the locales from glibc 2.3.3 */
212
213 switch (localeName[0]) {
214 case 'a':
215 if (c == 'a' || c == 'm') region = "ET";
216 else if (c == 'f') region = "ZA";
217 else if (c == 'n') region = "ES";
218 else if (c == 'r') region = "AE";
219 else if (c == 'z') region = "AZ";
220 break;
221 case 'b':
222 if (c == 'e') region = "BY";
223 else if (c == 'g') region = "BG";
224 else if (c == 'n') region = "BD";
225 else if (c == 'r') region = "FR";
226 else if (c == 's') region = "BA";
227 break;
228 case 'c':
229 if (c == 'a') region = "ES";
230 else if (c == 's') region = "CZ";
231 else if (c == 'y') region = "GB";
232 break;
233 case 'd':
234 if (c == 'a') region = "DK";
235 else if (c == 'e') region = "DE";
236 break;
237 case 'e':
238 if (c == 'l') region = "GR";
239 else if (c == 'n' || c == 'o') region = "US";
240 else if (c == 's' || c == 'u') region = "ES";
241 else if (c == 't') region = "EE";
242 break;
243 case 'f':
244 if (c == 'a') region = "IR";
245 else if (c == 'i') region = "FI";
246 else if (c == 'o') region = "FO";
247 else if (c == 'r') region = "FR";
248 break;
249 case 'g':
250 if (c == 'a') region = "IE";
251 else if (c == 'l') region = "ES";
252 else if (c == 'v') region = "GB";
253 break;
254 case 'h':
255 if (c == 'e') region = "IL";
256 else if (c == 'i') region = "IN";
257 else if (c == 'r') region = "HT";
258 else if (c == 'u') region = "HU";
259 break;
260 case 'i':
261 if (c == 'd') region = "ID";
262 else if (c == 's') region = "IS";
263 else if (c == 't') region = "IT";
264 else if (c == 'w') region = "IL";
265 break;
266 case 'j':
267 if (c == 'a') region = "JP";
268 break;
269 case 'k':
270 if (c == 'l') region = "GL";
271 else if (c == 'o') region = "KR";
272 else if (c == 'w') region = "GB";
273 break;
274 case 'l':
275 if (c == 't') region = "LT";
276 else if (c == 'v') region = "LV";
277 break;
278 case 'm':
279 if (c == 'k') region = "MK";
280 else if (c == 'l' || c == 'r') region = "IN";
281 else if (c == 'n') region = "MN";
282 else if (c == 's') region = "MY";
283 else if (c == 't') region = "MT";
284 break;
285 case 'n':
286 if (c == 'b' || c == 'n' || c == 'o') region = "NO";
287 else if (c == 'e') region = "NP";
288 else if (c == 'l') region = "NL";
289 break;
290 case 'o':
291 if (c == 'm') region = "ET";
292 break;
293 case 'p':
294 if (c == 'a') region = "IN";
295 else if (c == 'l') region = "PL";
296 else if (c == 't') region = "PT";
297 break;
298 case 'r':
299 if (c == 'o') region = "RO";
300 else if (c == 'u') region = "RU";
301 break;
302 case 's':
303 switch (c) {
304 case 'e': region = "NO"; break;
305 case 'h': region = "YU"; break;
306 case 'k': region = "SK"; break;
307 case 'l': region = "SI"; break;
308 case 'o': region = "ET"; break;
309 case 'q': region = "AL"; break;
310 case 't': region = "ZA"; break;
311 case 'v': region = "SE"; break;
312 }
313 break;
314 case 't':
315 if (c == 'a' || c == 'e') region = "IN";
316 else if (c == 'h') region = "TH";
317 else if (c == 'i') region = "ER";
318 else if (c == 'r') region = "TR";
319 else if (c == 't') region = "RU";
320 break;
321 case 'u':
322 if (c == 'k') region = "UA";
323 else if (c == 'r') region = "PK";
324 break;
325 case 'v':
326 if (c == 'i') region = "VN";
327 break;
328 case 'w':
329 if (c == 'a') region = "BE";
330 break;
331 case 'x':
332 if (c == 'h') region = "ZA";
333 break;
334 case 'z':
335 if (c == 'h') region = "CN";
336 else if (c == 'u') region = "ZA";
337 break;
338 }
339 return((xmlChar *)region);
340 }
341
342 /**
343 * xsltFreeLocale:
344 * @locale: the locale to free
345 *
346 * Frees a locale created with xsltNewLocale
347 */
348 void
349 xsltFreeLocale(xsltLocale locale) {
350 #ifdef XSLT_LOCALE_XLOCALE
351 freelocale(locale);
352 #endif
353 }
354
355 /**
356 * xsltStrxfrm:
357 * @locale: locale created with xsltNewLocale
358 * @string: UTF-8 string to transform
359 *
360 * Transforms a string according to locale. The transformed string must then be
361 * compared with xsltLocaleStrcmp and freed with xmlFree.
362 *
363 * Returns the transformed string or NULL on error
364 */
365 xsltLocaleChar *
366 xsltStrxfrm(xsltLocale locale, const xmlChar *string)
367 {
368 #ifdef XSLT_LOCALE_NONE
369 return(NULL);
370 #else
371 size_t xstrlen, r;
372 xsltLocaleChar *xstr;
373
374 #ifdef XSLT_LOCALE_XLOCALE
375 xstrlen = strxfrm_l(NULL, (const char *)string, 0, locale) + 1;
376 xstr = (xsltLocaleChar *) xmlMalloc(xstrlen);
377 if (xstr == NULL) {
378 xsltTransformError(NULL, NULL, NULL,
379 "xsltStrxfrm : out of memory error\n");
380 return(NULL);
381 }
382
383 r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, locale);
384 #endif
385
386 #ifdef XSLT_LOCALE_WINAPI
387 xstrlen = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
388 if (xstrlen == 0) {
389 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
390 return(NULL);
391 }
392 xstr = (xsltLocaleChar*) xmlMalloc(xstrlen * sizeof(xsltLocaleChar));
393 if (xstr == NULL) {
394 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
395 return(NULL);
396 }
397 r = MultiByteToWideChar(CP_UTF8, 0, string, -1, xstr, xstrlen);
398 if (r == 0) {
399 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
400 xmlFree(xstr);
401 return(NULL);
402 }
403 return(xstr);
404 #endif /* XSLT_LOCALE_WINAPI */
405
406 if (r >= xstrlen) {
407 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
408 xmlFree(xstr);
409 return(NULL);
410 }
411
412 return(xstr);
413 #endif /* XSLT_LOCALE_NONE */
414 }
415
416 /**
417 * xsltLocaleStrcmp:
418 * @locale: a locale identifier
419 * @str1: a string transformed with xsltStrxfrm
420 * @str2: a string transformed with xsltStrxfrm
421 *
422 * Compares two strings transformed with xsltStrxfrm
423 *
424 * Returns a value < 0 if str1 sorts before str2,
425 * a value > 0 if str1 sorts after str2,
426 * 0 if str1 and str2 are equal wrt sorting
427 */
428 int
429 xsltLocaleStrcmp(xsltLocale locale, const xsltLocaleChar *str1, const xsltLocaleChar *str2) {
430 (void)locale;
431 #ifdef XSLT_LOCALE_WINAPI
432 {
433 int ret;
434 if (str1 == str2) return(0);
435 if (str1 == NULL) return(-1);
436 if (str2 == NULL) return(1);
437 ret = CompareStringW(locale, 0, str1, -1, str2, -1);
438 if (ret == 0) {
439 xsltTransformError(NULL, NULL, NULL, "xsltLocaleStrcmp : CompareStringW fail\n");
440 return(0);
441 }
442 return(ret - 2);
443 }
444 #else
445 return(xmlStrcmp(str1, str2));
446 #endif
447 }
448
449 #ifdef XSLT_LOCALE_WINAPI
450 /**
451 * xsltCountSupportedLocales:
452 * @lcid: not used
453 *
454 * callback used to count locales
455 *
456 * Returns TRUE
457 */
458 BOOL CALLBACK
459 xsltCountSupportedLocales(LPSTR lcid) {
460 (void) lcid;
461 ++xsltLocaleListSize;
462 return(TRUE);
463 }
464
465 /**
466 * xsltIterateSupportedLocales:
467 * @lcid: not used
468 *
469 * callback used to track locales
470 *
471 * Returns TRUE if not at the end of the array
472 */
473 BOOL CALLBACK
474 xsltIterateSupportedLocales(LPSTR lcid) {
475 static int count = 0;
476 xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1];
477 xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1];
478 int k, l;
479 xsltRFC1766Info *p = xsltLocaleList + count;
480
481 k = sscanf(lcid, "%lx", (long*)&p->lcid);
482 if (k < 1) goto end;
483 /*don't count terminating null character*/
484 k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME , iso639lang , sizeof(iso639lang ));
485 if (--k < 1) goto end;
486 l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME, iso3136ctry, sizeof(iso3136ctry));
487 if (--l < 1) goto end;
488
489 { /*fill results*/
490 xmlChar *q = p->tag;
491 memcpy(q, iso639lang, k);
492 q += k;
493 *q++ = '-';
494 memcpy(q, iso3136ctry, l);
495 q += l;
496 *q = '\0';
497 }
498 ++count;
499 end:
500 return((count < xsltLocaleListSize) ? TRUE : FALSE);
501 }
502
503
504 static void
505 xsltEnumSupportedLocales(void) {
506 xmlRMutexLock(xsltLocaleMutex);
507 if (xsltLocaleListSize <= 0) {
508 size_t len;
509
510 EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED);
511
512 len = xsltLocaleListSize * sizeof(xsltRFC1766Info);
513 xsltLocaleList = xmlMalloc(len);
514 memset(xsltLocaleList, 0, len);
515 EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED);
516 }
517 xmlRMutexUnlock(xsltLocaleMutex);
518 }
519
520 #endif /*def XSLT_LOCALE_WINAPI*/