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>
19 #include "xsltlocale.h"
20 #include "xsltutils.h"
22 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 2
23 #define newlocale __newlocale
24 #define freelocale __freelocale
25 #define strxfrm_l __strxfrm_l
26 #define LC_COLLATE_MASK (1 << LC_COLLATE)
29 #define ISALPHA(c) ((c & 0xc0) == 0x40 && (unsigned)((c & 0x1f) - 1) < 26)
30 #define TOUPPER(c) (c & ~0x20)
31 #define TOLOWER(c) (c | 0x20)
33 /*without terminating null character*/
34 #define XSLTMAX_ISO639LANGLEN 8
35 #define XSLTMAX_ISO3166CNTRYLEN 8
37 #define XSLTMAX_LANGTAGLEN (XSLTMAX_ISO639LANGLEN+1+XSLTMAX_ISO3166CNTRYLEN)
39 static const xmlChar* xsltDefaultRegion(const xmlChar *localeName);
41 #ifdef XSLT_LOCALE_WINAPI
42 xmlRMutexPtr xsltLocaleMutex = NULL;
44 struct xsltRFC1766Info_s {
45 /*note typedef unsigned char xmlChar !*/
46 xmlChar tag[XSLTMAX_LANGTAGLEN+1];
47 /*note typedef LCID xsltLocale !*/
50 typedef struct xsltRFC1766Info_s xsltRFC1766Info;
52 static int xsltLocaleListSize = 0;
53 static xsltRFC1766Info *xsltLocaleList = NULL;
57 xslt_locale_WINAPI(const xmlChar *languageTag) {
59 xsltRFC1766Info *p = xsltLocaleList;
61 for (k=0; k<xsltLocaleListSize; k++, p++)
62 if (xmlStrcmp(p->tag, languageTag) == 0) return p->lcid;
63 return((xsltLocale)0);
66 static void xsltEnumSupportedLocales(void);
71 * @languageTag: RFC 3066 language tag
73 * Creates a new locale of an opaque system dependent type based on the
76 * Returns the locale or NULL on error or if no matching locale was found
79 xsltNewLocale(const xmlChar *languageTag) {
80 #ifdef XSLT_LOCALE_XLOCALE
82 char localeName[XSLTMAX_LANGTAGLEN+6]; /* 6 chars for ".utf8\0" */
83 const xmlChar *p = languageTag;
84 const char *region = NULL;
88 /* Convert something like "pt-br" to "pt_BR.utf8" */
90 if (languageTag == NULL)
93 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
106 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
107 *q++ = TOUPPER(*p++);
112 memcpy(q, ".utf8", 6);
113 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
117 /* Continue without using country code */
119 q = localeName + llen + 1;
122 /* Try locale without territory, e.g. for Esperanto (eo) */
124 memcpy(q, ".utf8", 6);
125 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
129 /* Try to find most common country for language */
134 region = (char *)xsltDefaultRegion((xmlChar *)localeName);
138 q = localeName + llen + 1;
141 memcpy(q, ".utf8", 6);
142 locale = newlocale(LC_COLLATE_MASK, localeName, NULL);
147 #ifdef XSLT_LOCALE_WINAPI
149 xsltLocale locale = (xsltLocale)0;
150 xmlChar localeName[XSLTMAX_LANGTAGLEN+1];
151 xmlChar *q = localeName;
152 const xmlChar *p = languageTag;
154 const xmlChar *region = NULL;
156 if (languageTag == NULL) goto end;
158 xsltEnumSupportedLocales();
160 for (i=0; i<XSLTMAX_ISO639LANGLEN && ISALPHA(*p); ++i)
161 *q++ = TOLOWER(*p++);
162 if (i == 0) goto end;
166 if (*p) { /*if country tag is given*/
167 if (*p++ != '-') goto end;
169 for (i=0; i<XSLTMAX_ISO3166CNTRYLEN && ISALPHA(*p); ++i)
170 *q++ = TOUPPER(*p++);
171 if (i == 0 || *p) goto end;
174 locale = xslt_locale_WINAPI(localeName);
175 if (locale != (xsltLocale)0) goto end;
177 /* Try to find most common country for language */
178 region = xsltDefaultRegion(localeName);
179 if (region == NULL) goto end;
181 strcpy(localeName + llen + 1, region);
182 locale = xslt_locale_WINAPI(localeName);
188 #ifdef XSLT_LOCALE_NONE
193 static const xmlChar*
194 xsltDefaultRegion(const xmlChar *localeName) {
196 /* region should be xmlChar, but gcc warns on all string assignments */
197 const char *region = NULL;
200 /* This is based on the locales from glibc 2.3.3 */
202 switch (localeName[0]) {
204 if (c == 'a' || c == 'm') region = "ET";
205 else if (c == 'f') region = "ZA";
206 else if (c == 'n') region = "ES";
207 else if (c == 'r') region = "AE";
208 else if (c == 'z') region = "AZ";
211 if (c == 'e') region = "BY";
212 else if (c == 'g') region = "BG";
213 else if (c == 'n') region = "BD";
214 else if (c == 'r') region = "FR";
215 else if (c == 's') region = "BA";
218 if (c == 'a') region = "ES";
219 else if (c == 's') region = "CZ";
220 else if (c == 'y') region = "GB";
223 if (c == 'a') region = "DK";
224 else if (c == 'e') region = "DE";
227 if (c == 'l') region = "GR";
228 else if (c == 'n' || c == 'o') region = "US";
229 else if (c == 's' || c == 'u') region = "ES";
230 else if (c == 't') region = "EE";
233 if (c == 'a') region = "IR";
234 else if (c == 'i') region = "FI";
235 else if (c == 'o') region = "FO";
236 else if (c == 'r') region = "FR";
239 if (c == 'a') region = "IE";
240 else if (c == 'l') region = "ES";
241 else if (c == 'v') region = "GB";
244 if (c == 'e') region = "IL";
245 else if (c == 'i') region = "IN";
246 else if (c == 'r') region = "HT";
247 else if (c == 'u') region = "HU";
250 if (c == 'd') region = "ID";
251 else if (c == 's') region = "IS";
252 else if (c == 't') region = "IT";
253 else if (c == 'w') region = "IL";
256 if (c == 'a') region = "JP";
259 if (c == 'l') region = "GL";
260 else if (c == 'o') region = "KR";
261 else if (c == 'w') region = "GB";
264 if (c == 't') region = "LT";
265 else if (c == 'v') region = "LV";
268 if (c == 'k') region = "MK";
269 else if (c == 'l' || c == 'r') region = "IN";
270 else if (c == 'n') region = "MN";
271 else if (c == 's') region = "MY";
272 else if (c == 't') region = "MT";
275 if (c == 'b' || c == 'n' || c == 'o') region = "NO";
276 else if (c == 'e') region = "NP";
277 else if (c == 'l') region = "NL";
280 if (c == 'm') region = "ET";
283 if (c == 'a') region = "IN";
284 else if (c == 'l') region = "PL";
285 else if (c == 't') region = "PT";
288 if (c == 'o') region = "RO";
289 else if (c == 'u') region = "RU";
293 case 'e': region = "NO"; break;
294 case 'h': region = "YU"; break;
295 case 'k': region = "SK"; break;
296 case 'l': region = "SI"; break;
297 case 'o': region = "ET"; break;
298 case 'q': region = "AL"; break;
299 case 't': region = "ZA"; break;
300 case 'v': region = "SE"; break;
304 if (c == 'a' || c == 'e') region = "IN";
305 else if (c == 'h') region = "TH";
306 else if (c == 'i') region = "ER";
307 else if (c == 'r') region = "TR";
308 else if (c == 't') region = "RU";
311 if (c == 'k') region = "UA";
312 else if (c == 'r') region = "PK";
315 if (c == 'i') region = "VN";
318 if (c == 'a') region = "BE";
321 if (c == 'h') region = "ZA";
324 if (c == 'h') region = "CN";
325 else if (c == 'u') region = "ZA";
328 return((xmlChar *)region);
333 * @locale: the locale to free
335 * Frees a locale created with xsltNewLocale
338 xsltFreeLocale(xsltLocale locale) {
339 #ifdef XSLT_LOCALE_XLOCALE
346 * @locale: locale created with xsltNewLocale
347 * @string: UTF-8 string to transform
349 * Transforms a string according to locale. The transformed string must then be
350 * compared with xsltLocaleStrcmp and freed with xmlFree.
352 * Returns the transformed string or NULL on error
355 xsltStrxfrm(xsltLocale locale, const xmlChar *string)
357 #ifdef XSLT_LOCALE_NONE
361 xsltLocaleChar *xstr;
363 #ifdef XSLT_LOCALE_XLOCALE
364 xstrlen = strxfrm_l(NULL, (const char *)string, 0, locale) + 1;
365 xstr = (xsltLocaleChar *) xmlMalloc(xstrlen);
367 xsltTransformError(NULL, NULL, NULL,
368 "xsltStrxfrm : out of memory error\n");
372 r = strxfrm_l((char *)xstr, (const char *)string, xstrlen, locale);
375 #ifdef XSLT_LOCALE_WINAPI
376 xstrlen = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
378 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar check failed\n");
381 xstr = (xsltLocaleChar*) xmlMalloc(xstrlen * sizeof(xsltLocaleChar));
383 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : out of memory\n");
386 r = MultiByteToWideChar(CP_UTF8, 0, string, -1, xstr, xstrlen);
388 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : MultiByteToWideChar failed\n");
393 #endif /* XSLT_LOCALE_WINAPI */
396 xsltTransformError(NULL, NULL, NULL, "xsltStrxfrm : strxfrm failed\n");
402 #endif /* XSLT_LOCALE_NONE */
407 * @locale: a locale identifier
408 * @str1: a string transformed with xsltStrxfrm
409 * @str2: a string transformed with xsltStrxfrm
411 * Compares two strings transformed with xsltStrxfrm
413 * Returns a value < 0 if str1 sorts before str2,
414 * a value > 0 if str1 sorts after str2,
415 * 0 if str1 and str2 are equal wrt sorting
418 xsltLocaleStrcmp(xsltLocale locale, const xsltLocaleChar *str1, const xsltLocaleChar *str2) {
420 #ifdef XSLT_LOCALE_WINAPI
423 if (str1 == str2) return(0);
424 if (str1 == NULL) return(-1);
425 if (str2 == NULL) return(1);
426 ret = CompareStringW(locale, 0, str1, -1, str2, -1);
428 xsltTransformError(NULL, NULL, NULL, "xsltLocaleStrcmp : CompareStringW fail\n");
434 return(xmlStrcmp(str1, str2));
438 #ifdef XSLT_LOCALE_WINAPI
440 * xsltCountSupportedLocales:
443 * callback used to count locales
448 xsltCountSupportedLocales(LPSTR lcid) {
450 ++xsltLocaleListSize;
455 * xsltIterateSupportedLocales:
458 * callback used to track locales
460 * Returns TRUE if not at the end of the array
463 xsltIterateSupportedLocales(LPSTR lcid) {
464 static int count = 0;
465 xmlChar iso639lang [XSLTMAX_ISO639LANGLEN +1];
466 xmlChar iso3136ctry[XSLTMAX_ISO3166CNTRYLEN+1];
468 xsltRFC1766Info *p = xsltLocaleList + count;
470 k = sscanf(lcid, "%lx", (long*)&p->lcid);
472 /*don't count terminating null character*/
473 k = GetLocaleInfoA(p->lcid, LOCALE_SISO639LANGNAME , iso639lang , sizeof(iso639lang ));
474 if (--k < 1) goto end;
475 l = GetLocaleInfoA(p->lcid, LOCALE_SISO3166CTRYNAME, iso3136ctry, sizeof(iso3136ctry));
476 if (--l < 1) goto end;
480 memcpy(q, iso639lang, k);
483 memcpy(q, iso3136ctry, l);
489 return((count < xsltLocaleListSize) ? TRUE : FALSE);
494 xsltEnumSupportedLocales(void) {
495 xmlRMutexLock(xsltLocaleMutex);
496 if (xsltLocaleListSize <= 0) {
499 EnumSystemLocalesA(xsltCountSupportedLocales, LCID_SUPPORTED);
501 len = xsltLocaleListSize * sizeof(xsltRFC1766Info);
502 xsltLocaleList = xmlMalloc(len);
503 memset(xsltLocaleList, 0, len);
504 EnumSystemLocalesA(xsltIterateSupportedLocales, LCID_SUPPORTED);
506 xmlRMutexUnlock(xsltLocaleMutex);
509 #endif /*def XSLT_LOCALE_WINAPI*/