source: trunk/src/msvcrt/locale.c@ 10366

Last change on this file since 10366 was 10005, checked in by sandervl, 22 years ago

PF: MSVCRT update

File size: 14.9 KB
Line 
1/*
2 * msvcrt.dll locale functions
3 *
4 * Copyright 2000 Jon Griffiths
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#ifndef __WIN32OS2__
21#include "config.h"
22#else
23#include <emxheader.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <ctype.h>
28#endif
29
30#include "wine/port.h"
31
32#include "winbase.h"
33#include "winuser.h"
34
35#include "msvcrt.h"
36#include "msvcrt/locale.h"
37#include "mtdll.h"
38
39#include "wine/debug.h"
40
41WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
42
43/* FIXME: Need to hold locale for each LC_* type and aggregate
44 * string to produce lc_all.
45 */
46#define MAX_ELEM_LEN 64 /* Max length of country/language/CP string */
47#define MAX_LOCALE_LENGTH 256
48char MSVCRT_current_lc_all[MAX_LOCALE_LENGTH];
49LCID MSVCRT_current_lc_all_lcid;
50int MSVCRT_current_lc_all_cp;
51
52/* MT */
53#define LOCK_LOCALE _mlock(_SETLOCALE_LOCK);
54#define UNLOCK_LOCALE _munlock(_SETLOCALE_LOCK);
55
56/* ctype data modified when the locale changes */
57extern WORD MSVCRT__ctype [257];
58extern WORD MSVCRT_current_ctype[257];
59extern WORD* MSVCRT__pctype;
60
61/* mbctype data modified when the locale changes */
62extern int MSVCRT___mb_cur_max;
63extern unsigned char MSVCRT_mbctype[257];
64
65#define MSVCRT_LEADBYTE 0x8000
66
67/* Friendly country strings & iso codes for synonym support.
68 * Based on MS documentation for setlocale().
69 */
70static const char* _country_synonyms[] =
71{
72 "Hong Kong","HK",
73 "Hong-Kong","HK",
74 "New Zealand","NZ",
75 "New-Zealand","NZ",
76 "PR China","CN",
77 "PR-China","CN",
78 "United Kingdom","GB",
79 "United-Kingdom","GB",
80 "Britain","GB",
81 "England","GB",
82 "Great Britain","GB",
83 "United States","US",
84 "United-States","US",
85 "America","US"
86};
87
88/* INTERNAL: Map a synonym to an ISO code */
89static void remap_synonym(char *name)
90{
91 size_t i;
92 for (i = 0; i < sizeof(_country_synonyms)/sizeof(char*); i += 2 )
93 {
94 if (!_stricmp(_country_synonyms[i],name))
95 {
96 TRACE(":Mapping synonym %s to %s\n",name,_country_synonyms[i+1]);
97 name[0] = _country_synonyms[i+1][0];
98 name[1] = _country_synonyms[i+1][1];
99 name[2] = '\0';
100 return;
101 }
102 }
103}
104
105/* Note: Flags are weighted in order of matching importance */
106#define FOUND_LANGUAGE 0x4
107#define FOUND_COUNTRY 0x2
108#define FOUND_CODEPAGE 0x1
109
110typedef struct {
111 char search_language[MAX_ELEM_LEN];
112 char search_country[MAX_ELEM_LEN];
113 char search_codepage[MAX_ELEM_LEN];
114 char found_language[MAX_ELEM_LEN];
115 char found_country[MAX_ELEM_LEN];
116 char found_codepage[MAX_ELEM_LEN];
117 unsigned int match_flags;
118 LANGID found_lang_id;
119} locale_search_t;
120
121#define CONTINUE_LOOKING TRUE
122#define STOP_LOOKING FALSE
123
124/* INTERNAL: Get and compare locale info with a given string */
125static int compare_info(LCID lcid, DWORD flags, char* buff, const char* cmp)
126{
127 buff[0] = 0;
128 GetLocaleInfoA(lcid, flags|LOCALE_NOUSEROVERRIDE,buff, MAX_ELEM_LEN);
129 if (!buff[0] || !cmp[0])
130 return 0;
131 /* Partial matches are allowed, e.g. "Germ" matches "Germany" */
132 return !_strnicmp(cmp, buff, strlen(cmp));
133}
134
135static BOOL CALLBACK
136find_best_locale_proc(HMODULE hModule WINE_UNUSED, LPCSTR type WINE_UNUSED,
137 LPCSTR name WINE_UNUSED, WORD LangID, LONG lParam)
138{
139 locale_search_t *res = (locale_search_t *)lParam;
140 const LCID lcid = MAKELCID(LangID, SORT_DEFAULT);
141 char buff[MAX_ELEM_LEN];
142 unsigned int flags = 0;
143
144 if(PRIMARYLANGID(LangID) == LANG_NEUTRAL)
145 return CONTINUE_LOOKING;
146
147 /* Check Language */
148 if (compare_info(lcid,LOCALE_SISO639LANGNAME,buff,res->search_language) ||
149 compare_info(lcid,LOCALE_SABBREVLANGNAME,buff,res->search_language) ||
150 compare_info(lcid,LOCALE_SENGLANGUAGE,buff,res->search_language))
151 {
152 TRACE(":Found language: %s->%s\n", res->search_language, buff);
153 flags |= FOUND_LANGUAGE;
154 memcpy(res->found_language,res->search_language,MAX_ELEM_LEN);
155 }
156 else if (res->match_flags & FOUND_LANGUAGE)
157 {
158 return CONTINUE_LOOKING;
159 }
160
161 /* Check Country */
162 if (compare_info(lcid,LOCALE_SISO3166CTRYNAME,buff,res->search_country) ||
163 compare_info(lcid,LOCALE_SABBREVCTRYNAME,buff,res->search_country) ||
164 compare_info(lcid,LOCALE_SENGCOUNTRY,buff,res->search_country))
165 {
166 TRACE("Found country:%s->%s\n", res->search_country, buff);
167 flags |= FOUND_COUNTRY;
168 memcpy(res->found_country,res->search_country,MAX_ELEM_LEN);
169 }
170 else if (res->match_flags & FOUND_COUNTRY)
171 {
172 return CONTINUE_LOOKING;
173 }
174
175 /* Check codepage */
176 if (compare_info(lcid,LOCALE_IDEFAULTCODEPAGE,buff,res->search_codepage) ||
177 (compare_info(lcid,LOCALE_IDEFAULTANSICODEPAGE,buff,res->search_codepage)))
178 {
179 TRACE("Found codepage:%s->%s\n", res->search_codepage, buff);
180 flags |= FOUND_CODEPAGE;
181 memcpy(res->found_codepage,res->search_codepage,MAX_ELEM_LEN);
182 }
183 else if (res->match_flags & FOUND_CODEPAGE)
184 {
185 return CONTINUE_LOOKING;
186 }
187
188 if (flags > res->match_flags)
189 {
190 /* Found a better match than previously */
191 res->match_flags = flags;
192 res->found_lang_id = LangID;
193 }
194 if (flags & (FOUND_LANGUAGE & FOUND_COUNTRY & FOUND_CODEPAGE))
195 {
196 TRACE(":found exact locale match\n");
197 return STOP_LOOKING;
198 }
199 return CONTINUE_LOOKING;
200}
201
202extern int atoi(const char *);
203
204/* Internal: Find the LCID for a locale specification */
205static LCID MSVCRT_locale_to_LCID(locale_search_t* locale)
206{
207 LCID lcid;
208 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA,
209 (LPCSTR)LOCALE_ILANGUAGE,find_best_locale_proc,
210 (LONG)locale);
211
212 if (!locale->match_flags)
213 return 0;
214
215 /* If we were given something that didn't match, fail */
216 if (locale->search_country[0] && !(locale->match_flags & FOUND_COUNTRY))
217 return 0;
218
219 lcid = MAKELCID(locale->found_lang_id, SORT_DEFAULT);
220
221 /* Populate partial locale, translating LCID to locale string elements */
222 if (!locale->found_codepage[0])
223 {
224 /* Even if a codepage is not enumerated for a locale
225 * it can be set if valid */
226 if (locale->search_codepage[0])
227 {
228 if (IsValidCodePage(atoi(locale->search_codepage)))
229 memcpy(locale->found_codepage,locale->search_codepage,MAX_ELEM_LEN);
230 else
231 {
232 /* Special codepage values: OEM & ANSI */
233 if (_stricmp(locale->search_codepage,"OCP"))
234 {
235 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
236 locale->found_codepage, MAX_ELEM_LEN);
237 }
238 if (_stricmp(locale->search_codepage,"ACP"))
239 {
240 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
241 locale->found_codepage, MAX_ELEM_LEN);
242 }
243 else
244 return 0;
245
246 if (!atoi(locale->found_codepage))
247 return 0;
248 }
249 }
250 else
251 {
252 /* Prefer ANSI codepages if present */
253 GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE,
254 locale->found_codepage, MAX_ELEM_LEN);
255 if (!locale->found_codepage[0] || !atoi(locale->found_codepage))
256 GetLocaleInfoA(lcid, LOCALE_IDEFAULTCODEPAGE,
257 locale->found_codepage, MAX_ELEM_LEN);
258 }
259 }
260 GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE|LOCALE_NOUSEROVERRIDE,
261 locale->found_language, MAX_ELEM_LEN);
262 GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY|LOCALE_NOUSEROVERRIDE,
263 locale->found_country, MAX_ELEM_LEN);
264 return lcid;
265}
266
267
268/* INTERNAL: Set ctype behaviour for a codepage */
269static void msvcrt_set_ctype(unsigned int codepage, LCID lcid)
270{
271 CPINFO cp;
272
273 memset(&cp, 0, sizeof(CPINFO));
274
275 if (GetCPInfo(codepage, &cp))
276 {
277 int i;
278 char str[3];
279 unsigned char *traverse = (unsigned char *)cp.LeadByte;
280
281 memset(MSVCRT_current_ctype, 0, sizeof(MSVCRT__ctype));
282 MSVCRT_current_lc_all_cp = codepage;
283
284 /* Switch ctype macros to MBCS if needed */
285 MSVCRT___mb_cur_max = cp.MaxCharSize;
286
287 /* Set remaining ctype flags: FIXME: faster way to do this? */
288 str[1] = str[2] = 0;
289 for (i = 0; i < 256; i++)
290 {
291 if (!(MSVCRT__pctype[i] & MSVCRT_LEADBYTE))
292 {
293 str[0] = i;
294 GetStringTypeA(lcid, CT_CTYPE1, str, 1, MSVCRT__pctype + i);
295 }
296 }
297
298 /* Set leadbyte flags */
299 while (traverse[0] || traverse[1])
300 {
301 for( i = traverse[0]; i <= traverse[1]; i++ )
302 MSVCRT_current_ctype[i+1] |= MSVCRT_LEADBYTE;
303 traverse += 2;
304 };
305 }
306}
307
308
309/*********************************************************************
310 * setlocale (MSVCRT.@)
311 */
312char* MSVCRT_setlocale(int category, const char* locale)
313{
314 LCID lcid = 0;
315 locale_search_t lc;
316 int haveLang, haveCountry, haveCP;
317 char* next;
318 int lc_all = 0;
319
320 TRACE("MSVCRT: setlocale (%d %s)\n",category,locale);
321
322 if (category < MSVCRT_LC_MIN || category > MSVCRT_LC_MAX)
323 return NULL;
324
325 if (locale == NULL)
326 {
327 /* Report the current Locale */
328 return MSVCRT_current_lc_all;
329 }
330
331 LOCK_LOCALE;
332
333 if (locale[0] == 'L' && locale[1] == 'C' && locale[2] == '_')
334 {
335 FIXME(":restore previous locale not implemented!\n");
336 /* FIXME: Easiest way to do this is parse the string and
337 * call this function recursively with its elements,
338 * Where they differ for each lc_ type.
339 */
340 UNLOCK_LOCALE;
341 return MSVCRT_current_lc_all;
342 }
343
344 /* Default Locale: Special case handling */
345 if (!strlen(locale) || ((toupper(locale[0]) == 'C') && !locale[1]))
346 {
347 MSVCRT_current_lc_all[0] = 'C';
348 MSVCRT_current_lc_all[1] = '\0';
349 MSVCRT_current_lc_all_cp = GetACP();
350
351 switch (category) {
352 case MSVCRT_LC_ALL:
353 lc_all = 1; /* Fall through all cases ... */
354 case MSVCRT_LC_COLLATE:
355 if (!lc_all) break;
356 case MSVCRT_LC_CTYPE:
357 /* Restore C locale ctype info */
358 MSVCRT___mb_cur_max = 1;
359 memcpy(MSVCRT_current_ctype, MSVCRT__ctype, sizeof(MSVCRT__ctype));
360 memset(MSVCRT_mbctype, 0, sizeof(MSVCRT_mbctype));
361 if (!lc_all) break;
362 case MSVCRT_LC_MONETARY:
363 if (!lc_all) break;
364 case MSVCRT_LC_NUMERIC:
365 if (!lc_all) break;
366 case MSVCRT_LC_TIME:
367 break;
368 }
369 UNLOCK_LOCALE;
370 return MSVCRT_current_lc_all;
371 }
372
373 /* Get locale elements */
374 haveLang = haveCountry = haveCP = 0;
375 memset(&lc,0,sizeof(lc));
376
377 next = strchr(locale,'_');
378 if (next && next != locale)
379 {
380 haveLang = 1;
381 strncpy(lc.search_language,locale,next-locale);
382 locale += next-locale+1;
383 }
384
385 next = strchr(locale,'.');
386 if (next)
387 {
388 haveCP = 1;
389 if (next == locale)
390 {
391 locale++;
392 strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
393 }
394 else
395 {
396 if (haveLang)
397 {
398 haveCountry = 1;
399 strncpy(lc.search_country,locale,next-locale);
400 locale += next-locale+1;
401 }
402 else
403 {
404 haveLang = 1;
405 strncpy(lc.search_language,locale,next-locale);
406 locale += next-locale+1;
407 }
408 strncpy(lc.search_codepage, locale, MAX_ELEM_LEN);
409 }
410 }
411 else
412 {
413 if (haveLang)
414 {
415 haveCountry = 1;
416 strncpy(lc.search_country, locale, MAX_ELEM_LEN);
417 }
418 else
419 {
420 haveLang = 1;
421 strncpy(lc.search_language, locale, MAX_ELEM_LEN);
422 }
423 }
424
425 if (haveCountry)
426 remap_synonym(lc.search_country);
427
428 if (haveCP && !haveCountry && !haveLang)
429 {
430 FIXME(":Codepage only locale not implemented\n");
431 /* FIXME: Use default lang/country and skip locale_to_LCID()
432 * call below...
433 */
434 UNLOCK_LOCALE;
435 return NULL;
436 }
437
438 lcid = MSVCRT_locale_to_LCID(&lc);
439
440 TRACE(":found LCID %ld\n",lcid);
441
442 if (lcid == 0)
443 {
444 UNLOCK_LOCALE;
445 return NULL;
446 }
447
448 MSVCRT_current_lc_all_lcid = lcid;
449
450 _snprintf(MSVCRT_current_lc_all,MAX_LOCALE_LENGTH,"%s_%s.%s",
451 lc.found_language,lc.found_country,lc.found_codepage);
452
453 switch (category) {
454 case MSVCRT_LC_ALL:
455 lc_all = 1; /* Fall through all cases ... */
456 case MSVCRT_LC_COLLATE:
457 if (!lc_all) break;
458 case MSVCRT_LC_CTYPE:
459 msvcrt_set_ctype(atoi(lc.found_codepage),lcid);
460 if (!lc_all) break;
461 case MSVCRT_LC_MONETARY:
462 if (!lc_all) break;
463 case MSVCRT_LC_NUMERIC:
464 if (!lc_all) break;
465 case MSVCRT_LC_TIME:
466 break;
467 }
468 UNLOCK_LOCALE;
469 return MSVCRT_current_lc_all;
470}
471
472
473/*********************************************************************
474 * _Getdays (MSVCRT.@)
475 */
476const char* _Getdays(void)
477{
478 static const char *MSVCRT_days = ":Sun:Sunday:Mon:Monday:Tue:Tuesday:Wed:"
479 "Wednesday:Thu:Thursday:Fri:Friday:Sat:Saturday";
480 /* FIXME: Use locale */
481 TRACE("(void) semi-stub\n");
482 return MSVCRT_days;
483}
484
485/*********************************************************************
486 * _Getmonths (MSVCRT.@)
487 */
488const char* _Getmonths(void)
489{
490 static const char *MSVCRT_months = ":Jan:January:Feb:February:Mar:March:Apr:"
491 "April:May:May:Jun:June:Jul:July:Aug:August:Sep:September:Oct:"
492 "October:Nov:November:Dec:December";
493 /* FIXME: Use locale */
494 TRACE("(void) semi-stub\n");
495 return MSVCRT_months;
496}
497
498/*********************************************************************
499 * _Getnames (MSVCRT.@)
500 */
501const char* _Getnames(void)
502{
503 /* FIXME: */
504 TRACE("(void) stub\n");
505 return "";
506}
507
508/*********************************************************************
509 * _Strftime (MSVCRT.@)
510 */
511const char* _Strftime(char *out, unsigned int len, const char *fmt,
512 const void *tm, void *foo)
513{
514 /* FIXME: */
515 TRACE("(%p %d %s %p %p) stub\n", out, len, fmt, tm, foo);
516 return "";
517}
518
519/* FIXME: MBCP probably belongs in mbcs.c */
520
521/*********************************************************************
522 * _setmbcp (MSVCRT.@)
523 */
524void _setmbcp(int cp)
525{
526 LOCK_LOCALE;
527 dprintf(("MSVCRT: _setmbcp %d",cp));
528 if (MSVCRT_current_lc_all_cp != cp)
529 {
530 /* FIXME: set ctype behaviour for this cp */
531 MSVCRT_current_lc_all_cp = cp;
532 }
533 UNLOCK_LOCALE;
534}
535
536/*********************************************************************
537 * _getmbcp (MSVCRT.@)
538 */
539int _getmbcp(void)
540{
541 dprintf(("MSVCRT: _getmbcp"));
542 return MSVCRT_current_lc_all_cp;
543}
544
545/*********************************************************************
546 * __crtLCMapStringA (MSVCRT.@)
547 */
548int __crtLCMapStringA(
549 LCID lcid, DWORD mapflags, const char* src, int srclen, char* dst,
550 int dstlen, unsigned int codepage, int xflag
551) {
552 FIXME("(lcid %lx, flags %lx, %s(%d), %p(%d), %x, %d), partial stub!\n",
553 lcid,mapflags,src,srclen,dst,dstlen,codepage,xflag);
554 /* FIXME: A bit incorrect. But msvcrt itself just converts its
555 * arguments to wide strings and then calls LCMapStringW
556 */
557 return LCMapStringA(lcid,mapflags,src,srclen,dst,dstlen);
558}
Note: See TracBrowser for help on using the repository browser.