xslt: Import upstream release 1.1.38.
[wine.git] / libs / xslt / libxslt / xsltlocale.c
blob5a9291887ec6abba805fbb0dee23a99c29f61577
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>
19 #include "xsltlocale.h"
20 #include "xsltutils.h"
22 #ifdef HAVE_STRXFRM_L
24 #define XSLT_LOCALE_POSIX
26 #ifdef HAVE_LOCALE_H
27 #include <locale.h>
28 #endif
29 #ifdef HAVE_XLOCALE_H
30 #include <xlocale.h>
31 #endif
33 #elif defined(_WIN32)
35 #define XSLT_LOCALE_WINAPI
37 #include <windows.h>
38 #include <winnls.h>
40 #else
42 #define XSLT_LOCALE_NONE
44 #endif
46 #define TOUPPER(c) (c & ~0x20)
47 #define TOLOWER(c) (c | 0x20)
48 #define ISALPHA(c) ((unsigned)(TOUPPER(c) - 'A') < 26)
50 /*without terminating null character*/
51 #define XSLTMAX_ISO639LANGLEN 8
52 #define XSLTMAX_ISO3166CNTRYLEN 8
53 /* <lang>-<cntry> */
54 #define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN)
56 static const xmlChar* xsltDefaultRegion(const xmlChar *localeName);
58 #ifdef XSLT_LOCALE_WINAPI
59 xmlRMutexPtr xsltLocaleMutex = NULL;
61 struct xsltRFC1766Info_s {
62 /*note typedef unsigned char xmlChar !*/
63 xmlChar tag[XSLTMAX_LANGTAGLEN+1];
64 LCID lcid;
66 typedef struct xsltRFC1766Info_s xsltRFC1766Info;
68 static int xsltLocaleListSize = 0;
69 static xsltRFC1766Info *xsltLocaleList = NULL;
72 static void *
73 xslt_locale_WINAPI(const xmlChar *languageTag) {
74 int k;
75 xsltRFC1766Info *p = xsltLocaleList;
77 for (k=0; k<xsltLocaleListSize; k++, p++)
78 if (xmlStrcmp(p->tag, languageTag) == 0)
79 return(&p->lcid);
80 return(NULL);
83 static void xsltEnumSupportedLocales(void);
84 #endif
86 /**
87 * xsltFreeLocales:
89 * Cleanup function for the locale support on shutdown
91 void
92 xsltFreeLocales(void) {
93 #ifdef XSLT_LOCALE_WINAPI
94 xmlRMutexLock(xsltLocaleMutex);
95 xmlFree(xsltLocaleList);
96 xsltLocaleList = NULL;
97 xmlRMutexUnlock(xsltLocaleMutex);
98 #endif
102 * xsltNewLocale:
103 * @languageTag: RFC 3066 language tag
105 * Creates a new locale of an opaque system dependent type based on the
106 * language tag.
108 * Returns the locale or NULL on error or if no matching locale was found
110 void *
111 xsltNewLocale(const xmlChar *languageTag, int lowerFirst ATTRIBUTE_UNUSED) {
112 #ifdef XSLT_LOCALE_POSIX
113 locale_t locale;
114 char localeName[XSLTMAX_LANGTAGLEN+7]; /* 7 chars for ".UTF-8\0" */
115 const xmlChar *p = languageTag;
116 const char *region = NULL;
117 char *q = localeName;
118 int i, llen;
120 /* Convert something like "pt-br" to "pt_BR.UTF-8" */
122 if (languageTag == NULL)
123 return(NULL);
125 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
126 *q++ = TOLOWER(*p++);
128 if (i == 0)
129 return(NULL);
131 llen = i;
133 if (*p) {
134 if (*p++ != '-')
135 return(NULL);
136 *q++ = '_';
138 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
139 *q++ = TOUPPER(*p++);
141 if (i == 0 || *p)
142 return(NULL);
144 memcpy(q, ".UTF-8", 7);
145 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
146 if (locale != NULL)
147 return(locale);
149 /* Continue without using country code */
151 q = localeName + llen;
154 /* Try locale without territory, e.g. for Esperanto (eo) */
156 memcpy(q, ".UTF-8", 7);
157 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
158 if (locale != NULL)
159 return(locale);
161 /* Try to find most common country for language */
163 if (llen != 2)
164 return(NULL);
166 region = (char *)xsltDefaultRegion((xmlChar *)localeName);
167 if (region == NULL)
168 return(NULL);
170 q = localeName + llen;
171 *q++ = '_';
172 *q++ = region[0];
173 *q++ = region[1];
174 memcpy(q, ".UTF-8", 7);
175 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
177 return(locale);
178 #endif
180 #ifdef XSLT_LOCALE_WINAPI
182 void *locale = NULL;
183 xmlChar localeName[XSLTMAX_LANGTAGLEN+1];
184 xmlChar *q = localeName;
185 const xmlChar *p = languageTag;
186 int i, llen;
187 const xmlChar *region = NULL;
189 if (languageTag == NULL) goto end;
191 xsltEnumSupportedLocales();
193 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
194 *q++ = TOLOWER(*p++);
195 if (i == 0) goto end;
197 llen = i;
198 *q++ = '-';
199 if (*p) { /*if country tag is given*/
200 if (*p++ != '-') goto end;
202 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
203 *q++ = TOUPPER(*p++);
204 if (i == 0 || *p) goto end;
206 *q = '\0';
207 locale = xslt_locale_WINAPI(localeName);
208 if (locale != (xsltLocale)0) goto end;
210 /* Try to find most common country for language */
211 region = xsltDefaultRegion(localeName);
212 if (region == NULL) goto end;
214 strcpy((char *) localeName + llen + 1, (char *) region);
215 locale = xslt_locale_WINAPI(localeName);
216 end:
217 return(locale);
219 #endif
221 #ifdef XSLT_LOCALE_NONE
222 return(NULL);
223 #endif
226 static const xmlChar*
227 xsltDefaultRegion(const xmlChar *localeName) {
228 xmlChar c;
229 /* region should be xmlChar, but gcc warns on all string assignments */
230 const char *region = NULL;
232 c = localeName[1];
233 /* This is based on the locales from glibc 2.3.3 */
235 switch (localeName[0]) {
236 case 'a':
237 if (c == 'a' || c == 'm') region = "ET";
238 else if (c == 'f') region = "ZA";
239 else if (c == 'n') region = "ES";
240 else if (c == 'r') region = "AE";
241 else if (c == 'z') region = "AZ";
242 break;
243 case 'b':
244 if (c == 'e') region = "BY";
245 else if (c == 'g') region = "BG";
246 else if (c == 'n') region = "BD";
247 else if (c == 'r') region = "FR";
248 else if (c == 's') region = "BA";
249 break;
250 case 'c':
251 if (c == 'a') region = "ES";
252 else if (c == 's') region = "CZ";
253 else if (c == 'y') region = "GB";
254 break;
255 case 'd':
256 if (c == 'a') region = "DK";
257 else if (c == 'e') region = "DE";
258 break;
259 case 'e':
260 if (c == 'l') region = "GR";
261 else if (c == 'n' || c == 'o') region = "US";
262 else if (c == 's' || c == 'u') region = "ES";
263 else if (c == 't') region = "EE";
264 break;
265 case 'f':
266 if (c == 'a') region = "IR";
267 else if (c == 'i') region = "FI";
268 else if (c == 'o') region = "FO";
269 else if (c == 'r') region = "FR";
270 break;
271 case 'g':
272 if (c == 'a') region = "IE";
273 else if (c == 'l') region = "ES";
274 else if (c == 'v') region = "GB";
275 break;
276 case 'h':
277 if (c == 'e') region = "IL";
278 else if (c == 'i') region = "IN";
279 else if (c == 'r') region = "HT";
280 else if (c == 'u') region = "HU";
281 break;
282 case 'i':
283 if (c == 'd') region = "ID";
284 else if (c == 's') region = "IS";
285 else if (c == 't') region = "IT";
286 else if (c == 'w') region = "IL";
287 break;
288 case 'j':
289 if (c == 'a') region = "JP";
290 break;
291 case 'k':
292 if (c == 'l') region = "GL";
293 else if (c == 'o') region = "KR";
294 else if (c == 'w') region = "GB";
295 break;
296 case 'l':
297 if (c == 't') region = "LT";
298 else if (c == 'v') region = "LV";
299 break;
300 case 'm':
301 if (c == 'k') region = "MK";
302 else if (c == 'l' || c == 'r') region = "IN";
303 else if (c == 'n') region = "MN";
304 else if (c == 's') region = "MY";
305 else if (c == 't') region = "MT";
306 break;
307 case 'n':
308 if (c == 'b' || c == 'n' || c == 'o') region = "NO";
309 else if (c == 'e') region = "NP";
310 else if (c == 'l') region = "NL";
311 break;
312 case 'o':
313 if (c == 'm') region = "ET";
314 break;
315 case 'p':
316 if (c == 'a') region = "IN";
317 else if (c == 'l') region = "PL";
318 else if (c == 't') region = "PT";
319 break;
320 case 'r':
321 if (c == 'o') region = "RO";
322 else if (c == 'u') region = "RU";
323 break;
324 case 's':
325 switch (c) {
326 case 'e': region = "NO"; break;
327 case 'h': region = "YU"; break;
328 case 'k': region = "SK"; break;
329 case 'l': region = "SI"; break;
330 case 'o': region = "ET"; break;
331 case 'q': region = "AL"; break;
332 case 't': region = "ZA"; break;
333 case 'v': region = "SE"; break;
335 break;
336 case 't':
337 if (c == 'a' || c == 'e') region = "IN";
338 else if (c == 'h') region = "TH";
339 else if (c == 'i') region = "ER";
340 else if (c == 'r') region = "TR";
341 else if (c == 't') region = "RU";
342 break;
343 case 'u':
344 if (c == 'k') region = "UA";
345 else if (c == 'r') region = "PK";
346 break;
347 case 'v':
348 if (c == 'i') region = "VN";
349 break;
350 case 'w':
351 if (c == 'a') region = "BE";
352 break;
353 case 'x':
354 if (c == 'h') region = "ZA";
355 break;
356 case 'z':
357 if (c == 'h') region = "CN";
358 else if (c == 'u') region = "ZA";
359 break;
361 return((xmlChar *)region);
365 * xsltFreeLocale:
366 * @locale: the locale to free
368 * Frees a locale created with xsltNewLocale
370 void
371 xsltFreeLocale(void *locale) {
372 #ifdef XSLT_LOCALE_POSIX
373 if (locale != NULL)
374 freelocale(locale);
375 #else
376 (void) locale;
377 #endif
381 * xsltStrxfrm:
382 * @locale: locale created with xsltNewLocale
383 * @string: UTF-8 string to transform
385 * Transforms a string according to locale. The transformed string must be
386 * freed with xmlFree.
388 * Returns the transformed string or NULL on error
390 xmlChar *
391 xsltStrxfrm(void *vlocale, const xmlChar *string)
393 #ifdef XSLT_LOCALE_NONE
394 return(NULL);
395 #else
396 xmlChar *xstr;
398 #ifdef XSLT_LOCALE_POSIX
399 size_t xstrlen, r;
401 xstrlen = strxfrm_l(NULL, (const char *)string, 0, vlocale) + 1;
402 xstr = (xmlChar *) xmlMalloc(xstrlen);
403 if (xstr == NULL) {
404 xsltTransformError(NULL, NULL, NULL,
405 "xsltStrxfrm : out of memory error\n");
406 return(NULL);
409 r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, vlocale);
411 if (r >= xstrlen) {
412 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
413 xmlFree(xstr);
414 return(NULL);
416 #endif
418 #ifdef XSLT_LOCALE_WINAPI
419 int wstrlen, xstrlen, r;
420 wchar_t *wstr;
421 LCID *lcid = vlocale;
423 wstrlen = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, NULL, 0);
424 if (wstrlen == 0) {
425 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
426 return(NULL);
428 wstr = (wchar_t *) xmlMalloc(wstrlen * sizeof(wchar_t));
429 if (wstr == NULL) {
430 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
431 return(NULL);
433 r = MultiByteToWideChar(CP_UTF8, 0, (char *) string, -1, wstr, wstrlen);
434 if (r == 0) {
435 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
436 xmlFree(wstr);
437 return(NULL);
439 /* This returns the size in bytes. */
440 xstrlen = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, NULL, 0);
441 if (xstrlen == 0) {
442 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
443 xmlFree(wstr);
444 return(NULL);
446 xstr = (xmlChar*) xmlMalloc(xstrlen);
447 if (xstr == NULL) {
448 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
449 xmlFree(wstr);
450 return(NULL);
452 r = LCMapStringW(*lcid, LCMAP_SORTKEY, wstr, wstrlen, (wchar_t *) xstr,
453 xstrlen);
454 xmlFree(wstr);
455 if (r == 0) {
456 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : LCMapStringW failed\n");
457 xmlFree(xstr);
458 return(NULL);
460 #endif /* XSLT_LOCALE_WINAPI */
462 return(xstr);
463 #endif /* XSLT_LOCALE_NONE */
467 * xsltLocaleStrcmp:
468 * @locale: unused
469 * @str1: a string transformed with xsltStrxfrm
470 * @str2: a string transformed with xsltStrxfrm
472 * DEPRECATED: Same as xmlStrcmp.
474 * Compares two strings transformed with xsltStrxfrm.
476 * Returns a value < 0 if str1 sorts before str2,
477 * a value > 0 if str1 sorts after str2,
478 * 0 if str1 and str2 are equal wrt sorting
481 xsltLocaleStrcmp(void *locale, const xmlChar *str1, const xmlChar *str2) {
482 (void)locale;
483 return(xmlStrcmp(str1, str2));
486 #ifdef XSLT_LOCALE_WINAPI
488 * xsltCountSupportedLocales:
489 * @lcid: not used
491 * callback used to count locales
493 * Returns TRUE
495 static BOOL CALLBACK
496 xsltCountSupportedLocales(LPSTR lcid) {
497 (void) lcid;
498 ++xsltLocaleListSize;
499 return(TRUE);
503 * xsltIterateSupportedLocales:
504 * @lcid: not used
506 * callback used to track locales
508 * Returns TRUE if not at the end of the array
510 static BOOL CALLBACK
511 xsltIterateSupportedLocales(LPSTR lcid) {
512 static int count = 0;
513 xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1];
514 xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1];
515 int k, l;
516 xsltRFC1766Info *p = xsltLocaleList + count;
518 k = sscanf(lcid, "%lx", (unsigned long*)&p->lcid);
519 if (k < 1) goto end;
520 /*don't count terminating null character*/
521 k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME,
522 (char *) iso639lang, sizeof(iso639lang));
523 if (--k < 1) goto end;
524 l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME,
525 (char *) iso3136ctry, sizeof(iso3136ctry));
526 if (--l < 1) goto end;
528 { /*fill results*/
529 xmlChar *q = p->tag;
530 memcpy(q, iso639lang, k);
531 q += k;
532 *q++ = '-';
533 memcpy(q, iso3136ctry, l);
534 q += l;
535 *q = '\0';
537 ++count;
538 end:
539 return((count < xsltLocaleListSize) ? TRUE : FALSE);
543 static void
544 xsltEnumSupportedLocales(void) {
545 xmlRMutexLock(xsltLocaleMutex);
546 if (xsltLocaleListSize <= 0) {
547 size_t len;
549 EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED);
551 len = xsltLocaleListSize * sizeof(xsltRFC1766Info);
552 xsltLocaleList = xmlMalloc(len);
553 memset(xsltLocaleList, 0, len);
554 EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED);
556 xmlRMutexUnlock(xsltLocaleMutex);
559 #endif /*def XSLT_LOCALE_WINAPI*/