Ignore the LPSURFACE bit if ALLOCONLOAD is set.
[wine/multimedia.git] / ole / ole2nls.c
blobe5ed453b071bf81f83b804672a6e3f0f60f0b56a
1 /*
2 * National Language Support library
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
6 * Copyright 2000 Julio César Gázquez
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config.h"
24 #include "wine/port.h"
26 #include <string.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <locale.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "winternl.h"
37 #include "wine/unicode.h"
38 #include "winver.h"
39 #include "winnls.h"
40 #include "winreg.h"
41 #include "winerror.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(nls);
47 /******************************************************************************
48 * ConvertDefaultLocale (KERNEL32.@)
50 LCID WINAPI ConvertDefaultLocale (LCID lcid)
51 { switch (lcid)
52 { case LOCALE_SYSTEM_DEFAULT:
53 return GetSystemDefaultLCID();
54 case LOCALE_USER_DEFAULT:
55 return GetUserDefaultLCID();
56 case LOCALE_NEUTRAL:
57 return MAKELCID (LANG_NEUTRAL, SUBLANG_NEUTRAL);
59 return MAKELANGID( PRIMARYLANGID(lcid), SUBLANG_NEUTRAL);
63 /******************************************************************************
64 * IsValidLocale [KERNEL32.@]
66 BOOL WINAPI IsValidLocale(LCID lcid,DWORD flags)
68 /* check if language is registered in the kernel32 resources */
69 if(!FindResourceExW(GetModuleHandleA("KERNEL32"), RT_STRINGW, (LPCWSTR)LOCALE_ILANGUAGE, LOWORD(lcid)))
70 return FALSE;
71 else
72 return TRUE;
75 static BOOL CALLBACK EnumResourceLanguagesProcW(HMODULE hModule, LPCWSTR type,
76 LPCWSTR name, WORD LangID, LONG lParam)
78 CHAR bufA[20];
79 WCHAR bufW[20];
80 LOCALE_ENUMPROCW lpfnLocaleEnum = (LOCALE_ENUMPROCW)lParam;
81 sprintf(bufA, "%08x", (UINT)LangID);
82 MultiByteToWideChar(CP_ACP, 0, bufA, -1, bufW, sizeof(bufW)/sizeof(bufW[0]));
83 return lpfnLocaleEnum(bufW);
86 /******************************************************************************
87 * EnumSystemLocalesW [KERNEL32.@]
89 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum,
90 DWORD flags )
92 TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
94 EnumResourceLanguagesW(GetModuleHandleA("KERNEL32"), RT_STRINGW,
95 (LPCWSTR)LOCALE_ILANGUAGE, EnumResourceLanguagesProcW,
96 (LONG)lpfnLocaleEnum);
98 return TRUE;
101 static BOOL CALLBACK EnumResourceLanguagesProcA(HMODULE hModule, LPCSTR type,
102 LPCSTR name, WORD LangID, LONG lParam)
104 CHAR bufA[20];
105 LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
106 sprintf(bufA, "%08x", (UINT)LangID);
107 return lpfnLocaleEnum(bufA);
110 /******************************************************************************
111 * EnumSystemLocalesA [KERNEL32.@]
113 BOOL WINAPI EnumSystemLocalesA(LOCALE_ENUMPROCA lpfnLocaleEnum,
114 DWORD flags)
116 TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
118 EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA,
119 (LPCSTR)LOCALE_ILANGUAGE, EnumResourceLanguagesProcA,
120 (LONG)lpfnLocaleEnum);
122 return TRUE;
125 /***********************************************************************
126 * VerLanguageNameA [KERNEL32.@]
128 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
130 if(!szLang)
131 return 0;
133 return GetLocaleInfoA(MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize);
136 /***********************************************************************
137 * VerLanguageNameW [KERNEL32.@]
139 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
141 if(!szLang)
142 return 0;
144 return GetLocaleInfoW(MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize);
148 static const unsigned char LCM_Unicode_LUT[] = {
149 6 , 3, /* - 1 */
150 6 , 4, /* - 2 */
151 6 , 5, /* - 3 */
152 6 , 6, /* - 4 */
153 6 , 7, /* - 5 */
154 6 , 8, /* - 6 */
155 6 , 9, /* - 7 */
156 6 , 10, /* - 8 */
157 7 , 5, /* - 9 */
158 7 , 6, /* - 10 */
159 7 , 7, /* - 11 */
160 7 , 8, /* - 12 */
161 7 , 9, /* - 13 */
162 6 , 11, /* - 14 */
163 6 , 12, /* - 15 */
164 6 , 13, /* - 16 */
165 6 , 14, /* - 17 */
166 6 , 15, /* - 18 */
167 6 , 16, /* - 19 */
168 6 , 17, /* - 20 */
169 6 , 18, /* - 21 */
170 6 , 19, /* - 22 */
171 6 , 20, /* - 23 */
172 6 , 21, /* - 24 */
173 6 , 22, /* - 25 */
174 6 , 23, /* - 26 */
175 6 , 24, /* - 27 */
176 6 , 25, /* - 28 */
177 6 , 26, /* - 29 */
178 6 , 27, /* - 30 */
179 6 , 28, /* - 31 */
180 7 , 2, /* - 32 */
181 7 , 28, /* ! - 33 */
182 7 , 29, /* " - 34 */ /* " */
183 7 , 31, /* # - 35 */
184 7 , 33, /* $ - 36 */
185 7 , 35, /* % - 37 */
186 7 , 37, /* & - 38 */
187 6 , 128, /* ' - 39 */
188 7 , 39, /* ( - 40 */
189 7 , 42, /* ) - 41 */
190 7 , 45, /* * - 42 */
191 8 , 3, /* + - 43 */
192 7 , 47, /* , - 44 */
193 6 , 130, /* - - 45 */
194 7 , 51, /* . - 46 */
195 7 , 53, /* / - 47 */
196 12 , 3, /* 0 - 48 */
197 12 , 33, /* 1 - 49 */
198 12 , 51, /* 2 - 50 */
199 12 , 70, /* 3 - 51 */
200 12 , 88, /* 4 - 52 */
201 12 , 106, /* 5 - 53 */
202 12 , 125, /* 6 - 54 */
203 12 , 144, /* 7 - 55 */
204 12 , 162, /* 8 - 56 */
205 12 , 180, /* 9 - 57 */
206 7 , 55, /* : - 58 */
207 7 , 58, /* ; - 59 */
208 8 , 14, /* < - 60 */
209 8 , 18, /* = - 61 */
210 8 , 20, /* > - 62 */
211 7 , 60, /* ? - 63 */
212 7 , 62, /* @ - 64 */
213 14 , 2, /* A - 65 */
214 14 , 9, /* B - 66 */
215 14 , 10, /* C - 67 */
216 14 , 26, /* D - 68 */
217 14 , 33, /* E - 69 */
218 14 , 35, /* F - 70 */
219 14 , 37, /* G - 71 */
220 14 , 44, /* H - 72 */
221 14 , 50, /* I - 73 */
222 14 , 53, /* J - 74 */
223 14 , 54, /* K - 75 */
224 14 , 72, /* L - 76 */
225 14 , 81, /* M - 77 */
226 14 , 112, /* N - 78 */
227 14 , 124, /* O - 79 */
228 14 , 126, /* P - 80 */
229 14 , 137, /* Q - 81 */
230 14 , 138, /* R - 82 */
231 14 , 145, /* S - 83 */
232 14 , 153, /* T - 84 */
233 14 , 159, /* U - 85 */
234 14 , 162, /* V - 86 */
235 14 , 164, /* W - 87 */
236 14 , 166, /* X - 88 */
237 14 , 167, /* Y - 89 */
238 14 , 169, /* Z - 90 */
239 7 , 63, /* [ - 91 */
240 7 , 65, /* \ - 92 */
241 7 , 66, /* ] - 93 */
242 7 , 67, /* ^ - 94 */
243 7 , 68, /* _ - 95 */
244 7 , 72, /* ` - 96 */
245 14 , 2, /* a - 97 */
246 14 , 9, /* b - 98 */
247 14 , 10, /* c - 99 */
248 14 , 26, /* d - 100 */
249 14 , 33, /* e - 101 */
250 14 , 35, /* f - 102 */
251 14 , 37, /* g - 103 */
252 14 , 44, /* h - 104 */
253 14 , 50, /* i - 105 */
254 14 , 53, /* j - 106 */
255 14 , 54, /* k - 107 */
256 14 , 72, /* l - 108 */
257 14 , 81, /* m - 109 */
258 14 , 112, /* n - 110 */
259 14 , 124, /* o - 111 */
260 14 , 126, /* p - 112 */
261 14 , 137, /* q - 113 */
262 14 , 138, /* r - 114 */
263 14 , 145, /* s - 115 */
264 14 , 153, /* t - 116 */
265 14 , 159, /* u - 117 */
266 14 , 162, /* v - 118 */
267 14 , 164, /* w - 119 */
268 14 , 166, /* x - 120 */
269 14 , 167, /* y - 121 */
270 14 , 169, /* z - 122 */
271 7 , 74, /* { - 123 */
272 7 , 76, /* | - 124 */
273 7 , 78, /* } - 125 */
274 7 , 80, /* ~ - 126 */
275 6 , 29, /* \x7f - 127 */
276 6 , 30, /* € - 128 */
277 6 , 31, /* � - 129 */
278 7 , 123, /* ‚ - 130 */
279 14 , 35, /* ƒ - 131 */
280 7 , 127, /* „ - 132 */
281 10 , 21, /* … - 133 */
282 10 , 15, /* † - 134 */
283 10 , 16, /* ‡ - 135 */
284 7 , 67, /* ˆ - 136 */
285 10 , 22, /* ‰ - 137 */
286 14 , 145, /* Š - 138 */
287 7 , 136, /* ‹ - 139 */
288 14 + 16 , 124, /* Π- 140 */
289 6 , 43, /* � - 141 */
290 6 , 44, /* Ž - 142 */
291 6 , 45, /* � - 143 */
292 6 , 46, /* � - 144 */
293 7 , 121, /* ‘ - 145 */
294 7 , 122, /* ’ - 146 */
295 7 , 125, /* “ - 147 */
296 7 , 126, /* ” - 148 */
297 10 , 17, /* • - 149 */
298 6 , 137, /* – - 150 */
299 6 , 139, /* — - 151 */
300 7 , 93, /* ˜ - 152 */
301 14 , 156, /* ™ - 153 */
302 14 , 145, /* š - 154 */
303 7 , 137, /* › - 155 */
304 14 + 16 , 124, /* œ - 156 */
305 6 , 59, /* � - 157 */
306 6 , 60, /* ž - 158 */
307 14 , 167, /* Ÿ - 159 */
308 7 , 4, /*   - 160 */
309 7 , 81, /* ¡ - 161 */
310 10 , 2, /* ¢ - 162 */
311 10 , 3, /* £ - 163 */
312 10 , 4, /* ¤ - 164 */
313 10 , 5, /* ¥ - 165 */
314 7 , 82, /* ¦ - 166 */
315 10 , 6, /* § - 167 */
316 7 , 83, /* ¨ - 168 */
317 10 , 7, /* © - 169 */
318 14 , 2, /* ª - 170 */
319 8 , 24, /* « - 171 */
320 10 , 8, /* ¬ - 172 */
321 6 , 131, /* ­ - 173 */
322 10 , 9, /* ® - 174 */
323 7 , 84, /* ¯ - 175 */
324 10 , 10, /* ° - 176 */
325 8 , 23, /* ± - 177 */
326 12 , 51, /* ² - 178 */
327 12 , 70, /* ³ - 179 */
328 7 , 85, /* ´ - 180 */
329 10 , 11, /* µ - 181 */
330 10 , 12, /* ¶ - 182 */
331 10 , 13, /* · - 183 */
332 7 , 86, /* ¸ - 184 */
333 12 , 33, /* ¹ - 185 */
334 14 , 124, /* º - 186 */
335 8 , 26, /* » - 187 */
336 12 , 21, /* ¼ - 188 */
337 12 , 25, /* ½ - 189 */
338 12 , 29, /* ¾ - 190 */
339 7 , 87, /* ¿ - 191 */
340 14 , 2, /* À - 192 */
341 14 , 2, /* Á - 193 */
342 14 , 2, /* Â - 194 */
343 14 , 2, /* Ã - 195 */
344 14 , 2, /* Ä - 196 */
345 14 , 2, /* Å - 197 */
346 14 + 16 , 2, /* Æ - 198 */
347 14 , 10, /* Ç - 199 */
348 14 , 33, /* È - 200 */
349 14 , 33, /* É - 201 */
350 14 , 33, /* Ê - 202 */
351 14 , 33, /* Ë - 203 */
352 14 , 50, /* Ì - 204 */
353 14 , 50, /* Í - 205 */
354 14 , 50, /* Î - 206 */
355 14 , 50, /* Ï - 207 */
356 14 , 26, /* Ð - 208 */
357 14 , 112, /* Ñ - 209 */
358 14 , 124, /* Ò - 210 */
359 14 , 124, /* Ó - 211 */
360 14 , 124, /* Ô - 212 */
361 14 , 124, /* Õ - 213 */
362 14 , 124, /* Ö - 214 */
363 8 , 28, /* × - 215 */
364 14 , 124, /* Ø - 216 */
365 14 , 159, /* Ù - 217 */
366 14 , 159, /* Ú - 218 */
367 14 , 159, /* Û - 219 */
368 14 , 159, /* Ü - 220 */
369 14 , 167, /* Ý - 221 */
370 14 + 32 , 153, /* Þ - 222 */
371 14 + 48 , 145, /* ß - 223 */
372 14 , 2, /* à - 224 */
373 14 , 2, /* á - 225 */
374 14 , 2, /* â - 226 */
375 14 , 2, /* ã - 227 */
376 14 , 2, /* ä - 228 */
377 14 , 2, /* å - 229 */
378 14 + 16 , 2, /* æ - 230 */
379 14 , 10, /* ç - 231 */
380 14 , 33, /* è - 232 */
381 14 , 33, /* é - 233 */
382 14 , 33, /* ê - 234 */
383 14 , 33, /* ë - 235 */
384 14 , 50, /* ì - 236 */
385 14 , 50, /* í - 237 */
386 14 , 50, /* î - 238 */
387 14 , 50, /* ï - 239 */
388 14 , 26, /* ð - 240 */
389 14 , 112, /* ñ - 241 */
390 14 , 124, /* ò - 242 */
391 14 , 124, /* ó - 243 */
392 14 , 124, /* ô - 244 */
393 14 , 124, /* õ - 245 */
394 14 , 124, /* ö - 246 */
395 8 , 29, /* ÷ - 247 */
396 14 , 124, /* ø - 248 */
397 14 , 159, /* ù - 249 */
398 14 , 159, /* ú - 250 */
399 14 , 159, /* û - 251 */
400 14 , 159, /* ü - 252 */
401 14 , 167, /* ý - 253 */
402 14 + 32 , 153, /* þ - 254 */
403 14 , 167 /* ÿ - 255 */ };
405 static const unsigned char LCM_Unicode_LUT_2[] = { 33, 44, 145 };
407 #define LCM_Diacritic_Start 131
409 static const unsigned char LCM_Diacritic_LUT[] = {
410 123, /* ƒ - 131 */
411 2, /* „ - 132 */
412 2, /* … - 133 */
413 2, /* † - 134 */
414 2, /* ‡ - 135 */
415 3, /* ˆ - 136 */
416 2, /* ‰ - 137 */
417 20, /* Š - 138 */
418 2, /* ‹ - 139 */
419 2, /* Π- 140 */
420 2, /* � - 141 */
421 2, /* Ž - 142 */
422 2, /* � - 143 */
423 2, /* � - 144 */
424 2, /* ‘ - 145 */
425 2, /* ’ - 146 */
426 2, /* “ - 147 */
427 2, /* ” - 148 */
428 2, /* • - 149 */
429 2, /* – - 150 */
430 2, /* — - 151 */
431 2, /* ˜ - 152 */
432 2, /* ™ - 153 */
433 20, /* š - 154 */
434 2, /* › - 155 */
435 2, /* œ - 156 */
436 2, /* � - 157 */
437 2, /* ž - 158 */
438 19, /* Ÿ - 159 */
439 2, /*   - 160 */
440 2, /* ¡ - 161 */
441 2, /* ¢ - 162 */
442 2, /* £ - 163 */
443 2, /* ¤ - 164 */
444 2, /* ¥ - 165 */
445 2, /* ¦ - 166 */
446 2, /* § - 167 */
447 2, /* ¨ - 168 */
448 2, /* © - 169 */
449 3, /* ª - 170 */
450 2, /* « - 171 */
451 2, /* ¬ - 172 */
452 2, /* ­ - 173 */
453 2, /* ® - 174 */
454 2, /* ¯ - 175 */
455 2, /* ° - 176 */
456 2, /* ± - 177 */
457 2, /* ² - 178 */
458 2, /* ³ - 179 */
459 2, /* ´ - 180 */
460 2, /* µ - 181 */
461 2, /* ¶ - 182 */
462 2, /* · - 183 */
463 2, /* ¸ - 184 */
464 2, /* ¹ - 185 */
465 3, /* º - 186 */
466 2, /* » - 187 */
467 2, /* ¼ - 188 */
468 2, /* ½ - 189 */
469 2, /* ¾ - 190 */
470 2, /* ¿ - 191 */
471 15, /* À - 192 */
472 14, /* Á - 193 */
473 18, /* Â - 194 */
474 25, /* Ã - 195 */
475 19, /* Ä - 196 */
476 26, /* Å - 197 */
477 2, /* Æ - 198 */
478 28, /* Ç - 199 */
479 15, /* È - 200 */
480 14, /* É - 201 */
481 18, /* Ê - 202 */
482 19, /* Ë - 203 */
483 15, /* Ì - 204 */
484 14, /* Í - 205 */
485 18, /* Î - 206 */
486 19, /* Ï - 207 */
487 104, /* Ð - 208 */
488 25, /* Ñ - 209 */
489 15, /* Ò - 210 */
490 14, /* Ó - 211 */
491 18, /* Ô - 212 */
492 25, /* Õ - 213 */
493 19, /* Ö - 214 */
494 2, /* × - 215 */
495 33, /* Ø - 216 */
496 15, /* Ù - 217 */
497 14, /* Ú - 218 */
498 18, /* Û - 219 */
499 19, /* Ü - 220 */
500 14, /* Ý - 221 */
501 2, /* Þ - 222 */
502 2, /* ß - 223 */
503 15, /* à - 224 */
504 14, /* á - 225 */
505 18, /* â - 226 */
506 25, /* ã - 227 */
507 19, /* ä - 228 */
508 26, /* å - 229 */
509 2, /* æ - 230 */
510 28, /* ç - 231 */
511 15, /* è - 232 */
512 14, /* é - 233 */
513 18, /* ê - 234 */
514 19, /* ë - 235 */
515 15, /* ì - 236 */
516 14, /* í - 237 */
517 18, /* î - 238 */
518 19, /* ï - 239 */
519 104, /* ð - 240 */
520 25, /* ñ - 241 */
521 15, /* ò - 242 */
522 14, /* ó - 243 */
523 18, /* ô - 244 */
524 25, /* õ - 245 */
525 19, /* ö - 246 */
526 2, /* ÷ - 247 */
527 33, /* ø - 248 */
528 15, /* ù - 249 */
529 14, /* ú - 250 */
530 18, /* û - 251 */
531 19, /* ü - 252 */
532 14, /* ý - 253 */
533 2, /* þ - 254 */
534 19, /* ÿ - 255 */
537 /******************************************************************************
538 * OLE2NLS_isPunctuation [INTERNAL]
540 static int OLE2NLS_isPunctuation(unsigned char c)
542 /* "punctuation character" in this context is a character which is
543 considered "less important" during word sort comparison.
544 See LCMapString implementation for the precise definition
545 of "less important". */
547 return (LCM_Unicode_LUT[-2+2*c]==6);
550 /******************************************************************************
551 * OLE2NLS_isNonSpacing [INTERNAL]
553 static int OLE2NLS_isNonSpacing(unsigned char c)
555 /* This function is used by LCMapStringA. Characters
556 for which it returns true are ignored when mapping a
557 string with NORM_IGNORENONSPACE */
558 return ((c==136) || (c==170) || (c==186));
561 /******************************************************************************
562 * OLE2NLS_isSymbol [INTERNAL]
563 * FIXME: handle current locale
565 static int OLE2NLS_isSymbol(unsigned char c)
567 /* This function is used by LCMapStringA. Characters
568 for which it returns true are ignored when mapping a
569 string with NORM_IGNORESYMBOLS */
570 return ( (c!=0) && !(isalpha(c) || isdigit(c)) );
573 /******************************************************************************
574 * identity [Internal]
576 static int identity(int c)
578 return c;
581 /*************************************************************************
582 * LCMapStringA [KERNEL32.@]
584 * Convert a string, or generate a sort key from it.
586 * If (mapflags & LCMAP_SORTKEY), the function will generate
587 * a sort key for the source string. Else, it will convert it
588 * accordingly to the flags LCMAP_UPPERCASE, LCMAP_LOWERCASE,...
590 * RETURNS
591 * Error : 0.
592 * Success : length of the result string.
594 * NOTES
595 * If called with scrlen = -1, the function will compute the length
596 * of the 0-terminated string strsrc by itself.
598 * If called with dstlen = 0, returns the buffer length that
599 * would be required.
601 * NORM_IGNOREWIDTH means to compare ASCII and wide characters
602 * as if they are equal.
603 * In the only code page implemented so far, there may not be
604 * wide characters in strings passed to LCMapStringA,
605 * so there is nothing to be done for this flag.
607 INT WINAPI LCMapStringA(
608 LCID lcid, /* [in] locale identifier created with MAKELCID;
609 LOCALE_SYSTEM_DEFAULT and LOCALE_USER_DEFAULT are
610 predefined values. */
611 DWORD mapflags, /* [in] flags */
612 LPCSTR srcstr, /* [in] source buffer */
613 INT srclen, /* [in] source length */
614 LPSTR dststr, /* [out] destination buffer */
615 INT dstlen) /* [in] destination buffer length */
617 int i;
619 TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
620 lcid,mapflags,debugstr_an(srcstr,srclen),srclen,dststr,dstlen);
622 if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
624 ERR("(src=%s,dest=%s): Invalid NULL string\n",
625 debugstr_an(srcstr,srclen), dststr);
626 SetLastError(ERROR_INVALID_PARAMETER);
627 return 0;
629 if (srclen == -1)
630 srclen = strlen(srcstr) + 1 ; /* (include final '\0') */
632 #define LCMAPSTRINGA_SUPPORTED_FLAGS (LCMAP_UPPERCASE | \
633 LCMAP_LOWERCASE | \
634 LCMAP_SORTKEY | \
635 NORM_IGNORECASE | \
636 NORM_IGNORENONSPACE | \
637 SORT_STRINGSORT | \
638 NORM_IGNOREWIDTH | \
639 NORM_IGNOREKANATYPE)
640 /* FIXME: as long as we don't support Katakana nor Hiragana
641 * characters, we can support NORM_IGNOREKANATYPE
643 if (mapflags & ~LCMAPSTRINGA_SUPPORTED_FLAGS)
645 FIXME("(0x%04lx,0x%08lx,%p,%d,%p,%d): "
646 "unimplemented flags: 0x%08lx\n",
647 lcid,
648 mapflags,
649 srcstr,
650 srclen,
651 dststr,
652 dstlen,
653 mapflags & ~LCMAPSTRINGA_SUPPORTED_FLAGS
657 if ( !(mapflags & LCMAP_SORTKEY) )
659 int i,j;
660 int (*f)(int) = identity;
661 int flag_ignorenonspace = mapflags & NORM_IGNORENONSPACE;
662 int flag_ignoresymbols = mapflags & NORM_IGNORESYMBOLS;
664 if (flag_ignorenonspace || flag_ignoresymbols)
666 /* For some values of mapflags, the length of the resulting
667 string is not known at this point. Windows does map the string
668 and does not SetLastError ERROR_INSUFFICIENT_BUFFER in
669 these cases. */
670 if (dstlen==0)
672 /* Compute required length */
673 for (i=j=0; i < srclen; i++)
675 if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
676 && !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
677 j++;
679 return j;
682 else
684 if (dstlen==0)
685 return srclen;
686 if (dstlen<srclen)
688 SetLastError(ERROR_INSUFFICIENT_BUFFER);
689 return 0;
692 if (mapflags & LCMAP_UPPERCASE)
693 f = toupper;
694 else if (mapflags & LCMAP_LOWERCASE)
695 f = tolower;
696 /* FIXME: NORM_IGNORENONSPACE requires another conversion */
697 for (i=j=0; (i<srclen) && (j<dstlen) ; i++)
699 if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
700 && !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
702 dststr[j] = (CHAR) f(srcstr[i]);
703 j++;
706 return j;
709 /* FIXME: This function completely ignores the "lcid" parameter. */
710 /* else ... (mapflags & LCMAP_SORTKEY) */
712 int unicode_len=0;
713 int case_len=0;
714 int diacritic_len=0;
715 int delayed_punctuation_len=0;
716 char *case_component;
717 char *diacritic_component;
718 char *delayed_punctuation_component;
719 int room,count;
720 int flag_stringsort = mapflags & SORT_STRINGSORT;
722 /* compute how much room we will need */
723 for (i=0;i<srclen;i++)
725 int ofs;
726 unsigned char source_char = srcstr[i];
727 if (source_char!='\0')
729 if (flag_stringsort || !OLE2NLS_isPunctuation(source_char))
731 unicode_len++;
732 if ( LCM_Unicode_LUT[-2+2*source_char] & ~15 )
733 unicode_len++; /* double letter */
735 else
737 delayed_punctuation_len++;
741 if (isupper(source_char))
742 case_len=unicode_len;
744 ofs = source_char - LCM_Diacritic_Start;
745 if ((ofs>=0) && (LCM_Diacritic_LUT[ofs]!=2))
746 diacritic_len=unicode_len;
749 if (mapflags & NORM_IGNORECASE)
750 case_len=0;
751 if (mapflags & NORM_IGNORENONSPACE)
752 diacritic_len=0;
754 room = 2 * unicode_len /* "unicode" component */
755 + diacritic_len /* "diacritic" component */
756 + case_len /* "case" component */
757 + 4 * delayed_punctuation_len /* punctuation in word sort mode */
758 + 4 /* four '\1' separators */
759 + 1 ; /* terminal '\0' */
760 if (dstlen==0)
761 return room;
762 else if (dstlen<room)
764 SetLastError(ERROR_INSUFFICIENT_BUFFER);
765 return 0;
767 #if 0
768 /*FIXME the Pointercheck should not be nessesary */
769 if (IsBadWritePtr (dststr,room))
770 { ERR("bad destination buffer (dststr) : %p,%d\n",dststr,dstlen);
771 SetLastError(ERROR_INSUFFICIENT_BUFFER);
772 return 0;
774 #endif
775 /* locate each component, write separators */
776 diacritic_component = dststr + 2*unicode_len ;
777 *diacritic_component++ = '\1';
778 case_component = diacritic_component + diacritic_len ;
779 *case_component++ = '\1';
780 delayed_punctuation_component = case_component + case_len ;
781 *delayed_punctuation_component++ = '\1';
782 *delayed_punctuation_component++ = '\1';
784 /* read source string char by char, write
785 corresponding weight in each component. */
786 for (i=0,count=0;i<srclen;i++)
788 unsigned char source_char=srcstr[i];
789 if (source_char!='\0')
791 int type,longcode;
792 type = LCM_Unicode_LUT[-2+2*source_char];
793 longcode = type >> 4;
794 type &= 15;
795 if (!flag_stringsort && OLE2NLS_isPunctuation(source_char))
797 WORD encrypted_location = (1<<15) + 7 + 4*count;
798 *delayed_punctuation_component++ = (unsigned char) (encrypted_location>>8);
799 *delayed_punctuation_component++ = (unsigned char) (encrypted_location&255);
800 /* big-endian is used here because it lets string comparison be
801 compatible with numerical comparison */
803 *delayed_punctuation_component++ = type;
804 *delayed_punctuation_component++ = LCM_Unicode_LUT[-1+2*source_char];
805 /* assumption : a punctuation character is never a
806 double or accented letter */
808 else
810 dststr[2*count] = type;
811 dststr[2*count+1] = LCM_Unicode_LUT[-1+2*source_char];
812 if (longcode)
814 if (count<case_len)
815 case_component[count] = ( isupper(source_char) ? 18 : 2 ) ;
816 if (count<diacritic_len)
817 diacritic_component[count] = 2; /* assumption: a double letter
818 is never accented */
819 count++;
821 dststr[2*count] = type;
822 dststr[2*count+1] = *(LCM_Unicode_LUT_2 - 1 + longcode);
823 /* 16 in the first column of LCM_Unicode_LUT --> longcode = 1
824 32 in the first column of LCM_Unicode_LUT --> longcode = 2
825 48 in the first column of LCM_Unicode_LUT --> longcode = 3 */
828 if (count<case_len)
829 case_component[count] = ( isupper(source_char) ? 18 : 2 ) ;
830 if (count<diacritic_len)
832 int ofs = source_char - LCM_Diacritic_Start;
833 diacritic_component[count] = (ofs>=0 ? LCM_Diacritic_LUT[ofs] : 2);
835 count++;
839 dststr[room-1] = '\0';
840 return room;
844 /*************************************************************************
845 * LCMapStringW [KERNEL32.@]
847 * Convert a string, or generate a sort key from it.
849 * NOTE
851 * See LCMapStringA for documentation
853 INT WINAPI LCMapStringW(
854 LCID lcid,DWORD mapflags,LPCWSTR srcstr,INT srclen,LPWSTR dststr,
855 INT dstlen)
857 int i;
859 TRACE("(0x%04lx,0x%08lx,%p,%d,%p,%d)\n",
860 lcid, mapflags, srcstr, srclen, dststr, dstlen);
862 if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
864 ERR("(src=%p,dst=%p): Invalid NULL string\n", srcstr, dststr);
865 SetLastError(ERROR_INVALID_PARAMETER);
866 return 0;
868 if (srclen==-1)
869 srclen = strlenW(srcstr)+1;
871 /* FIXME: Both this function and it's companion LCMapStringA()
872 * completely ignore the "lcid" parameter. In place of the "lcid"
873 * parameter the application must set the "LC_COLLATE" or "LC_ALL"
874 * environment variable prior to invoking this function. */
875 if (mapflags & LCMAP_SORTKEY)
877 /* Possible values of LC_COLLATE. */
878 char *lc_collate_default = 0; /* value prior to this function */
879 char *lc_collate_env = 0; /* value retrieved from the environment */
881 /* General purpose index into strings of any type. */
882 int str_idx = 0;
884 /* Lengths of various strings where the length is measured in
885 * wide characters for wide character strings and in bytes for
886 * native strings. The lengths include the NULL terminator. */
887 size_t returned_len = 0;
888 size_t src_native_len = 0;
889 size_t dst_native_len = 0;
890 size_t dststr_libc_len = 0;
892 /* Native (character set determined by locale) versions of the
893 * strings source and destination strings. */
894 LPSTR src_native = 0;
895 LPSTR dst_native = 0;
897 /* Version of the source and destination strings using the
898 * "wchar_t" Unicode data type needed by various libc functions. */
899 wchar_t *srcstr_libc = 0;
900 wchar_t *dststr_libc = 0;
902 if(!(srcstr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
903 srclen * sizeof(wchar_t))))
905 ERR("Unable to allocate %d bytes for srcstr_libc\n",
906 srclen * sizeof(wchar_t));
907 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
908 return 0;
911 /* Convert source string to a libc Unicode string. */
912 for(str_idx = 0; str_idx < srclen; str_idx++)
914 srcstr_libc[str_idx] = srcstr[str_idx];
917 /* src_native should contain at most 3 bytes for each
918 * multibyte characters in the original srcstr string. */
919 src_native_len = 3 * srclen;
920 if(!(src_native = (LPSTR)HeapAlloc(GetProcessHeap(), 0,
921 src_native_len)))
923 ERR("Unable to allocate %d bytes for src_native\n", src_native_len);
924 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
925 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
926 return 0;
929 /* FIXME: Prior to to setting the LC_COLLATE locale category the
930 * current value is backed up so it can be restored after the
931 * last LC_COLLATE sensitive function returns.
933 * Even though the locale is adjusted for a minimum amount of
934 * time a race condition exists where other threads may be
935 * affected if they invoke LC_COLLATE sensitive functions. One
936 * possible solution is to wrap all LC_COLLATE sensitive Wine
937 * functions, like LCMapStringW(), in a mutex.
939 * Another enhancement to the following would be to set the
940 * LC_COLLATE locale category as a function of the "lcid"
941 * parameter instead of the "LC_COLLATE" environment variable. */
942 if(!(lc_collate_default = setlocale(LC_COLLATE, NULL)))
944 ERR("Unable to query the LC_COLLATE catagory\n");
945 SetLastError(ERROR_INVALID_PARAMETER);
946 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
947 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
948 return 0;
951 if(!(lc_collate_env = setlocale(LC_COLLATE, "")))
953 ERR("Unable to inherit the LC_COLLATE locale category from the "
954 "environment. The \"LC_COLLATE\" environment variable is "
955 "\"%s\".\n", getenv("LC_COLLATE") ?
956 getenv("LC_COLLATE") : "<unset>");
957 SetLastError(ERROR_INVALID_PARAMETER);
958 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
959 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
960 return 0;
963 TRACE("lc_collate_default = %s\n", lc_collate_default);
964 TRACE("lc_collate_env = %s\n", lc_collate_env);
966 /* Convert the libc Unicode string to a native multibyte character
967 * string. */
968 returned_len = wcstombs(src_native, srcstr_libc, src_native_len) + 1;
969 if(returned_len == 0)
971 ERR("wcstombs failed. The string specified (%s) may contain an invalid character.\n",
972 debugstr_w(srcstr));
973 SetLastError(ERROR_INVALID_PARAMETER);
974 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
975 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
976 setlocale(LC_COLLATE, lc_collate_default);
977 return 0;
979 else if(returned_len > src_native_len)
981 src_native[src_native_len - 1] = 0;
982 ERR("wcstombs returned a string (%s) that was longer (%d bytes) "
983 "than expected (%d bytes).\n", src_native, returned_len,
984 dst_native_len);
986 /* Since this is an internal error I'm not sure what the correct
987 * error code is. */
988 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
990 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
991 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
992 setlocale(LC_COLLATE, lc_collate_default);
993 return 0;
995 src_native_len = returned_len;
997 TRACE("src_native = %s src_native_len = %d\n",
998 src_native, src_native_len);
1000 /* dst_native seems to contain at most 4 bytes for each byte in
1001 * the original src_native string. Change if need be since this
1002 * isn't documented by the strxfrm() man page. */
1003 dst_native_len = 4 * src_native_len;
1004 if(!(dst_native = (LPSTR)HeapAlloc(GetProcessHeap(), 0, dst_native_len)))
1006 ERR("Unable to allocate %d bytes for dst_native\n", dst_native_len);
1007 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1008 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1009 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1010 setlocale(LC_COLLATE, lc_collate_default);
1011 return 0;
1014 /* The actual translation is done by the following call to
1015 * strxfrm(). The surrounding code could have been simplified
1016 * by calling wcsxfrm() instead except that wcsxfrm() is not
1017 * available on older Linux systems (RedHat 4.1 with
1018 * libc-5.3.12-17).
1020 * Also, it is possible that the translation could be done by
1021 * various tables as it is done in LCMapStringA(). However, I'm
1022 * not sure what those tables are. */
1023 returned_len = strxfrm(dst_native, src_native, dst_native_len) + 1;
1025 if(returned_len > dst_native_len)
1027 dst_native[dst_native_len - 1] = 0;
1028 ERR("strxfrm returned a string (%s) that was longer (%d bytes) "
1029 "than expected (%d bytes).\n", dst_native, returned_len,
1030 dst_native_len);
1032 /* Since this is an internal error I'm not sure what the correct
1033 * error code is. */
1034 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1036 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1037 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1038 if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
1039 setlocale(LC_COLLATE, lc_collate_default);
1040 return 0;
1042 dst_native_len = returned_len;
1044 TRACE("dst_native = %s dst_native_len = %d\n",
1045 dst_native, dst_native_len);
1047 dststr_libc_len = dst_native_len;
1048 if(!(dststr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
1049 dststr_libc_len * sizeof(wchar_t))))
1051 ERR("Unable to allocate %d bytes for dststr_libc\n",
1052 dststr_libc_len * sizeof(wchar_t));
1053 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1054 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1055 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1056 if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
1057 setlocale(LC_COLLATE, lc_collate_default);
1058 return 0;
1061 /* Convert the native multibyte string to a libc Unicode string. */
1062 returned_len = mbstowcs(dststr_libc, dst_native, dst_native_len) + 1;
1064 /* Restore LC_COLLATE now that the last LC_COLLATE sensitive
1065 * function has returned. */
1066 setlocale(LC_COLLATE, lc_collate_default);
1068 if(returned_len == 0)
1070 ERR("mbstowcs failed. The native version of the translated string "
1071 "(%s) may contain an invalid character.\n", dst_native);
1072 SetLastError(ERROR_INVALID_PARAMETER);
1073 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1074 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1075 if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
1076 if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
1077 return 0;
1079 if(dstlen)
1081 if(returned_len > dstlen)
1083 ERR("mbstowcs returned a string that was longer (%d chars) "
1084 "than the buffer provided (%d chars).\n", returned_len,
1085 dstlen);
1086 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1087 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1088 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1089 if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
1090 if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
1091 return 0;
1093 dstlen = returned_len;
1095 /* Convert a libc Unicode string to the destination string. */
1096 for(str_idx = 0; str_idx < dstlen; str_idx++)
1098 dststr[str_idx] = dststr_libc[str_idx];
1100 TRACE("1st 4 int sized chunks of dststr = %x %x %x %x\n",
1101 *(((int *)dststr) + 0),
1102 *(((int *)dststr) + 1),
1103 *(((int *)dststr) + 2),
1104 *(((int *)dststr) + 3));
1106 else
1108 dstlen = returned_len;
1110 TRACE("dstlen (return) = %d\n", dstlen);
1111 if(srcstr_libc) HeapFree(GetProcessHeap(), 0, srcstr_libc);
1112 if(src_native) HeapFree(GetProcessHeap(), 0, src_native);
1113 if(dst_native) HeapFree(GetProcessHeap(), 0, dst_native);
1114 if(dststr_libc) HeapFree(GetProcessHeap(), 0, dststr_libc);
1115 return dstlen;
1117 else
1119 int (*f)(int)=identity;
1121 if (dstlen==0)
1122 return srclen;
1123 if (dstlen<srclen)
1125 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1126 return 0;
1129 if (mapflags & LCMAP_UPPERCASE)
1130 f = toupper;
1131 else if (mapflags & LCMAP_LOWERCASE)
1132 f = tolower;
1133 for (i=0; i < srclen; i++)
1134 dststr[i] = (WCHAR) f(srcstr[i]);
1135 return srclen;
1140 /***********************************************************************
1141 * OLE2NLS_EstimateMappingLength
1143 * Estimates the number of characters required to hold the string
1144 * computed by LCMapStringA.
1146 * The size is always over-estimated, with a fixed limit on the
1147 * amount of estimation error.
1149 * Note that len == -1 is not permitted.
1151 static inline int OLE2NLS_EstimateMappingLength(LCID lcid, DWORD dwMapFlags,
1152 LPCSTR str, DWORD len)
1154 /* Estimate only for small strings to keep the estimation error from
1155 * becoming too large. */
1156 if (len < 128) return len * 8 + 5;
1157 else return LCMapStringA(lcid, dwMapFlags, str, len, NULL, 0);
1160 /******************************************************************************
1161 * CompareStringA [KERNEL32.@]
1162 * Compares two strings using locale
1164 * RETURNS
1166 * success: CSTR_LESS_THAN, CSTR_EQUAL, CSTR_GREATER_THAN
1167 * failure: 0
1169 * NOTES
1171 * Defaults to a word sort, but uses a string sort if
1172 * SORT_STRINGSORT is set.
1173 * Calls SetLastError for ERROR_INVALID_FLAGS, ERROR_INVALID_PARAMETER.
1175 * BUGS
1177 * This implementation ignores the locale
1179 * FIXME
1181 * Quite inefficient.
1183 int WINAPI CompareStringA(
1184 LCID lcid, /* [in] locale ID */
1185 DWORD fdwStyle, /* [in] comparison-style options */
1186 LPCSTR s1, /* [in] first string */
1187 int l1, /* [in] length of first string */
1188 LPCSTR s2, /* [in] second string */
1189 int l2) /* [in] length of second string */
1191 int mapstring_flags;
1192 int len1,len2;
1193 int result;
1194 LPSTR sk1,sk2;
1195 TRACE("%s and %s\n",
1196 debugstr_an (s1,l1), debugstr_an (s2,l2));
1198 if ( (s1==NULL) || (s2==NULL) )
1200 ERR("(s1=%s,s2=%s): Invalid NULL string\n",
1201 debugstr_an(s1,l1), debugstr_an(s2,l2));
1202 SetLastError(ERROR_INVALID_PARAMETER);
1203 return 0;
1206 if(fdwStyle & NORM_IGNORESYMBOLS)
1207 FIXME("IGNORESYMBOLS not supported\n");
1209 if (l1 == -1) l1 = strlen(s1);
1210 if (l2 == -1) l2 = strlen(s2);
1212 mapstring_flags = LCMAP_SORTKEY | fdwStyle ;
1213 len1 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s1, l1);
1214 len2 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s2, l2);
1216 if ((len1==0)||(len2==0))
1217 return 0; /* something wrong happened */
1219 sk1 = (LPSTR)HeapAlloc(GetProcessHeap(), 0, len1 + len2);
1220 sk2 = sk1 + len1;
1221 if ( (!LCMapStringA(lcid,mapstring_flags,s1,l1,sk1,len1))
1222 || (!LCMapStringA(lcid,mapstring_flags,s2,l2,sk2,len2)) )
1224 ERR("Bug in LCmapStringA.\n");
1225 result = 0;
1227 else
1229 /* strcmp doesn't necessarily return -1, 0, or 1 */
1230 result = strcmp(sk1,sk2);
1232 HeapFree(GetProcessHeap(),0,sk1);
1234 if (result < 0)
1235 return 1;
1236 if (result == 0)
1237 return 2;
1239 /* must be greater, if we reach this point */
1240 return 3;
1243 /******************************************************************************
1244 * CompareStringW [KERNEL32.@]
1245 * This implementation ignores the locale
1246 * FIXME : Does only string sort. Should
1247 * be reimplemented the same way as CompareStringA.
1249 int WINAPI CompareStringW(LCID lcid, DWORD fdwStyle,
1250 LPCWSTR s1, int l1, LPCWSTR s2, int l2)
1252 int len,ret;
1253 if(fdwStyle & NORM_IGNORENONSPACE)
1254 FIXME("IGNORENONSPACE not supported\n");
1255 if(fdwStyle & NORM_IGNORESYMBOLS)
1256 FIXME("IGNORESYMBOLS not supported\n");
1258 if(s1==NULL || s2==NULL)
1260 SetLastError(ERROR_INVALID_PARAMETER);
1261 return 0;
1263 /* Is strcmp defaulting to string sort or to word sort?? */
1264 /* FIXME: Handle NORM_STRINGSORT */
1265 l1 = (l1==-1)?strlenW(s1):l1;
1266 l2 = (l2==-1)?strlenW(s2):l2;
1267 len = l1<l2 ? l1:l2;
1268 ret = (fdwStyle & NORM_IGNORECASE) ? strncmpiW(s1,s2,len) : strncmpW(s1,s2,len);
1269 /* not equal, return 1 or 3 */
1270 if(ret!=0) {
1271 /* need to translate result */
1272 return ((int)ret < 0) ? 1 : 3;
1274 /* same len, return 2 */
1275 if(l1==l2) return 2;
1276 /* the longer one is lexically greater */
1277 return (l1<l2)? 1 : 3;
1280 /***********************************************************************
1281 * lstrcmp (KERNEL32.@)
1282 * lstrcmpA (KERNEL32.@)
1284 INT WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 )
1286 return CompareStringA(LOCALE_SYSTEM_DEFAULT,0,str1,-1,str2,-1) - 2 ;
1290 /***********************************************************************
1291 * lstrcmpW (KERNEL32.@)
1292 * FIXME : should call CompareStringW, when it is implemented.
1293 * This implementation is not "word sort", as it should.
1295 INT WINAPI lstrcmpW( LPCWSTR str1, LPCWSTR str2 )
1297 TRACE("%s and %s\n",
1298 debugstr_w (str1), debugstr_w (str2));
1299 if (!str1 || !str2) {
1300 SetLastError(ERROR_INVALID_PARAMETER);
1301 return 0;
1303 while (*str1 && (*str1 == *str2)) { str1++; str2++; }
1304 return (INT)(*str1 - *str2);
1308 /***********************************************************************
1309 * lstrcmpi (KERNEL32.@)
1310 * lstrcmpiA (KERNEL32.@)
1312 INT WINAPI lstrcmpiA( LPCSTR str1, LPCSTR str2 )
1313 { TRACE("strcmpi %s and %s\n",
1314 debugstr_a (str1), debugstr_a (str2));
1315 return CompareStringA(LOCALE_SYSTEM_DEFAULT,NORM_IGNORECASE,str1,-1,str2,-1)-2;
1319 /***********************************************************************
1320 * lstrcmpiW (KERNEL32.@)
1322 INT WINAPI lstrcmpiW( LPCWSTR str1, LPCWSTR str2 )
1324 if (!str1 || !str2) {
1325 SetLastError(ERROR_INVALID_PARAMETER);
1326 return 0;
1328 return strcmpiW( str1, str2 );
1332 /******************************************************************************
1333 * OLE_GetFormatW [Internal]
1335 * dateformat is set TRUE if being called for a date, false for a time
1337 This function implements stuff for GetDateFormat() and
1338 GetTimeFormat().
1340 d single-digit (no leading zero) day (of month)
1341 dd two-digit day (of month)
1342 ddd short day-of-week name
1343 dddd long day-of-week name
1344 M single-digit month
1345 MM two-digit month
1346 MMM short month name
1347 MMMM full month name
1348 y two-digit year, no leading 0
1349 yy two-digit year
1350 yyyy four-digit year
1351 gg era string
1352 h hours with no leading zero (12-hour)
1353 hh hours with full two digits
1354 H hours with no leading zero (24-hour)
1355 HH hours with full two digits
1356 m minutes with no leading zero
1357 mm minutes with full two digits
1358 s seconds with no leading zero
1359 ss seconds with full two digits
1360 t time marker (A or P)
1361 tt time marker (AM, PM)
1362 '' used to quote literal characters
1363 '' (within a quoted string) indicates a literal '
1365 If TIME_NOMINUTESORSECONDS or TIME_NOSECONDS is specified, the function
1366 removes the separator(s) preceding the minutes and/or seconds element(s).
1368 If TIME_NOTIMEMARKER is specified, the function removes the separator(s)
1369 preceding and following the time marker.
1371 If TIME_FORCE24HOURFORMAT is specified, the function displays any existing
1372 time marker, unless the TIME_NOTIMEMARKER flag is also set.
1374 These functions REQUIRE valid locale, date, and format.
1376 If the time or date is invalid, return 0 and set ERROR_INVALID_PARAMETER
1378 Return value is the number of characters written, or if outlen is zero
1379 it is the number of characters required for the output including
1380 the terminating null.
1382 static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
1383 const SYSTEMTIME* xtime,
1384 LPCWSTR format,
1385 LPWSTR output, INT outlen, int dateformat)
1387 INT outpos;
1388 INT lastFormatPos; /* the position in the output buffer of */
1389 /* the end of the output from the last formatting */
1390 /* character */
1391 BOOL dropUntilNextFormattingChar = FALSE; /* TIME_NOTIMEMARKER drops
1392 all of the text around the dropped marker,
1393 eg. "h@!t@!m" becomes "hm" */
1395 /* make a debug report */
1396 TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
1397 "%p with max len %d\n",
1398 locale, flags, tflags,
1399 xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
1400 debugstr_w(format), format, output, outlen);
1402 /* initialize state variables */
1403 outpos = 0;
1404 lastFormatPos = 0;
1406 while (*format) {
1407 /* Literal string: Maybe terminated early by a \0 */
1408 if (*format == (WCHAR) '\'')
1410 format++;
1412 /* We loop while we haven't reached the end of the format string */
1413 /* and until we haven't found another "'" character */
1414 while (*format)
1416 /* we found what might be the close single quote mark */
1417 /* we need to make sure there isn't another single quote */
1418 /* after it, if there is we need to skip over this quote mark */
1419 /* as the user is trying to put a single quote mark in their output */
1420 if (*format == (WCHAR) '\'')
1422 format++;
1423 if (*format != '\'')
1425 break; /* It was a terminating quote */
1429 /* if outlen is zero then we are couting the number of */
1430 /* characters of space we need to output this text, don't */
1431 /* modify the output buffer */
1432 if (!outlen)
1434 outpos++; /* We are counting */;
1435 } else if (outpos >= outlen)
1437 goto too_short;
1438 } else
1440 /* even drop literal strings */
1441 if(!dropUntilNextFormattingChar)
1443 output[outpos] = *format;
1444 outpos++;
1447 format++;
1449 } else if ( (dateformat && (*format=='d' ||
1450 *format=='M' ||
1451 *format=='y' ||
1452 *format=='g') ) ||
1453 (!dateformat && (*format=='H' ||
1454 *format=='h' ||
1455 *format=='m' ||
1456 *format=='s' ||
1457 *format=='t') ) )
1458 /* if processing a date and we have a date formatting character, OR */
1459 /* if we are processing a time and we have a time formatting character */
1461 int type, count;
1462 char tmp[16];
1463 WCHAR buf[40];
1464 int buflen=0;
1465 type = *format;
1466 format++;
1468 /* clear out the drop text flag if we are in here */
1469 dropUntilNextFormattingChar = FALSE;
1471 /* count up the number of the same letter values in a row that */
1472 /* we get, this lets us distinguish between "s" and "ss" and it */
1473 /* eliminates the duplicate to simplify the below case statement */
1474 for (count = 1; *format == type; format++)
1475 count++;
1477 buf[0] = 0; /* always null terminate the buffer */
1479 switch(type)
1481 case 'd':
1482 if (count >= 4) {
1483 GetLocaleInfoW(locale,
1484 LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7,
1485 buf, sizeof(buf)/sizeof(WCHAR) );
1486 } else if (count == 3) {
1487 GetLocaleInfoW(locale,
1488 LOCALE_SABBREVDAYNAME1 +
1489 (xtime->wDayOfWeek +6)%7,
1490 buf, sizeof(buf)/sizeof(WCHAR) );
1491 } else {
1492 sprintf( tmp, "%.*d", count, xtime->wDay );
1493 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1495 break;
1497 case 'M':
1498 if (count >= 4) {
1499 GetLocaleInfoW(locale, LOCALE_SMONTHNAME1 +
1500 xtime->wMonth -1, buf,
1501 sizeof(buf)/sizeof(WCHAR) );
1502 } else if (count == 3) {
1503 GetLocaleInfoW(locale, LOCALE_SABBREVMONTHNAME1 +
1504 xtime->wMonth -1, buf,
1505 sizeof(buf)/sizeof(WCHAR) );
1506 } else {
1507 sprintf( tmp, "%.*d", count, xtime->wMonth );
1508 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1510 break;
1511 case 'y':
1512 if (count >= 4) {
1513 sprintf( tmp, "%d", xtime->wYear );
1514 } else {
1515 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wYear % 100 );
1517 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1518 break;
1520 case 'g':
1521 if (count == 2) {
1522 FIXME("LOCALE_ICALENDARTYPE unimplemented\n");
1523 strcpy( tmp, "AD" );
1524 } else {
1525 /* Win API sez we copy it verbatim */
1526 strcpy( tmp, "g" );
1528 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1529 break;
1531 case 'h':
1532 /* fallthrough if we are forced to output in 24 hour format */
1533 if(!(tflags & TIME_FORCE24HOURFORMAT))
1535 /* hours 1:00-12:00 --- is this right? */
1536 /* NOTE: 0000 hours is also 12 midnight */
1537 sprintf( tmp, "%.*d", count > 2 ? 2 : count,
1538 xtime->wHour == 0 ? 12 : (xtime->wHour-1)%12 +1);
1539 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1540 break;
1542 case 'H':
1543 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wHour );
1544 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1545 break;
1547 case 'm':
1548 /* if TIME_NOMINUTESORSECONDS don't display minutes */
1549 if(!(tflags & TIME_NOMINUTESORSECONDS))
1551 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wMinute );
1552 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1553 } else
1555 outpos = lastFormatPos;
1557 break;
1559 case 's':
1560 /* if we have a TIME_NOSECONDS or TIME_NOMINUTESORSECONDS
1561 flag then don't display seconds */
1562 if(!(tflags & TIME_NOSECONDS) && !(tflags &
1563 TIME_NOMINUTESORSECONDS))
1565 sprintf( tmp, "%.*d", count > 2 ? 2 : count, xtime->wSecond );
1566 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1567 } else
1569 outpos = lastFormatPos;
1571 break;
1573 case 't':
1574 if(!(tflags & TIME_NOTIMEMARKER))
1576 GetLocaleInfoW(locale, (xtime->wHour < 12) ?
1577 LOCALE_S1159 : LOCALE_S2359,
1578 buf, sizeof(buf) );
1579 if (count == 1)
1581 buf[1] = 0;
1583 } else
1585 outpos = lastFormatPos; /* remove any prior text up until
1586 the output due to formatting characters */
1587 dropUntilNextFormattingChar = TRUE; /* drop everything
1588 until we hit the next formatting character */
1590 break;
1593 /* cat buf onto the output */
1594 buflen = strlenW(buf);
1596 /* we are counting how many characters we need for output */
1597 /* don't modify the output buffer... */
1598 if (!outlen)
1599 /* We are counting */;
1600 else if (outpos + buflen < outlen) {
1601 strcpyW( output + outpos, buf );
1602 } else {
1603 lstrcpynW( output + outpos, buf, outlen - outpos );
1604 /* Is this an undocumented feature we are supporting? */
1605 goto too_short;
1607 outpos += buflen;
1608 lastFormatPos = outpos; /* record the end of the formatting text we just output */
1609 } else /* we are processing a NON formatting character */
1611 /* a literal character */
1612 if (!outlen)
1614 outpos++; /* We are counting */;
1616 else if (outpos >= outlen)
1618 goto too_short;
1620 else /* just copy the character into the output buffer */
1622 /* unless we are dropping characters */
1623 if(!dropUntilNextFormattingChar)
1625 output[outpos] = *format;
1626 outpos++;
1629 format++;
1633 /* final string terminator and sanity check */
1634 if (!outlen)
1635 /* We are counting */;
1636 else if (outpos >= outlen)
1637 goto too_short;
1638 else
1639 output[outpos] = '\0';
1641 outpos++; /* add one for the terminating null character */
1643 TRACE(" returning %d %s\n", outpos, debugstr_w(output));
1644 return outpos;
1646 too_short:
1647 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1648 WARN(" buffer overflow\n");
1649 return 0;
1653 /******************************************************************************
1654 * GetDateFormatA [KERNEL32.@]
1655 * Makes an ASCII string of the date
1657 * Acts the same as GetDateFormatW(), except that it's ASCII.
1659 INT WINAPI GetDateFormatA(LCID locale,DWORD flags,
1660 const SYSTEMTIME* xtime,
1661 LPCSTR format, LPSTR date, INT datelen)
1663 INT ret;
1664 LPWSTR wformat = NULL;
1665 LPWSTR wdate = NULL;
1667 if (format)
1669 wformat = HeapAlloc(GetProcessHeap(), 0,
1670 (strlen(format) + 1) * sizeof(wchar_t));
1672 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
1675 if (date && datelen)
1677 wdate = HeapAlloc(GetProcessHeap(), 0,
1678 (datelen + 1) * sizeof(wchar_t));
1681 ret = GetDateFormatW(locale, flags, xtime, wformat, wdate, datelen);
1683 if (wdate)
1685 WideCharToMultiByte(CP_ACP, 0, wdate, ret, date, datelen, NULL, NULL);
1686 HeapFree(GetProcessHeap(), 0, wdate);
1689 if (wformat)
1691 HeapFree(GetProcessHeap(), 0, wformat);
1694 return ret;
1697 /******************************************************************************
1698 * GetDateFormatW [KERNEL32.@]
1699 * Makes a Unicode string of the date
1701 * This function uses format to format the date, or, if format
1702 * is NULL, uses the default for the locale. format is a string
1703 * of literal fields and characters as follows:
1705 * - d single-digit (no leading zero) day (of month)
1706 * - dd two-digit day (of month)
1707 * - ddd short day-of-week name
1708 * - dddd long day-of-week name
1709 * - M single-digit month
1710 * - MM two-digit month
1711 * - MMM short month name
1712 * - MMMM full month name
1713 * - y two-digit year, no leading 0
1714 * - yy two-digit year
1715 * - yyyy four-digit year
1716 * - gg era string
1718 * Accepts & returns sizes as counts of Unicode characters.
1720 INT WINAPI GetDateFormatW(LCID locale,DWORD flags,
1721 const SYSTEMTIME* xtime,
1722 LPCWSTR format,
1723 LPWSTR date, INT datelen)
1725 WCHAR format_buf[40];
1726 LPCWSTR thisformat;
1727 SYSTEMTIME t;
1728 LPSYSTEMTIME thistime;
1729 LCID thislocale;
1730 INT ret;
1731 FILETIME ft;
1732 BOOL res;
1734 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
1735 locale,flags,xtime,debugstr_w(format),date,datelen);
1737 /* Tests */
1738 if (flags && format) /* if lpFormat is non-null then flags must be zero */
1740 SetLastError (ERROR_INVALID_FLAGS);
1741 return 0;
1743 if (datelen && !date)
1745 SetLastError (ERROR_INVALID_PARAMETER);
1746 return 0;
1748 if (!locale)
1750 locale = LOCALE_SYSTEM_DEFAULT;
1753 if (locale == LOCALE_SYSTEM_DEFAULT)
1755 thislocale = GetSystemDefaultLCID();
1756 } else if (locale == LOCALE_USER_DEFAULT)
1758 thislocale = GetUserDefaultLCID();
1759 } else
1761 thislocale = locale;
1764 /* check for invalid flag combinations */
1765 if((flags & DATE_LTRREADING) && (flags & DATE_RTLREADING))
1767 SetLastError (ERROR_INVALID_FLAGS);
1768 return 0;
1771 /* DATE_SHORTDATE, DATE_LONGDATE and DATE_YEARMONTH are mutually */
1772 /* exclusive */
1773 if((flags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
1774 && !((flags & DATE_SHORTDATE) ^ (flags &
1775 DATE_LONGDATE) ^ (flags & DATE_YEARMONTH)))
1777 SetLastError (ERROR_INVALID_FLAGS);
1778 return 0;
1781 /* if the user didn't pass in a pointer to the current time we read it */
1782 /* here */
1783 if (xtime == NULL)
1785 GetSystemTime(&t);
1786 } else
1788 /* NOTE: check here before we perform the SystemTimeToFileTime conversion */
1789 /* because this conversion will fix invalid time values */
1790 /* check to see if the time/date is valid */
1791 /* set ERROR_INVALID_PARAMETER and return 0 if invalid */
1792 if((xtime->wDay > 31) || (xtime->wDayOfWeek > 6) || (xtime->wMonth > 12))
1794 SetLastError(ERROR_INVALID_PARAMETER);
1795 return 0;
1798 /* Silently correct wDayOfWeek by transforming to FileTime and back again */
1799 res=SystemTimeToFileTime(xtime,&ft);
1801 /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */
1802 if(!res)
1804 SetLastError(ERROR_INVALID_PARAMETER);
1805 return 0;
1807 FileTimeToSystemTime(&ft,&t);
1810 thistime = &t;
1812 if (format == NULL)
1814 GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
1815 ? LOCALE_SLONGDATE
1816 : LOCALE_SSHORTDATE),
1817 format_buf, sizeof(format_buf)/sizeof(*format_buf));
1818 thisformat = format_buf;
1819 } else
1821 thisformat = format;
1825 ret = OLE_GetFormatW(thislocale, flags, 0, thistime, thisformat,
1826 date, datelen, 1);
1829 TRACE("GetDateFormatW() returning %d, with data=%s\n",
1830 ret, debugstr_w(date));
1831 return ret;
1834 /**************************************************************************
1835 * EnumDateFormatsA (KERNEL32.@)
1837 BOOL WINAPI EnumDateFormatsA(
1838 DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
1840 LCID Loc = GetUserDefaultLCID();
1841 if(!lpDateFmtEnumProc)
1843 SetLastError(ERROR_INVALID_PARAMETER);
1844 return FALSE;
1847 switch( Loc )
1850 case 0x00000407: /* (Loc,"de_DE") */
1852 switch(dwFlags)
1854 case DATE_SHORTDATE:
1855 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1856 if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1857 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1858 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1859 return TRUE;
1860 case DATE_LONGDATE:
1861 if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1862 if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1863 if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1864 return TRUE;
1865 default:
1866 FIXME("Unknown date format (%ld)\n", dwFlags);
1867 SetLastError(ERROR_INVALID_PARAMETER);
1868 return FALSE;
1872 case 0x0000040c: /* (Loc,"fr_FR") */
1874 switch(dwFlags)
1876 case DATE_SHORTDATE:
1877 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1878 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1879 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1880 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1881 return TRUE;
1882 case DATE_LONGDATE:
1883 if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
1884 if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
1885 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1886 return TRUE;
1887 default:
1888 FIXME("Unknown date format (%ld)\n", dwFlags);
1889 SetLastError(ERROR_INVALID_PARAMETER);
1890 return FALSE;
1894 case 0x00000c0c: /* (Loc,"fr_CA") */
1896 switch(dwFlags)
1898 case DATE_SHORTDATE:
1899 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1900 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1901 if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
1902 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1903 return TRUE;
1904 case DATE_LONGDATE:
1905 if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
1906 if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
1907 return TRUE;
1908 default:
1909 FIXME("Unknown date format (%ld)\n", dwFlags);
1910 SetLastError(ERROR_INVALID_PARAMETER);
1911 return FALSE;
1915 case 0x00000809: /* (Loc,"en_UK") */
1917 switch(dwFlags)
1919 case DATE_SHORTDATE:
1920 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1921 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1922 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1923 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1924 return TRUE;
1925 case DATE_LONGDATE:
1926 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1927 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1928 return TRUE;
1929 default:
1930 FIXME("Unknown date format (%ld)\n", dwFlags);
1931 SetLastError(ERROR_INVALID_PARAMETER);
1932 return FALSE;
1936 case 0x00000c09: /* (Loc,"en_AU") */
1938 switch(dwFlags)
1940 case DATE_SHORTDATE:
1941 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1942 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1943 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1944 return TRUE;
1945 case DATE_LONGDATE:
1946 if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
1947 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1948 return TRUE;
1949 default:
1950 FIXME("Unknown date format (%ld)\n", dwFlags);
1951 SetLastError(ERROR_INVALID_PARAMETER);
1952 return FALSE;
1956 case 0x00001009: /* (Loc,"en_CA") */
1958 switch(dwFlags)
1960 case DATE_SHORTDATE:
1961 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1962 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1963 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1964 if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
1965 return TRUE;
1966 case DATE_LONGDATE:
1967 if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
1968 if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
1969 return TRUE;
1970 default:
1971 FIXME("Unknown date format (%ld)\n", dwFlags);
1972 SetLastError(ERROR_INVALID_PARAMETER);
1973 return FALSE;
1977 case 0x00001409: /* (Loc,"en_NZ") */
1979 switch(dwFlags)
1981 case DATE_SHORTDATE:
1982 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1983 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1984 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1985 return TRUE;
1986 case DATE_LONGDATE:
1987 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1988 if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
1989 return TRUE;
1990 default:
1991 FIXME("Unknown date format (%ld)\n", dwFlags);
1992 SetLastError(ERROR_INVALID_PARAMETER);
1993 return FALSE;
1997 case 0x00001809: /* (Loc,"en_IE") */
1999 switch(dwFlags)
2001 case DATE_SHORTDATE:
2002 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2003 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2004 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2005 return TRUE;
2006 case DATE_LONGDATE:
2007 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2008 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2009 return TRUE;
2010 default:
2011 FIXME("Unknown date format (%ld)\n", dwFlags);
2012 SetLastError(ERROR_INVALID_PARAMETER);
2013 return FALSE;
2017 case 0x00001c09: /* (Loc,"en_ZA") */
2019 switch(dwFlags)
2021 case DATE_SHORTDATE:
2022 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2023 return TRUE;
2024 case DATE_LONGDATE:
2025 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2026 return TRUE;
2027 default:
2028 FIXME("Unknown date format (%ld)\n", dwFlags);
2029 SetLastError(ERROR_INVALID_PARAMETER);
2030 return FALSE;
2034 case 0x00002009: /* (Loc,"en_JM") */
2036 switch(dwFlags)
2038 case DATE_SHORTDATE:
2039 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2040 return TRUE;
2041 case DATE_LONGDATE:
2042 if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
2043 if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
2044 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
2045 if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
2046 return TRUE;
2047 default:
2048 FIXME("Unknown date format (%ld)\n", dwFlags);
2049 SetLastError(ERROR_INVALID_PARAMETER);
2050 return FALSE;
2054 case 0x00002809: /* (Loc,"en_BZ") */
2055 case 0x00002c09: /* (Loc,"en_TT") */
2057 switch(dwFlags)
2059 case DATE_SHORTDATE:
2060 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2061 return TRUE;
2062 case DATE_LONGDATE:
2063 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
2064 return TRUE;
2065 default:
2066 FIXME("Unknown date format (%ld)\n", dwFlags);
2067 SetLastError(ERROR_INVALID_PARAMETER);
2068 return FALSE;
2072 default: /* default to US English "en_US" */
2074 switch(dwFlags)
2076 case DATE_SHORTDATE:
2077 if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
2078 if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
2079 if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
2080 if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
2081 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2082 if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
2083 return TRUE;
2084 case DATE_LONGDATE:
2085 if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
2086 if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
2087 if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
2088 if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
2089 return TRUE;
2090 default:
2091 FIXME("Unknown date format (%ld)\n", dwFlags);
2092 SetLastError(ERROR_INVALID_PARAMETER);
2093 return FALSE;
2099 /**************************************************************************
2100 * EnumDateFormatsW (KERNEL32.@)
2102 BOOL WINAPI EnumDateFormatsW(
2103 DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
2105 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
2106 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2107 return FALSE;
2110 /**************************************************************************
2111 * EnumTimeFormatsA (KERNEL32.@)
2113 BOOL WINAPI EnumTimeFormatsA(
2114 TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2116 LCID Loc = GetUserDefaultLCID();
2117 if(!lpTimeFmtEnumProc)
2119 SetLastError(ERROR_INVALID_PARAMETER);
2120 return FALSE;
2122 if(dwFlags)
2124 FIXME("Unknown time format (%ld)\n", dwFlags);
2127 switch( Loc )
2129 case 0x00000407: /* (Loc,"de_DE") */
2131 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2132 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2133 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2134 if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
2135 if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
2136 return TRUE;
2139 case 0x0000040c: /* (Loc,"fr_FR") */
2140 case 0x00000c0c: /* (Loc,"fr_CA") */
2142 if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
2143 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2144 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2145 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2146 if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
2147 return TRUE;
2150 case 0x00000809: /* (Loc,"en_UK") */
2151 case 0x00000c09: /* (Loc,"en_AU") */
2152 case 0x00001409: /* (Loc,"en_NZ") */
2153 case 0x00001809: /* (Loc,"en_IE") */
2155 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2156 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2157 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2158 return TRUE;
2161 case 0x00001c09: /* (Loc,"en_ZA") */
2162 case 0x00002809: /* (Loc,"en_BZ") */
2163 case 0x00002c09: /* (Loc,"en_TT") */
2165 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2166 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2167 return TRUE;
2170 default: /* default to US style "en_US" */
2172 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2173 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2174 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2175 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2176 return TRUE;
2181 /**************************************************************************
2182 * EnumTimeFormatsW (KERNEL32.@)
2184 BOOL WINAPI EnumTimeFormatsW(
2185 TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2187 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
2188 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2189 return FALSE;
2192 /**************************************************************************
2193 * This function is used just locally !
2194 * Description: Inverts a string.
2196 static void OLE_InvertString(char* string)
2198 char sTmpArray[128];
2199 INT counter, i = 0;
2201 for (counter = strlen(string); counter > 0; counter--)
2203 memcpy(sTmpArray + i, string + counter-1, 1);
2204 i++;
2206 memcpy(sTmpArray + i, "\0", 1);
2207 strcpy(string, sTmpArray);
2210 /***************************************************************************************
2211 * This function is used just locally !
2212 * Description: Test if the given string (psNumber) is valid or not.
2213 * The valid characters are the following:
2214 * - Characters '0' through '9'.
2215 * - One decimal point (dot) if the number is a floating-point value.
2216 * - A minus sign in the first character position if the number is
2217 * a negative value.
2218 * If the function succeeds, psBefore/psAfter will point to the string
2219 * on the right/left of the decimal symbol. pbNegative indicates if the
2220 * number is negative.
2222 static INT OLE_GetNumberComponents(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
2224 char sNumberSet[] = "0123456789";
2225 BOOL bInDecimal = FALSE;
2227 /* Test if we do have a minus sign */
2228 if ( *pInput == '-' )
2230 *pbNegative = TRUE;
2231 pInput++; /* Jump to the next character. */
2234 while(*pInput != '\0')
2236 /* Do we have a valid numeric character */
2237 if ( strchr(sNumberSet, *pInput) != NULL )
2239 if (bInDecimal == TRUE)
2240 *psAfter++ = *pInput;
2241 else
2242 *psBefore++ = *pInput;
2244 else
2246 /* Is this a decimal point (dot) */
2247 if ( *pInput == '.' )
2249 /* Is it the first time we find it */
2250 if ((bInDecimal == FALSE))
2251 bInDecimal = TRUE;
2252 else
2253 return -1; /* ERROR: Invalid parameter */
2255 else
2257 /* It's neither a numeric character, nor a decimal point.
2258 * Thus, return an error.
2260 return -1;
2263 pInput++;
2266 /* Add an End of Line character to the output buffers */
2267 *psBefore = '\0';
2268 *psAfter = '\0';
2270 return 0;
2273 /**************************************************************************
2274 * This function is used just locally !
2275 * Description: A number could be formatted using different numbers
2276 * of "digits in group" (example: 4;3;2;0).
2277 * The first parameter of this function is an array
2278 * containing the rule to be used. Its format is the following:
2279 * |NDG|DG1|DG2|...|0|
2280 * where NDG is the number of used "digits in group" and DG1, DG2,
2281 * are the corresponding "digits in group".
2282 * Thus, this function returns the grouping value in the array
2283 * pointed by the second parameter.
2285 static INT OLE_GetGrouping(char* sRule, INT index)
2287 char sData[2], sRuleSize[2];
2288 INT nData, nRuleSize;
2290 memcpy(sRuleSize, sRule, 1);
2291 memcpy(sRuleSize+1, "\0", 1);
2292 nRuleSize = atoi(sRuleSize);
2294 if (index > 0 && index < nRuleSize)
2296 memcpy(sData, sRule+index, 1);
2297 memcpy(sData+1, "\0", 1);
2298 nData = atoi(sData);
2301 else
2303 memcpy(sData, sRule+nRuleSize-1, 1);
2304 memcpy(sData+1, "\0", 1);
2305 nData = atoi(sData);
2308 return nData;
2311 /**************************************************************************
2312 * GetNumberFormatA (KERNEL32.@)
2314 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
2315 LPCSTR lpvalue, const NUMBERFMTA * lpFormat,
2316 LPSTR lpNumberStr, int cchNumber)
2318 char sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
2319 INT nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
2320 char sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
2321 char sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
2322 char *pStr = NULL, *pTmpStr = NULL;
2323 LCID systemDefaultLCID;
2324 BOOL bNegative = FALSE;
2325 enum Operations
2327 USE_PARAMETER,
2328 USE_LOCALEINFO,
2329 USE_SYSTEMDEFAULT,
2330 RETURN_ERROR
2331 } used_operation;
2333 strncpy(sNumber, lpvalue, 128);
2334 sNumber[127] = '\0';
2336 /* Make sure we have a valid input string, get the number
2337 * of digits before and after the decimal symbol, and check
2338 * if this is a negative number.
2340 if ( OLE_GetNumberComponents(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
2342 nNumberDecimal = strlen(sDigitsBeforeDecimal);
2343 nDigits = strlen(sDigitsAfterDecimal);
2345 else
2347 SetLastError(ERROR_INVALID_PARAMETER);
2348 return 0;
2351 /* Which source will we use to format the string */
2352 used_operation = RETURN_ERROR;
2353 if (lpFormat != NULL)
2355 if (dwflags == 0)
2356 used_operation = USE_PARAMETER;
2358 else
2360 if (dwflags & LOCALE_NOUSEROVERRIDE)
2361 used_operation = USE_SYSTEMDEFAULT;
2362 else
2363 used_operation = USE_LOCALEINFO;
2366 /* Load the fields we need */
2367 switch(used_operation)
2369 case USE_LOCALEINFO:
2370 GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2371 GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2372 GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2373 GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2374 GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2375 GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2376 break;
2377 case USE_PARAMETER:
2378 sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
2379 strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
2380 sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
2381 /* Win95-WinME only allow 0-9 for grouping, no matter what MSDN says. */
2382 strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
2383 sprintf(sILZero, "%d",lpFormat->LeadingZero);
2384 sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
2385 break;
2386 case USE_SYSTEMDEFAULT:
2387 systemDefaultLCID = GetSystemDefaultLCID();
2388 GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2389 GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2390 GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2391 GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2392 GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2393 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2394 break;
2395 default:
2396 SetLastError(ERROR_INVALID_PARAMETER);
2397 return 0;
2400 nNumberDigits = atoi(sNumberDigits);
2402 /* Remove the ";" */
2403 i=0;
2404 j = 1;
2405 for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
2407 if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
2409 memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
2410 i++;
2411 j++;
2414 sprintf(sBuffer, "%d", i);
2415 memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by OLE_GetGrouping() ) */
2416 memcpy(sRule + j, "\0", 1);
2418 /* First, format the digits before the decimal. */
2419 if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
2421 /* Working on an inverted string is easier ! */
2422 OLE_InvertString(sDigitsBeforeDecimal);
2424 nStep = nCounter = i = j = 0;
2425 nRuleIndex = 1;
2426 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2427 if (nGrouping == 0) /* If the first grouping is zero */
2428 nGrouping = nNumberDecimal; /* Don't do grouping */
2430 /* Here, we will loop until we reach the end of the string.
2431 * An internal counter (j) is used in order to know when to
2432 * insert the "digit group symbol".
2434 while (nNumberDecimal > 0)
2436 i = nCounter + nStep;
2437 memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
2438 nCounter++;
2439 j++;
2440 if (j >= nGrouping)
2442 j = 0;
2443 if (nRuleIndex < sRule[0])
2444 nRuleIndex++;
2445 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2446 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
2447 nStep+= strlen(sDigitGroupSymbol);
2450 nNumberDecimal--;
2453 memcpy(sDestination + i+1, "\0", 1);
2454 /* Get the string in the right order ! */
2455 OLE_InvertString(sDestination);
2457 else
2459 nLZ = atoi(sILZero);
2460 if (nLZ != 0)
2462 /* Use 0.xxx instead of .xxx */
2463 memcpy(sDestination, "0", 1);
2464 memcpy(sDestination+1, "\0", 1);
2466 else
2467 memcpy(sDestination, "\0", 1);
2471 /* Second, format the digits after the decimal. */
2472 j = 0;
2473 nCounter = nNumberDigits;
2474 if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
2476 i = strlen(sNumber) - strlen(pStr) + 1;
2477 strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
2478 j = strlen(sDigitsAfterDecimal);
2479 if (j < nNumberDigits)
2480 nCounter = nNumberDigits-j;
2482 for (i=0;i<nCounter;i++)
2483 memcpy(sDigitsAfterDecimal+i+j, "0", 1);
2484 memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
2486 i = strlen(sDestination);
2487 j = strlen(sDigitsAfterDecimal);
2488 /* Finally, construct the resulting formatted string. */
2490 for (nCounter=0; nCounter<i; nCounter++)
2491 memcpy(sNumber + nCounter, sDestination + nCounter, 1);
2493 memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
2495 for (i=0; i<j; i++)
2496 memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
2497 memcpy(sNumber + nCounter+i+ (i ? strlen(sDecimalSymbol) : 0), "\0", 1);
2499 /* Is it a negative number */
2500 if (bNegative == TRUE)
2502 i = atoi(sNegNumber);
2503 pStr = sDestination;
2504 pTmpStr = sNumber;
2505 switch (i)
2507 case 0:
2508 *pStr++ = '(';
2509 while (*sNumber != '\0')
2510 *pStr++ = *pTmpStr++;
2511 *pStr++ = ')';
2512 break;
2513 case 1:
2514 *pStr++ = '-';
2515 while (*pTmpStr != '\0')
2516 *pStr++ = *pTmpStr++;
2517 break;
2518 case 2:
2519 *pStr++ = '-';
2520 *pStr++ = ' ';
2521 while (*pTmpStr != '\0')
2522 *pStr++ = *pTmpStr++;
2523 break;
2524 case 3:
2525 while (*pTmpStr != '\0')
2526 *pStr++ = *pTmpStr++;
2527 *pStr++ = '-';
2528 break;
2529 case 4:
2530 while (*pTmpStr != '\0')
2531 *pStr++ = *pTmpStr++;
2532 *pStr++ = ' ';
2533 *pStr++ = '-';
2534 break;
2535 default:
2536 while (*pTmpStr != '\0')
2537 *pStr++ = *pTmpStr++;
2538 break;
2541 else
2542 strcpy(sDestination, sNumber);
2544 /* If cchNumber is zero, then returns the number of bytes or characters
2545 * required to hold the formatted number string
2547 retVal = strlen(sDestination) + 1;
2548 if (cchNumber!=0)
2550 memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
2551 if (cchNumber < retVal) {
2552 retVal = 0;
2553 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2556 return retVal;
2559 /**************************************************************************
2560 * GetNumberFormatW (KERNEL32.@)
2562 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
2563 LPCWSTR lpvalue, const NUMBERFMTW * lpFormat,
2564 LPWSTR lpNumberStr, int cchNumber)
2566 FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
2568 lstrcpynW( lpNumberStr, lpvalue, cchNumber );
2569 return cchNumber? strlenW( lpNumberStr ) : 0;
2572 /**************************************************************************
2573 * GetCurrencyFormatA (KERNEL32.@)
2575 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
2576 LPCSTR lpvalue, const CURRENCYFMTA * lpFormat,
2577 LPSTR lpCurrencyStr, int cchCurrency)
2579 UINT nPosOrder, nNegOrder;
2580 INT retVal;
2581 char sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
2582 char *pDestination = sDestination;
2583 char sNumberFormated[128];
2584 char *pNumberFormated = sNumberFormated;
2585 LCID systemDefaultLCID;
2586 BOOL bIsPositive = FALSE, bValidFormat = FALSE;
2587 enum Operations
2589 USE_PARAMETER,
2590 USE_LOCALEINFO,
2591 USE_SYSTEMDEFAULT,
2592 RETURN_ERROR
2593 } used_operation;
2595 NUMBERFMTA numberFmt;
2597 /* Which source will we use to format the string */
2598 used_operation = RETURN_ERROR;
2599 if (lpFormat != NULL)
2601 if (dwflags == 0)
2602 used_operation = USE_PARAMETER;
2604 else
2606 if (dwflags & LOCALE_NOUSEROVERRIDE)
2607 used_operation = USE_SYSTEMDEFAULT;
2608 else
2609 used_operation = USE_LOCALEINFO;
2612 /* Load the fields we need */
2613 switch(used_operation)
2615 case USE_LOCALEINFO:
2616 /* Specific to CURRENCYFMTA */
2617 GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2618 GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2619 GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2621 nPosOrder = atoi(sPosOrder);
2622 nNegOrder = atoi(sNegOrder);
2623 break;
2624 case USE_PARAMETER:
2625 /* Specific to CURRENCYFMTA */
2626 nNegOrder = lpFormat->NegativeOrder;
2627 nPosOrder = lpFormat->PositiveOrder;
2628 strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
2629 break;
2630 case USE_SYSTEMDEFAULT:
2631 systemDefaultLCID = GetSystemDefaultLCID();
2632 /* Specific to CURRENCYFMTA */
2633 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2634 GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2635 GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2637 nPosOrder = atoi(sPosOrder);
2638 nNegOrder = atoi(sNegOrder);
2639 break;
2640 default:
2641 SetLastError(ERROR_INVALID_PARAMETER);
2642 return 0;
2645 /* Construct a temporary number format structure */
2646 if (lpFormat != NULL)
2648 numberFmt.NumDigits = lpFormat->NumDigits;
2649 numberFmt.LeadingZero = lpFormat->LeadingZero;
2650 numberFmt.Grouping = lpFormat->Grouping;
2651 numberFmt.NegativeOrder = 0;
2652 numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
2653 numberFmt.lpThousandSep = lpFormat->lpThousandSep;
2654 bValidFormat = TRUE;
2657 /* Make a call to GetNumberFormatA() */
2658 if (*lpvalue == '-')
2660 bIsPositive = FALSE;
2661 retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2663 else
2665 bIsPositive = TRUE;
2666 retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2669 if (retVal == 0)
2670 return 0;
2672 /* construct the formatted string */
2673 if (bIsPositive)
2675 switch (nPosOrder)
2677 case 0: /* Prefix, no separation */
2678 strcpy (pDestination, sCurrencySymbol);
2679 strcat (pDestination, pNumberFormated);
2680 break;
2681 case 1: /* Suffix, no separation */
2682 strcpy (pDestination, pNumberFormated);
2683 strcat (pDestination, sCurrencySymbol);
2684 break;
2685 case 2: /* Prefix, 1 char separation */
2686 strcpy (pDestination, sCurrencySymbol);
2687 strcat (pDestination, " ");
2688 strcat (pDestination, pNumberFormated);
2689 break;
2690 case 3: /* Suffix, 1 char separation */
2691 strcpy (pDestination, pNumberFormated);
2692 strcat (pDestination, " ");
2693 strcat (pDestination, sCurrencySymbol);
2694 break;
2695 default:
2696 SetLastError(ERROR_INVALID_PARAMETER);
2697 return 0;
2700 else /* negative number */
2702 switch (nNegOrder)
2704 case 0: /* format: ($1.1) */
2705 strcpy (pDestination, "(");
2706 strcat (pDestination, sCurrencySymbol);
2707 strcat (pDestination, pNumberFormated);
2708 strcat (pDestination, ")");
2709 break;
2710 case 1: /* format: -$1.1 */
2711 strcpy (pDestination, "-");
2712 strcat (pDestination, sCurrencySymbol);
2713 strcat (pDestination, pNumberFormated);
2714 break;
2715 case 2: /* format: $-1.1 */
2716 strcpy (pDestination, sCurrencySymbol);
2717 strcat (pDestination, "-");
2718 strcat (pDestination, pNumberFormated);
2719 break;
2720 case 3: /* format: $1.1- */
2721 strcpy (pDestination, sCurrencySymbol);
2722 strcat (pDestination, pNumberFormated);
2723 strcat (pDestination, "-");
2724 break;
2725 case 4: /* format: (1.1$) */
2726 strcpy (pDestination, "(");
2727 strcat (pDestination, pNumberFormated);
2728 strcat (pDestination, sCurrencySymbol);
2729 strcat (pDestination, ")");
2730 break;
2731 case 5: /* format: -1.1$ */
2732 strcpy (pDestination, "-");
2733 strcat (pDestination, pNumberFormated);
2734 strcat (pDestination, sCurrencySymbol);
2735 break;
2736 case 6: /* format: 1.1-$ */
2737 strcpy (pDestination, pNumberFormated);
2738 strcat (pDestination, "-");
2739 strcat (pDestination, sCurrencySymbol);
2740 break;
2741 case 7: /* format: 1.1$- */
2742 strcpy (pDestination, pNumberFormated);
2743 strcat (pDestination, sCurrencySymbol);
2744 strcat (pDestination, "-");
2745 break;
2746 case 8: /* format: -1.1 $ */
2747 strcpy (pDestination, "-");
2748 strcat (pDestination, pNumberFormated);
2749 strcat (pDestination, " ");
2750 strcat (pDestination, sCurrencySymbol);
2751 break;
2752 case 9: /* format: -$ 1.1 */
2753 strcpy (pDestination, "-");
2754 strcat (pDestination, sCurrencySymbol);
2755 strcat (pDestination, " ");
2756 strcat (pDestination, pNumberFormated);
2757 break;
2758 case 10: /* format: 1.1 $- */
2759 strcpy (pDestination, pNumberFormated);
2760 strcat (pDestination, " ");
2761 strcat (pDestination, sCurrencySymbol);
2762 strcat (pDestination, "-");
2763 break;
2764 case 11: /* format: $ 1.1- */
2765 strcpy (pDestination, sCurrencySymbol);
2766 strcat (pDestination, " ");
2767 strcat (pDestination, pNumberFormated);
2768 strcat (pDestination, "-");
2769 break;
2770 case 12: /* format: $ -1.1 */
2771 strcpy (pDestination, sCurrencySymbol);
2772 strcat (pDestination, " ");
2773 strcat (pDestination, "-");
2774 strcat (pDestination, pNumberFormated);
2775 break;
2776 case 13: /* format: 1.1- $ */
2777 strcpy (pDestination, pNumberFormated);
2778 strcat (pDestination, "-");
2779 strcat (pDestination, " ");
2780 strcat (pDestination, sCurrencySymbol);
2781 break;
2782 case 14: /* format: ($ 1.1) */
2783 strcpy (pDestination, "(");
2784 strcat (pDestination, sCurrencySymbol);
2785 strcat (pDestination, " ");
2786 strcat (pDestination, pNumberFormated);
2787 strcat (pDestination, ")");
2788 break;
2789 case 15: /* format: (1.1 $) */
2790 strcpy (pDestination, "(");
2791 strcat (pDestination, pNumberFormated);
2792 strcat (pDestination, " ");
2793 strcat (pDestination, sCurrencySymbol);
2794 strcat (pDestination, ")");
2795 break;
2796 default:
2797 SetLastError(ERROR_INVALID_PARAMETER);
2798 return 0;
2802 retVal = strlen(pDestination) + 1;
2804 if (cchCurrency)
2806 memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
2807 if (cchCurrency < retVal) {
2808 retVal = 0;
2809 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2812 return retVal;
2815 /**************************************************************************
2816 * GetCurrencyFormatW (KERNEL32.@)
2818 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
2819 LPCWSTR lpvalue, const CURRENCYFMTW * lpFormat,
2820 LPWSTR lpCurrencyStr, int cchCurrency)
2822 FIXME("This API function is NOT implemented !\n");
2823 return 0;
2826 /******************************************************************************
2827 * OLE2NLS_CheckLocale [intern]
2829 static LCID OLE2NLS_CheckLocale (LCID locale)
2831 if (!locale)
2832 { locale = LOCALE_SYSTEM_DEFAULT;
2835 if (locale == LOCALE_SYSTEM_DEFAULT)
2836 { return GetSystemDefaultLCID();
2838 else if (locale == LOCALE_USER_DEFAULT)
2839 { return GetUserDefaultLCID();
2841 else
2842 { return locale;
2845 /******************************************************************************
2846 * GetTimeFormatA [KERNEL32.@]
2847 * Makes an ASCII string of the time
2849 * Formats date according to format, or locale default if format is
2850 * NULL. The format consists of literal characters and fields as follows:
2852 * h hours with no leading zero (12-hour)
2853 * hh hours with full two digits
2854 * H hours with no leading zero (24-hour)
2855 * HH hours with full two digits
2856 * m minutes with no leading zero
2857 * mm minutes with full two digits
2858 * s seconds with no leading zero
2859 * ss seconds with full two digits
2860 * t time marker (A or P)
2861 * tt time marker (AM, PM)
2864 INT WINAPI
2865 GetTimeFormatA(LCID locale, /* [in] */
2866 DWORD flags, /* [in] */
2867 const SYSTEMTIME* xtime, /* [in] */
2868 LPCSTR format, /* [in] */
2869 LPSTR timestr, /* [out] */
2870 INT timelen /* [in] */)
2872 INT ret;
2873 LPWSTR wformat = NULL;
2874 LPWSTR wtime = NULL;
2876 if (format)
2878 wformat = HeapAlloc(GetProcessHeap(), 0,
2879 (strlen(format) + 1) * sizeof(wchar_t));
2880 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
2883 if (timestr && timelen)
2885 wtime = HeapAlloc(GetProcessHeap(), 0,
2886 (timelen + 1) * sizeof(wchar_t));
2889 ret = GetTimeFormatW(locale, flags, xtime, wformat, wtime, timelen);
2891 if (wtime)
2893 WideCharToMultiByte(CP_ACP, 0, wtime, ret, timestr, timelen, NULL, NULL);
2894 HeapFree(GetProcessHeap(), 0, wtime);
2897 if (wformat)
2899 HeapFree(GetProcessHeap(), 0, wformat);
2902 return ret;
2906 /******************************************************************************
2907 * GetTimeFormatW [KERNEL32.@]
2908 * Makes a Unicode string of the time
2910 * NOTE: See OLE_GetFormatW() for further documentation
2912 INT WINAPI
2913 GetTimeFormatW(LCID locale, /* [in] */
2914 DWORD flags, /* [in] */
2915 const SYSTEMTIME* xtime, /* [in] */
2916 LPCWSTR format, /* [in] */
2917 LPWSTR timestr, /* [out] */
2918 INT timelen /* [in] */)
2919 { WCHAR format_buf[40];
2920 LPCWSTR thisformat;
2921 SYSTEMTIME t;
2922 const SYSTEMTIME* thistime;
2923 LCID thislocale=0;
2924 DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
2925 INT ret;
2927 TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
2928 xtime,debugstr_w(format),timestr,timelen);
2930 thislocale = OLE2NLS_CheckLocale ( locale );
2932 /* if the user didn't specify a format we use the default */
2933 /* format for this locale */
2934 if (format == NULL)
2936 if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */
2938 thislocale = GetSystemDefaultLCID();
2940 GetLocaleInfoW(thislocale, thisflags, format_buf, 40);
2941 thisformat = format_buf;
2943 else
2945 /* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
2946 /* NOTE: this could be either invalid flags or invalid parameter */
2947 /* windows sets it to invalid flags */
2948 if (flags & LOCALE_NOUSEROVERRIDE)
2950 SetLastError(ERROR_INVALID_FLAGS);
2951 return 0;
2954 thisformat = format;
2957 if (xtime == NULL) /* NULL means use the current local time */
2958 { GetLocalTime(&t);
2959 thistime = &t;
2961 else
2963 /* check time values */
2964 if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
2966 SetLastError(ERROR_INVALID_PARAMETER);
2967 return 0;
2970 thistime = xtime;
2973 ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat,
2974 timestr, timelen, 0);
2975 return ret;
2978 /******************************************************************************
2979 * EnumCalendarInfoA [KERNEL32.@]
2981 BOOL WINAPI EnumCalendarInfoA(
2982 CALINFO_ENUMPROCA calinfoproc,LCID locale,CALID calendar,CALTYPE caltype
2984 FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
2985 return FALSE;