2 * xsltlocale.c: locale handling
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
17 #include <libxml/xmlmemory.h>
18 #include <libxml/threads.h>
20 #include "xsltlocale.h"
21 #include "xsltutils.h"
25 #define XSLT_LOCALE_POSIX
36 #define XSLT_LOCALE_WINAPI
43 #define XSLT_LOCALE_NONE
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
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];
67 typedef struct xsltRFC1766Info_s xsltRFC1766Info
;
69 static int xsltLocaleListSize
= 0;
70 static xsltRFC1766Info
*xsltLocaleList
= NULL
;
74 xslt_locale_WINAPI(const xmlChar
*languageTag
) {
76 xsltRFC1766Info
*p
= xsltLocaleList
;
78 for (k
=0; k
<xsltLocaleListSize
; k
++, p
++)
79 if (xmlStrcmp(p
->tag
, languageTag
) == 0)
84 static void xsltEnumSupportedLocales(void);
90 * Cleanup function for the locale support on shutdown
93 xsltFreeLocales(void) {
94 #ifdef XSLT_LOCALE_WINAPI
95 xmlRMutexLock(xsltLocaleMutex
);
96 xmlFree(xsltLocaleList
);
97 xsltLocaleList
= NULL
;
98 xmlRMutexUnlock(xsltLocaleMutex
);
104 * @languageTag: RFC 3066 language tag
106 * Creates a new locale of an opaque system dependent type based on the
109 * Returns the locale or NULL on error or if no matching locale was found
112 xsltNewLocale(const xmlChar
*languageTag
, int lowerFirst ATTRIBUTE_UNUSED
) {
113 #ifdef XSLT_LOCALE_POSIX
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
;
121 /* Convert something like "pt-br" to "pt_BR.UTF-8" */
123 if (languageTag
== NULL
)
126 for (i
=0; i
<XSLTMAX_ISO639LANGLEN
&& ISALPHA(*p
); ++i
)
127 *q
++ = TOLOWER(*p
++);
139 for (i
=0; i
<XSLTMAX_ISO3166CNTRYLEN
&& ISALPHA(*p
); ++i
)
140 *q
++ = TOUPPER(*p
++);
145 memcpy(q
, ".UTF-8", 7);
146 locale
= newlocale(LC_COLLATE_MASK
, localeName
, NULL
);
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
);
162 /* Try to find most common country for language */
167 region
= (char *)xsltDefaultRegion((xmlChar
*)localeName
);
171 q
= localeName
+ llen
;
175 memcpy(q
, ".UTF-8", 7);
176 locale
= newlocale(LC_COLLATE_MASK
, localeName
, NULL
);
181 #ifdef XSLT_LOCALE_WINAPI
184 xmlChar localeName
[XSLTMAX_LANGTAGLEN
+1];
185 xmlChar
*q
= localeName
;
186 const xmlChar
*p
= languageTag
;
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
;
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
;
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
);
222 #ifdef XSLT_LOCALE_NONE
227 static const xmlChar
*
228 xsltDefaultRegion(const xmlChar
*localeName
) {
230 /* region should be xmlChar, but gcc warns on all string assignments */
231 const char *region
= NULL
;
234 /* This is based on the locales from glibc 2.3.3 */
236 switch (localeName
[0]) {
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";
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";
252 if (c
== 'a') region
= "ES";
253 else if (c
== 's') region
= "CZ";
254 else if (c
== 'y') region
= "GB";
257 if (c
== 'a') region
= "DK";
258 else if (c
== 'e') region
= "DE";
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";
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";
273 if (c
== 'a') region
= "IE";
274 else if (c
== 'l') region
= "ES";
275 else if (c
== 'v') region
= "GB";
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";
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";
290 if (c
== 'a') region
= "JP";
293 if (c
== 'l') region
= "GL";
294 else if (c
== 'o') region
= "KR";
295 else if (c
== 'w') region
= "GB";
298 if (c
== 't') region
= "LT";
299 else if (c
== 'v') region
= "LV";
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";
309 if (c
== 'b' || c
== 'n' || c
== 'o') region
= "NO";
310 else if (c
== 'e') region
= "NP";
311 else if (c
== 'l') region
= "NL";
314 if (c
== 'm') region
= "ET";
317 if (c
== 'a') region
= "IN";
318 else if (c
== 'l') region
= "PL";
319 else if (c
== 't') region
= "PT";
322 if (c
== 'o') region
= "RO";
323 else if (c
== 'u') region
= "RU";
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;
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";
345 if (c
== 'k') region
= "UA";
346 else if (c
== 'r') region
= "PK";
349 if (c
== 'i') region
= "VN";
352 if (c
== 'a') region
= "BE";
355 if (c
== 'h') region
= "ZA";
358 if (c
== 'h') region
= "CN";
359 else if (c
== 'u') region
= "ZA";
362 return((xmlChar
*)region
);
367 * @locale: the locale to free
369 * Frees a locale created with xsltNewLocale
372 xsltFreeLocale(void *locale
) {
373 #ifdef XSLT_LOCALE_POSIX
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
392 xsltStrxfrm(void *vlocale
, const xmlChar
*string
)
394 #ifdef XSLT_LOCALE_NONE
399 #ifdef XSLT_LOCALE_POSIX
402 xstrlen
= strxfrm_l(NULL
, (const char *)string
, 0, vlocale
) + 1;
403 xstr
= (xmlChar
*) xmlMalloc(xstrlen
);
405 xsltTransformError(NULL
, NULL
, NULL
,
406 "xsltStrxfrm : out of memory error\n");
410 r
= strxfrm_l((char *)xstr
, (const char *)string
, xstrlen
, vlocale
);
413 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : strxfrm failed\n");
419 #ifdef XSLT_LOCALE_WINAPI
420 int wstrlen
, xstrlen
, r
;
422 LCID
*lcid
= vlocale
;
424 wstrlen
= MultiByteToWideChar(CP_UTF8
, 0, (char *) string
, -1, NULL
, 0);
426 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : MultiByteToWideChar check failed\n");
429 wstr
= (wchar_t *) xmlMalloc(wstrlen
* sizeof(wchar_t));
431 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : out of memory\n");
434 r
= MultiByteToWideChar(CP_UTF8
, 0, (char *) string
, -1, wstr
, wstrlen
);
436 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : MultiByteToWideChar failed\n");
440 /* This returns the size in bytes. */
441 xstrlen
= LCMapStringW(*lcid
, LCMAP_SORTKEY
, wstr
, wstrlen
, NULL
, 0);
443 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : LCMapStringW failed\n");
447 xstr
= (xmlChar
*) xmlMalloc(xstrlen
);
449 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : out of memory\n");
453 r
= LCMapStringW(*lcid
, LCMAP_SORTKEY
, wstr
, wstrlen
, (wchar_t *) xstr
,
457 xsltTransformError(NULL
, NULL
, NULL
, "xsltStrxfrm : LCMapStringW failed\n");
461 #endif /* XSLT_LOCALE_WINAPI */
464 #endif /* XSLT_LOCALE_NONE */
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
) {
484 return(xmlStrcmp(str1
, str2
));
487 #ifdef XSLT_LOCALE_WINAPI
489 * xsltCountSupportedLocales:
492 * callback used to count locales
497 xsltCountSupportedLocales(LPSTR lcid
) {
499 ++xsltLocaleListSize
;
504 * xsltIterateSupportedLocales:
507 * callback used to track locales
509 * Returns TRUE if not at the end of the array
512 xsltIterateSupportedLocales(LPSTR lcid
) {
513 static int count
= 0;
514 xmlChar iso639lang
[XSLTMAX_ISO639LANGLEN
+1];
515 xmlChar iso3136ctry
[XSLTMAX_ISO3166CNTRYLEN
+1];
517 xsltRFC1766Info
*p
= xsltLocaleList
+ count
;
519 k
= sscanf(lcid
, "%lx", (unsigned long*)&p
->lcid
);
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
;
531 memcpy(q
, iso639lang
, k
);
534 memcpy(q
, iso3136ctry
, l
);
540 return((count
< xsltLocaleListSize
) ? TRUE
: FALSE
);
545 xsltEnumSupportedLocales(void) {
546 xmlRMutexLock(xsltLocaleMutex
);
547 if (xsltLocaleListSize
<= 0) {
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*/