+#if (WINVER >= 0x0600)
+/***********************************************************************
+ * charset_cmp (internal)
+ */
+static int charset_cmp( const void *name, const void *entry )
+{
+ const struct charset_entry *charset = entry;
+ return strcasecmp( name, charset->charset_name );
+}
+
+/***********************************************************************
+ * find_charset
+ */
+static UINT find_charset( const WCHAR *name )
+{
+ const struct charset_entry *entry;
+ char charset_name[16];
+ size_t i, j;
+
+ /* remove punctuation characters from charset name */
+ for (i = j = 0; name[i] && j < sizeof(charset_name)-1; i++)
+ if (isalnum((unsigned char)name[i])) charset_name[j++] = name[i];
+ charset_name[j] = 0;
+
+ entry = bsearch( charset_name, charset_names, ARRAY_SIZE( charset_names ),
+ sizeof(charset_names[0]), charset_cmp );
+ if (entry) return entry->codepage;
+ return 0;
+}
+
+static LANGID get_default_sublang( LANGID lang )
+{
+ switch (lang)
+ {
+ case MAKELANGID( LANG_SPANISH, SUBLANG_NEUTRAL ):
+ return MAKELANGID( LANG_SPANISH, SUBLANG_SPANISH_MODERN );
+ case MAKELANGID( LANG_CHINESE, SUBLANG_NEUTRAL ):
+ return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
+ case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE ):
+ return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
+ case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL ):
+ case MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_MACAU ):
+ return MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_HONGKONG );
+ }
+ if (SUBLANGID( lang ) == SUBLANG_NEUTRAL) lang = MAKELANGID( PRIMARYLANGID(lang), SUBLANG_DEFAULT );
+ return lang;
+}
+
+/***********************************************************************
+ * find_locale_id_callback
+ */
+static BOOL CALLBACK find_locale_id_callback( HMODULE hModule, LPCWSTR type,
+ LPCWSTR name, LANGID lang, LPARAM lParam )
+{
+ struct locale_name *data = (struct locale_name *)lParam;
+ WCHAR buffer[128];
+ int matches = 0;
+ LCID lcid = MAKELCID( lang, SORT_DEFAULT ); /* FIXME: handle sort order */
+
+ if (PRIMARYLANGID(lang) == LANG_NEUTRAL) return TRUE; /* continue search */
+
+ /* first check exact name */
+ if (data->win_name[0] &&
+ GetLocaleInfoW( lcid, LOCALE_SNAME | LOCALE_NOUSEROVERRIDE, buffer, ARRAY_SIZE( buffer )))
+ {
+ if (!strcmpiW( data->win_name, buffer ))
+ {
+ matches = 4; /* everything matches */
+ goto done;
+ }
+ }
+
+ if (!GetLocaleInfoW( lcid, LOCALE_SISO639LANGNAME | LOCALE_NOUSEROVERRIDE,
+ buffer, ARRAY_SIZE( buffer )))
+ return TRUE;
+ if (strcmpiW( buffer, data->lang )) return TRUE;
+ matches++; /* language name matched */
+
+ if (data->script)
+ {
+ if (GetLocaleInfoW( lcid, LOCALE_SSCRIPTS | LOCALE_NOUSEROVERRIDE,
+ buffer, ARRAY_SIZE( buffer )))
+ {
+ const WCHAR *p = buffer;
+ unsigned int len = strlenW( data->script );
+ while (*p)
+ {
+ if (!strncmpiW( p, data->script, len ) && (!p[len] || p[len] == ';')) break;
+ if (!(p = strchrW( p, ';'))) goto done;
+ p++;
+ }
+ if (!*p) goto done;
+ matches++; /* script matched */
+ }
+ }
+
+ if (data->country)
+ {
+ if (GetLocaleInfoW( lcid, LOCALE_SISO3166CTRYNAME|LOCALE_NOUSEROVERRIDE,
+ buffer, ARRAY_SIZE( buffer )))
+ {
+ if (strcmpiW( buffer, data->country )) goto done;
+ matches++; /* country name matched */
+ }
+ }
+ else /* match default language */
+ {
+ LANGID def_lang = data->script ? lang : MAKELANGID( PRIMARYLANGID(lang), LANG_NEUTRAL );
+ if (lang == get_default_sublang( def_lang )) matches++;
+ }
+
+ if (data->codepage)
+ {
+ UINT unix_cp;
+ if (GetLocaleInfoW( lcid, LOCALE_IDEFAULTUNIXCODEPAGE | LOCALE_RETURN_NUMBER,
+ (LPWSTR)&unix_cp, sizeof(unix_cp)/sizeof(WCHAR) ))
+ {
+ if (unix_cp == data->codepage) matches++;
+ }
+ }
+
+ /* FIXME: check sort order */
+
+done:
+ if (matches > data->matches)
+ {
+ data->lcid = lcid;
+ data->matches = matches;
+ }
+ return (data->matches < 4); /* no need to continue for perfect match */
+}
+
+
+/***********************************************************************
+ * parse_locale_name
+ *
+ * Parse a locale name into a struct locale_name, handling both Windows and Unix formats.
+ * Unix format is: lang[_country][.charset][@modifier]
+ * Windows format is: lang[-script][-country][_modifier]
+ */
+static void parse_locale_name( const WCHAR *str, struct locale_name *name )
+{
+ static const WCHAR sepW[] = {'-','_','.','@',0};
+ static const WCHAR winsepW[] = {'-','_',0};
+ static const WCHAR posixW[] = {'P','O','S','I','X',0};
+ static const WCHAR cW[] = {'C',0};
+ static const WCHAR latinW[] = {'l','a','t','i','n',0};
+ static const WCHAR latnW[] = {'-','L','a','t','n',0};
+ WCHAR *p;
+
+ TRACE("%s\n", debugstr_w(str));
+
+ name->country = name->charset = name->script = name->modifier = NULL;
+ name->lcid = MAKELCID( MAKELANGID(LANG_ENGLISH,SUBLANG_DEFAULT), SORT_DEFAULT );
+ name->matches = 0;
+ name->codepage = 0;
+ name->win_name[0] = 0;
+ lstrcpynW( name->lang, str, ARRAY_SIZE( name->lang ));
+
+ if (!*name->lang)
+ {
+ name->lcid = LOCALE_INVARIANT;
+ name->matches = 4;
+ return;
+ }
+
+ if (!(p = strpbrkW( name->lang, sepW )))
+ {
+ if (!strcmpW( name->lang, posixW ) || !strcmpW( name->lang, cW ))
+ {
+ name->matches = 4; /* perfect match for default English lcid */
+ return;
+ }
+ strcpyW( name->win_name, name->lang );
+ }
+ else if (*p == '-') /* Windows format */
+ {
+ strcpyW( name->win_name, name->lang );
+ *p++ = 0;
+ name->country = p;
+ if ((p = strpbrkW( p, winsepW )) && *p == '-')
+ {
+ *p++ = 0;
+ name->script = name->country;
+ name->country = p;
+ p = strpbrkW( p, winsepW );
+ }
+ if (p)
+ {
+ *p++ = 0;
+ name->modifier = p;
+ }
+ /* second value can be script or country, check length to resolve the ambiguity */
+ if (!name->script && strlenW( name->country ) == 4)
+ {
+ name->script = name->country;
+ name->country = NULL;
+ }
+ }
+ else /* Unix format */
+ {
+ if (*p == '_')
+ {
+ *p++ = 0;
+ name->country = p;
+ p = strpbrkW( p, sepW + 2 );
+ }
+ if (p && *p == '.')
+ {
+ *p++ = 0;
+ name->charset = p;
+ p = strchrW( p, '@' );
+ }
+ if (p)
+ {
+ *p++ = 0;
+ name->modifier = p;
+ }
+
+ if (name->charset)
+ name->codepage = find_charset( name->charset );
+
+ /* rebuild a Windows name if possible */
+
+ if (name->charset) goto done; /* can't specify charset in Windows format */
+ if (name->modifier && strcmpW( name->modifier, latinW ))
+ goto done; /* only Latn script supported for now */
+ strcpyW( name->win_name, name->lang );
+ if (name->modifier) strcatW( name->win_name, latnW );
+ if (name->country)
+ {
+ p = name->win_name + strlenW(name->win_name);
+ *p++ = '-';
+ strcpyW( p, name->country );
+ }
+ }
+done:
+ EnumResourceLanguagesW( kernel32_handle, (LPCWSTR)RT_STRING, (LPCWSTR)LOCALE_ILANGUAGE,
+ find_locale_id_callback, (LPARAM)name );
+}
+#endif
+