ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / libs / xslt / libxslt / xsltlocale.c
blobaa38ccf795049a6a99c7b9393d0ab6fe65be62ae
1 /*
2 * xsltlocale.c: locale handling
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
9 * Author: Nick Wellnhofer
10 * winapi port: Roumen Petrov
13 #define IN_LIBXSLT
14 #include "libxslt.h"
16 #include <string.h>
17 #include <libxml/xmlmemory.h>
18 #include <libxml/threads.h>
20 #include "xsltlocale.h"
21 #include "xsltutils.h"
23 #ifdef HAVE_STRXFRM_L
25 #define XSLT_LOCALE_POSIX
27 #ifdef HAVE_LOCALE_H
28 #include <locale.h>
29 #endif
30 #ifdef HAVE_XLOCALE_H
31 #include <xlocale.h>
32 #endif
34 #elif defined(_WIN32)
36 #define XSLT_LOCALE_WINAPI
38 #include <windows.h>
39 #include <winnls.h>
41 #else
43 #define XSLT_LOCALE_NONE
45 #endif
47 #define TOUPPER(c) (c & ~0x20)
48 #define TOLOWER(c) (c | 0x20)
49 #define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26)
51 /*without terminating null character*/
52 #define XSLTMAX_ISO639LANGLEN 8
53 #define XSLTMAX_ISO3166CNTRYLEN 8
54 /* <lang>-<cntry> */
55 #define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN)
57 static const xmlChar* xsltDefaultRegion(const xmlChar *localeName);
59 #ifdef XSLT_LOCALE_WINAPI
60 xmlRMutexPtr xsltLocaleMutex = NULL;
62 struct xsltRFC1766Info_s {
63 /*note typedef unsigned char xmlChar !*/
64 xmlChar tag[XSLTMAX_LANGTAGLEN+1];
65 LCID lcid;
67 typedef struct xsltRFC1766Info_s xsltRFC1766Info;
69 static int xsltLocaleListSize = 0;
70 static xsltRFC1766Info *xsltLocaleList = NULL;
73 static void *
74 xslt_locale_WINAPI(const xmlChar *languageTag) {
75 int k;
76 xsltRFC1766Info *p = xsltLocaleList;
78 for (k=0; k<xsltLocaleListSize; k++, p++)
79 if (xmlStrcmp(p->tag, languageTag) == 0)
80 return(&p->lcid);
81 return(NULL);
84 static void xsltEnumSupportedLocales(void);
85 #endif
87 /**
88 * xsltFreeLocales:
90 * Cleanup function for the locale support on shutdown
92 void
93 xsltFreeLocales(void) {
94 #ifdef XSLT_LOCALE_WINAPI
95 xmlRMutexLock(xsltLocaleMutex);
96 xmlFree(xsltLocaleList);
97 xsltLocaleList = NULL;
98 xmlRMutexUnlock(xsltLocaleMutex);
99 #endif
103 * xsltNewLocale:
104 * @languageTag: RFC 3066 language tag
106 * Creates a new locale of an opaque system dependent type based on the
107 * language tag.
109 * Returns the locale or NULL on error or if no matching locale was found
111 void *
112 xsltNewLocale(const xmlChar *languageTag, int lowerFirst ATTRIBUTE_UNUSED) {
113 #ifdef XSLT_LOCALE_POSIX
114 locale_t locale;
115 char localeName[XSLTMAX_LANGTAGLEN+7]; /* 7 chars for ".UTF-8\0" */
116 const xmlChar *p = languageTag;
117 const char *region = NULL;
118 char *q = localeName;
119 int i, llen;
121 /* Convert something like "pt-br" to "pt_BR.UTF-8" */
123 if (languageTag == NULL)
124 return(NULL);
126 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
127 *q++ = TOLOWER(*p++);
129 if (i == 0)
130 return(NULL);
132 llen = i;
134 if (*p) {
135 if (*p++ != '-')
136 return(NULL);
137 *q++ = '_';
139 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
140 *q++ = TOUPPER(*p++);
142 if (i == 0 || *p)
143 return(NULL);
145 memcpy(q, ".UTF-8", 7);
146 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
147 if (locale != NULL)
148 return(locale);
150 /* Continue without using country code */
152 q = localeName + llen;
155 /* Try locale without territory, e.g. for Esperanto (eo) */
157 memcpy(q, ".UTF-8", 7);
158 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
159 if (locale != NULL)
160 return(locale);
162 /* Try to find most common country for language */
164 if (llen != 2)
165 return(NULL);
167 region = (char *)xsltDefaultRegion((xmlChar *)localeName);
168 if (region == NULL)
169 return(NULL);
171 q = localeName + llen;
172 *q++ = '_';
173 *q++ = region[0];
174 *q++ = region[1];
175 memcpy(q, ".UTF-8", 7);
176 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
178 return(locale);
179 #endif
181 #ifdef XSLT_LOCALE_WINAPI
183 void *locale = NULL;
184 xmlChar localeName[XSLTMAX_LANGTAGLEN+1];
185 xmlChar *q = localeName;
186 const xmlChar *p = languageTag;
187 int i, llen;
188 const xmlChar *region = NULL;
190 if (languageTag == NULL) goto end;
192 xsltEnumSupportedLocales();
194 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
195 *q++ = TOLOWER(*p++);
196 if (i == 0) goto end;
198 llen = i;
199 *q++ = '-';
200 if (*p) { /*if country tag is given*/
201 if (*p++ != '-') goto end;
203 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
204 *q++ = TOUPPER(*p++);
205 if (i == 0 || *p) goto end;
207 *q = '\0';
208 locale = xslt_locale_WINAPI(localeName);
209 if (locale != (xsltLocale)0) goto end;
211 /* Try to find most common country for language */
212 region = xsltDefaultRegion(localeName);
213 if (region == NULL) goto end;
215 strcpy((char *) localeName + llen + 1, (char *) region);
216 locale = xslt_locale_WINAPI(localeName);
217 end:
218 return(locale);
220 #endif
222 #ifdef XSLT_LOCALE_NONE
223 return(NULL);
224 #endif
227 static const xmlChar*
228 xsltDefaultRegion(const xmlChar *localeName) {
229 xmlChar c;
230 /* region should be xmlChar, but gcc warns on all string assignments */
231 const char *region = NULL;
233 c = localeName[1];
234 /* This is based on the locales from glibc 2.3.3 */
236 switch (localeName[0]) {
237 case 'a':
238 if (c == 'a' || c == 'm') region = "ET";
239 else if (c == 'f') region = "ZA";
240 else if (c == 'n') region = "ES";
241 else if (c == 'r') region = "AE";
242 else if (c == 'z') region = "AZ";
243 break;
244 case 'b':
245 if (c == 'e') region = "BY";
246 else if (c == 'g') region = "BG";
247 else if (c == 'n') region = "BD";
248 else if (c == 'r') region = "FR";
249 else if (c == 's') region = "BA";
250 break;
251 case 'c':
252 if (c == 'a') region = "ES";
253 else if (c == 's') region = "CZ";
254 else if (c == 'y') region = "GB";
255 break;
256 case 'd':
257 if (c == 'a') region = "DK";
258 else if (c == 'e') region = "DE";
259 break;
260 case 'e':
261 if (c == 'l') region = "GR";
262 else if (c == 'n' || c == 'o') region = "US";
263 else if (c == 's' || c == 'u') region = "ES";
264 else if (c == 't') region = "EE";
265 break;
266 case 'f':
267 if (c == 'a') region = "IR";
268 else if (c == 'i') region = "FI";
269 else if (c == 'o') region = "FO";
270 else if (c == 'r') region = "FR";
271 break;
272 case 'g':
273 if (c == 'a') region = "IE";
274 else if (c == 'l') region = "ES";
275 else if (c == 'v') region = "GB";
276 break;
277 case 'h':
278 if (c == 'e') region = "IL";
279 else if (c == 'i') region = "IN";
280 else if (c == 'r') region = "HT";
281 else if (c == 'u') region = "HU";
282 break;
283 case 'i':
284 if (c == 'd') region = "ID";
285 else if (c == 's') region = "IS";
286 else if (c == 't') region = "IT";
287 else if (c == 'w') region = "IL";
288 break;
289 case 'j':
290 if (c == 'a') region = "JP";
291 break;
292 case 'k':
293 if (c == 'l') region = "GL";
294 else if (c == 'o') region = "KR";
295 else if (c == 'w') region = "GB";
296 break;
297 case 'l':
298 if (c == 't') region = "LT";
299 else if (c == 'v') region = "LV";
300 break;
301 case 'm':
302 if (c == 'k') region = "MK";
303 else if (c == 'l' || c == 'r') region = "IN";
304 else if (c == 'n') region = "MN";
305 else if (c == 's') region = "MY";
306 else if (c == 't') region = "MT";
307 break;
308 case 'n':
309 if (c == 'b' || c == 'n' || c == 'o') region = "NO";
310 else if (c == 'e') region = "NP";
311 else if (c == 'l') region = "NL";
312 break;
313 case 'o':
314 if (c == 'm') region = "ET";
315 break;
316 case 'p':
317 if (c == 'a') region = "IN";
318 else if (c == 'l') region = "PL";
319 else if (c == 't') region = "PT";
320 break;
321 case 'r':
322 if (c == 'o') region = "RO";
323 else if (c == 'u') region = "RU";
324 break;
325 case 's':
326 switch (c) {
327 case 'e': region = "NO"; break;
328 case 'h': region = "YU"; break;
329 case 'k': region = "SK"; break;
330 case 'l': region = "SI"; break;
331 case 'o': region = "ET"; break;
332 case 'q': region = "AL"; break;
333 case 't': region = "ZA"; break;
334 case 'v': region = "SE"; break;
336 break;
337 case 't':
338 if (c == 'a' || c == 'e') region = "IN";
339 else if (c == 'h') region = "TH";
340 else if (c == 'i') region = "ER";
341 else if (c == 'r') region = "TR";
342 else if (c == 't') region = "RU";
343 break;
344 case 'u':
345 if (c == 'k') region = "UA";
346 else if (c == 'r') region = "PK";
347 break;
348 case 'v':
349 if (c == 'i') region = "VN";
350 break;
351 case 'w':
352 if (c == 'a') region = "BE";
353 break;
354 case 'x':
355 if (c == 'h') region = "ZA";
356 break;
357 case 'z':
358 if (c == 'h') region = "CN";
359 else if (c == 'u') region = "ZA";
360 break;
362 return((xmlChar *)region);
366 * xsltFreeLocale:
367 * @locale: the locale to free
369 * Frees a locale created with xsltNewLocale
371 void
372 xsltFreeLocale(void *locale) {
373 #ifdef XSLT_LOCALE_POSIX
374 if (locale != NULL)
375 freelocale(locale);
376 #else
377 (void) locale;
378 #endif
382 * xsltStrxfrm:
383 * @locale: locale created with xsltNewLocale
384 * @string: UTF-8 string to transform
386 * Transforms a string according to locale. The transformed string must be
387 * freed with xmlFree.
389 * Returns the transformed string or NULL on error
391 xmlChar *
392 xsltStrxfrm(void *vlocale, const xmlChar *string)
394 #ifdef XSLT_LOCALE_NONE
395 return(NULL);
396 #else
397 xmlChar *xstr;
399 #ifdef XSLT_LOCALE_POSIX
400 size_t xstrlen, r;
402 xstrlen = strxfrm_l(NULL, (const char *)string, 0, vlocale) + 1;
403 xstr = (xmlChar *) xmlMalloc(xstrlen);
404 if (xstr == NULL) {
405 xsltTransformError(NULL, NULL, NULL,
406 "xsltStrxfrm : out of memory error\n");
407 return(NULL);
410 r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, vlocale);
412 if (r >= xstrlen) {
413 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
414 xmlFree(xstr);
415 return(NULL);
417 #endif
419 #ifdef XSLT_LOCALE_WINAPI
420 int wstrlen, xstrlen, r;
421 wchar_t *wstr;
422 LCID *lcid = vlocale;
424 wstrlen = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, NULL, 0);
425 if (wstrlen == 0) {
426 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
427 return(NULL);
429 wstr = (wchar_t *) xmlMalloc(wstrlen * sizeof(wchar_t));
430 if (wstr == NULL) {
431 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
432 return(NULL);
434 r = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, wstr, wstrlen);
435 if (r == 0) {
436 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
437 xmlFree(wstr);
438 return(NULL);
440 /* This returns the size in bytes. */
441 xstrlen = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, NULL, 0);
442 if (xstrlen == 0) {
443 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
444 xmlFree(wstr);
445 return(NULL);
447 xstr = (xmlChar*) xmlMalloc(xstrlen);
448 if (xstr == NULL) {
449 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
450 xmlFree(wstr);
451 return(NULL);
453 r = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, (wchar_t *) xstr,
454 xstrlen);
455 xmlFree(wstr);
456 if (r == 0) {
457 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
458 xmlFree(xstr);
459 return(NULL);
461 #endif /* XSLT_LOCALE_WINAPI */
463 return(xstr);
464 #endif /* XSLT_LOCALE_NONE */
468 * xsltLocaleStrcmp:
469 * @locale: unused
470 * @str1: a string transformed with xsltStrxfrm
471 * @str2: a string transformed with xsltStrxfrm
473 * DEPRECATED: Same as xmlStrcmp.
475 * Compares two strings transformed with xsltStrxfrm.
477 * Returns a value < 0 if str1 sorts before str2,
478 * a value > 0 if str1 sorts after str2,
479 * 0 if str1 and str2 are equal wrt sorting
482 xsltLocaleStrcmp(void *locale, const xmlChar *str1, const xmlChar *str2) {
483 (void)locale;
484 return(xmlStrcmp(str1, str2));
487 #ifdef XSLT_LOCALE_WINAPI
489 * xsltCountSupportedLocales:
490 * @lcid: not used
492 * callback used to count locales
494 * Returns TRUE
496 static BOOL CALLBACK
497 xsltCountSupportedLocales(LPSTR lcid) {
498 (void) lcid;
499 ++xsltLocaleListSize;
500 return(TRUE);
504 * xsltIterateSupportedLocales:
505 * @lcid: not used
507 * callback used to track locales
509 * Returns TRUE if not at the end of the array
511 static BOOL CALLBACK
512 xsltIterateSupportedLocales(LPSTR lcid) {
513 static int count = 0;
514 xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1];
515 xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1];
516 int k, l;
517 xsltRFC1766Info *p = xsltLocaleList + count;
519 k = sscanf(lcid, "%lx", (unsigned long*)&p->lcid);
520 if (k < 1) goto end;
521 /*don't count terminating null character*/
522 k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME,
523 (char *) iso639lang, sizeof(iso639lang));
524 if (--k < 1) goto end;
525 l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME,
526 (char *) iso3136ctry, sizeof(iso3136ctry));
527 if (--l < 1) goto end;
529 { /*fill results*/
530 xmlChar *q = p->tag;
531 memcpy(q, iso639lang, k);
532 q += k;
533 *q++ = '-';
534 memcpy(q, iso3136ctry, l);
535 q += l;
536 *q = '\0';
538 ++count;
539 end:
540 return((count < xsltLocaleListSize) ? TRUE : FALSE);
544 static void
545 xsltEnumSupportedLocales(void) {
546 xmlRMutexLock(xsltLocaleMutex);
547 if (xsltLocaleListSize <= 0) {
548 size_t len;
550 EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED);
552 len = xsltLocaleListSize * sizeof(xsltRFC1766Info);
553 xsltLocaleList = xmlMalloc(len);
554 memset(xsltLocaleList, 0, len);
555 EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED);
557 xmlRMutexUnlock(xsltLocaleMutex);
560 #endif /*def XSLT_LOCALE_WINAPI*/