Updated rules for API doc generation.
[wine/hacks.git] / ole / ole2nls.c
blobf921f07db33164400938501874fc1045998eee25
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->wMonth > 12))
1794 SetLastError(ERROR_INVALID_PARAMETER);
1795 return 0;
1797 /* For all we know the day of week and the time may be absolute
1798 * rubbish. Therefore if we are going to use conversion through
1799 * FileTime we had better use a clean time (and hopefully we won't
1800 * fall over any timezone complications).
1801 * If we go with an alternative method of correcting the day of week
1802 * (e.g. Zeller's congruence) then we won't need to, but we will need
1803 * to check the date.
1805 memset (&t, 0, sizeof(t));
1806 t.wYear = xtime->wYear;
1807 t.wMonth = xtime->wMonth;
1808 t.wDay = xtime->wDay;
1810 /* Silently correct wDayOfWeek by transforming to FileTime and back again */
1811 res=SystemTimeToFileTime(&t,&ft);
1813 /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER on error */
1814 if(!res)
1816 SetLastError(ERROR_INVALID_PARAMETER);
1817 return 0;
1819 FileTimeToSystemTime(&ft,&t);
1822 thistime = &t;
1824 if (format == NULL)
1826 GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
1827 ? LOCALE_SLONGDATE
1828 : LOCALE_SSHORTDATE),
1829 format_buf, sizeof(format_buf)/sizeof(*format_buf));
1830 thisformat = format_buf;
1831 } else
1833 thisformat = format;
1837 ret = OLE_GetFormatW(thislocale, flags, 0, thistime, thisformat,
1838 date, datelen, 1);
1841 TRACE("GetDateFormatW() returning %d, with data=%s\n",
1842 ret, debugstr_w(date));
1843 return ret;
1846 /**************************************************************************
1847 * EnumDateFormatsA (KERNEL32.@)
1849 BOOL WINAPI EnumDateFormatsA(
1850 DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
1852 LCID Loc = GetUserDefaultLCID();
1853 if(!lpDateFmtEnumProc)
1855 SetLastError(ERROR_INVALID_PARAMETER);
1856 return FALSE;
1859 switch( Loc )
1862 case 0x00000407: /* (Loc,"de_DE") */
1864 switch(dwFlags)
1866 case DATE_SHORTDATE:
1867 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1868 if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1869 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1870 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1871 return TRUE;
1872 case DATE_LONGDATE:
1873 if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1874 if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1875 if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1876 return TRUE;
1877 default:
1878 FIXME("Unknown date format (%ld)\n", dwFlags);
1879 SetLastError(ERROR_INVALID_PARAMETER);
1880 return FALSE;
1884 case 0x0000040c: /* (Loc,"fr_FR") */
1886 switch(dwFlags)
1888 case DATE_SHORTDATE:
1889 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1890 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1891 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1892 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1893 return TRUE;
1894 case DATE_LONGDATE:
1895 if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
1896 if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
1897 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1898 return TRUE;
1899 default:
1900 FIXME("Unknown date format (%ld)\n", dwFlags);
1901 SetLastError(ERROR_INVALID_PARAMETER);
1902 return FALSE;
1906 case 0x00000c0c: /* (Loc,"fr_CA") */
1908 switch(dwFlags)
1910 case DATE_SHORTDATE:
1911 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1912 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1913 if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
1914 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1915 return TRUE;
1916 case DATE_LONGDATE:
1917 if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
1918 if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
1919 return TRUE;
1920 default:
1921 FIXME("Unknown date format (%ld)\n", dwFlags);
1922 SetLastError(ERROR_INVALID_PARAMETER);
1923 return FALSE;
1927 case 0x00000809: /* (Loc,"en_UK") */
1929 switch(dwFlags)
1931 case DATE_SHORTDATE:
1932 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1933 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1934 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1935 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1936 return TRUE;
1937 case DATE_LONGDATE:
1938 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1939 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1940 return TRUE;
1941 default:
1942 FIXME("Unknown date format (%ld)\n", dwFlags);
1943 SetLastError(ERROR_INVALID_PARAMETER);
1944 return FALSE;
1948 case 0x00000c09: /* (Loc,"en_AU") */
1950 switch(dwFlags)
1952 case DATE_SHORTDATE:
1953 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1954 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1955 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1956 return TRUE;
1957 case DATE_LONGDATE:
1958 if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
1959 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1960 return TRUE;
1961 default:
1962 FIXME("Unknown date format (%ld)\n", dwFlags);
1963 SetLastError(ERROR_INVALID_PARAMETER);
1964 return FALSE;
1968 case 0x00001009: /* (Loc,"en_CA") */
1970 switch(dwFlags)
1972 case DATE_SHORTDATE:
1973 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1974 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1975 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1976 if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
1977 return TRUE;
1978 case DATE_LONGDATE:
1979 if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
1980 if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
1981 return TRUE;
1982 default:
1983 FIXME("Unknown date format (%ld)\n", dwFlags);
1984 SetLastError(ERROR_INVALID_PARAMETER);
1985 return FALSE;
1989 case 0x00001409: /* (Loc,"en_NZ") */
1991 switch(dwFlags)
1993 case DATE_SHORTDATE:
1994 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1995 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1996 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1997 return TRUE;
1998 case DATE_LONGDATE:
1999 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2000 if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
2001 return TRUE;
2002 default:
2003 FIXME("Unknown date format (%ld)\n", dwFlags);
2004 SetLastError(ERROR_INVALID_PARAMETER);
2005 return FALSE;
2009 case 0x00001809: /* (Loc,"en_IE") */
2011 switch(dwFlags)
2013 case DATE_SHORTDATE:
2014 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2015 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2016 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2017 return TRUE;
2018 case DATE_LONGDATE:
2019 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2020 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2021 return TRUE;
2022 default:
2023 FIXME("Unknown date format (%ld)\n", dwFlags);
2024 SetLastError(ERROR_INVALID_PARAMETER);
2025 return FALSE;
2029 case 0x00001c09: /* (Loc,"en_ZA") */
2031 switch(dwFlags)
2033 case DATE_SHORTDATE:
2034 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2035 return TRUE;
2036 case DATE_LONGDATE:
2037 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2038 return TRUE;
2039 default:
2040 FIXME("Unknown date format (%ld)\n", dwFlags);
2041 SetLastError(ERROR_INVALID_PARAMETER);
2042 return FALSE;
2046 case 0x00002009: /* (Loc,"en_JM") */
2048 switch(dwFlags)
2050 case DATE_SHORTDATE:
2051 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2052 return TRUE;
2053 case DATE_LONGDATE:
2054 if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
2055 if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
2056 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
2057 if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
2058 return TRUE;
2059 default:
2060 FIXME("Unknown date format (%ld)\n", dwFlags);
2061 SetLastError(ERROR_INVALID_PARAMETER);
2062 return FALSE;
2066 case 0x00002809: /* (Loc,"en_BZ") */
2067 case 0x00002c09: /* (Loc,"en_TT") */
2069 switch(dwFlags)
2071 case DATE_SHORTDATE:
2072 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2073 return TRUE;
2074 case DATE_LONGDATE:
2075 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
2076 return TRUE;
2077 default:
2078 FIXME("Unknown date format (%ld)\n", dwFlags);
2079 SetLastError(ERROR_INVALID_PARAMETER);
2080 return FALSE;
2084 default: /* default to US English "en_US" */
2086 switch(dwFlags)
2088 case DATE_SHORTDATE:
2089 if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
2090 if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
2091 if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
2092 if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
2093 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2094 if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
2095 return TRUE;
2096 case DATE_LONGDATE:
2097 if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
2098 if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
2099 if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
2100 if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
2101 return TRUE;
2102 default:
2103 FIXME("Unknown date format (%ld)\n", dwFlags);
2104 SetLastError(ERROR_INVALID_PARAMETER);
2105 return FALSE;
2111 /**************************************************************************
2112 * EnumDateFormatsW (KERNEL32.@)
2114 BOOL WINAPI EnumDateFormatsW(
2115 DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
2117 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
2118 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2119 return FALSE;
2122 /**************************************************************************
2123 * EnumTimeFormatsA (KERNEL32.@)
2125 BOOL WINAPI EnumTimeFormatsA(
2126 TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2128 LCID Loc = GetUserDefaultLCID();
2129 if(!lpTimeFmtEnumProc)
2131 SetLastError(ERROR_INVALID_PARAMETER);
2132 return FALSE;
2134 if(dwFlags)
2136 FIXME("Unknown time format (%ld)\n", dwFlags);
2139 switch( Loc )
2141 case 0x00000407: /* (Loc,"de_DE") */
2143 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2144 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2145 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2146 if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
2147 if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
2148 return TRUE;
2151 case 0x0000040c: /* (Loc,"fr_FR") */
2152 case 0x00000c0c: /* (Loc,"fr_CA") */
2154 if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
2155 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2156 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2157 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2158 if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
2159 return TRUE;
2162 case 0x00000809: /* (Loc,"en_UK") */
2163 case 0x00000c09: /* (Loc,"en_AU") */
2164 case 0x00001409: /* (Loc,"en_NZ") */
2165 case 0x00001809: /* (Loc,"en_IE") */
2167 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2168 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2169 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2170 return TRUE;
2173 case 0x00001c09: /* (Loc,"en_ZA") */
2174 case 0x00002809: /* (Loc,"en_BZ") */
2175 case 0x00002c09: /* (Loc,"en_TT") */
2177 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2178 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2179 return TRUE;
2182 default: /* default to US style "en_US" */
2184 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2185 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2186 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2187 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2188 return TRUE;
2193 /**************************************************************************
2194 * EnumTimeFormatsW (KERNEL32.@)
2196 BOOL WINAPI EnumTimeFormatsW(
2197 TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2199 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
2200 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2201 return FALSE;
2204 /**************************************************************************
2205 * This function is used just locally !
2206 * Description: Inverts a string.
2208 static void OLE_InvertString(char* string)
2210 char sTmpArray[128];
2211 INT counter, i = 0;
2213 for (counter = strlen(string); counter > 0; counter--)
2215 memcpy(sTmpArray + i, string + counter-1, 1);
2216 i++;
2218 memcpy(sTmpArray + i, "\0", 1);
2219 strcpy(string, sTmpArray);
2222 /***************************************************************************************
2223 * This function is used just locally !
2224 * Description: Test if the given string (psNumber) is valid or not.
2225 * The valid characters are the following:
2226 * - Characters '0' through '9'.
2227 * - One decimal point (dot) if the number is a floating-point value.
2228 * - A minus sign in the first character position if the number is
2229 * a negative value.
2230 * If the function succeeds, psBefore/psAfter will point to the string
2231 * on the right/left of the decimal symbol. pbNegative indicates if the
2232 * number is negative.
2234 static INT OLE_GetNumberComponents(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
2236 char sNumberSet[] = "0123456789";
2237 BOOL bInDecimal = FALSE;
2239 /* Test if we do have a minus sign */
2240 if ( *pInput == '-' )
2242 *pbNegative = TRUE;
2243 pInput++; /* Jump to the next character. */
2246 while(*pInput != '\0')
2248 /* Do we have a valid numeric character */
2249 if ( strchr(sNumberSet, *pInput) != NULL )
2251 if (bInDecimal == TRUE)
2252 *psAfter++ = *pInput;
2253 else
2254 *psBefore++ = *pInput;
2256 else
2258 /* Is this a decimal point (dot) */
2259 if ( *pInput == '.' )
2261 /* Is it the first time we find it */
2262 if ((bInDecimal == FALSE))
2263 bInDecimal = TRUE;
2264 else
2265 return -1; /* ERROR: Invalid parameter */
2267 else
2269 /* It's neither a numeric character, nor a decimal point.
2270 * Thus, return an error.
2272 return -1;
2275 pInput++;
2278 /* Add an End of Line character to the output buffers */
2279 *psBefore = '\0';
2280 *psAfter = '\0';
2282 return 0;
2285 /**************************************************************************
2286 * This function is used just locally !
2287 * Description: A number could be formatted using different numbers
2288 * of "digits in group" (example: 4;3;2;0).
2289 * The first parameter of this function is an array
2290 * containing the rule to be used. Its format is the following:
2291 * |NDG|DG1|DG2|...|0|
2292 * where NDG is the number of used "digits in group" and DG1, DG2,
2293 * are the corresponding "digits in group".
2294 * Thus, this function returns the grouping value in the array
2295 * pointed by the second parameter.
2297 static INT OLE_GetGrouping(char* sRule, INT index)
2299 char sData[2], sRuleSize[2];
2300 INT nData, nRuleSize;
2302 memcpy(sRuleSize, sRule, 1);
2303 memcpy(sRuleSize+1, "\0", 1);
2304 nRuleSize = atoi(sRuleSize);
2306 if (index > 0 && index < nRuleSize)
2308 memcpy(sData, sRule+index, 1);
2309 memcpy(sData+1, "\0", 1);
2310 nData = atoi(sData);
2313 else
2315 memcpy(sData, sRule+nRuleSize-1, 1);
2316 memcpy(sData+1, "\0", 1);
2317 nData = atoi(sData);
2320 return nData;
2323 /**************************************************************************
2324 * GetNumberFormatA (KERNEL32.@)
2326 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
2327 LPCSTR lpvalue, const NUMBERFMTA * lpFormat,
2328 LPSTR lpNumberStr, int cchNumber)
2330 char sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
2331 INT nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
2332 char sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
2333 char sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
2334 char *pStr = NULL, *pTmpStr = NULL;
2335 LCID systemDefaultLCID;
2336 BOOL bNegative = FALSE;
2337 enum Operations
2339 USE_PARAMETER,
2340 USE_LOCALEINFO,
2341 USE_SYSTEMDEFAULT,
2342 RETURN_ERROR
2343 } used_operation;
2345 strncpy(sNumber, lpvalue, 128);
2346 sNumber[127] = '\0';
2348 /* Make sure we have a valid input string, get the number
2349 * of digits before and after the decimal symbol, and check
2350 * if this is a negative number.
2352 if ( OLE_GetNumberComponents(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
2354 nNumberDecimal = strlen(sDigitsBeforeDecimal);
2355 nDigits = strlen(sDigitsAfterDecimal);
2357 else
2359 SetLastError(ERROR_INVALID_PARAMETER);
2360 return 0;
2363 /* Which source will we use to format the string */
2364 used_operation = RETURN_ERROR;
2365 if (lpFormat != NULL)
2367 if (dwflags == 0)
2368 used_operation = USE_PARAMETER;
2370 else
2372 if (dwflags & LOCALE_NOUSEROVERRIDE)
2373 used_operation = USE_SYSTEMDEFAULT;
2374 else
2375 used_operation = USE_LOCALEINFO;
2378 /* Load the fields we need */
2379 switch(used_operation)
2381 case USE_LOCALEINFO:
2382 GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2383 GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2384 GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2385 GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2386 GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2387 GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2388 break;
2389 case USE_PARAMETER:
2390 sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
2391 strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
2392 sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
2393 /* Win95-WinME only allow 0-9 for grouping, no matter what MSDN says. */
2394 strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
2395 sprintf(sILZero, "%d",lpFormat->LeadingZero);
2396 sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
2397 break;
2398 case USE_SYSTEMDEFAULT:
2399 systemDefaultLCID = GetSystemDefaultLCID();
2400 GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2401 GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2402 GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2403 GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2404 GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2405 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2406 break;
2407 default:
2408 SetLastError(ERROR_INVALID_PARAMETER);
2409 return 0;
2412 nNumberDigits = atoi(sNumberDigits);
2414 /* Remove the ";" */
2415 i=0;
2416 j = 1;
2417 for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
2419 if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
2421 memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
2422 i++;
2423 j++;
2426 sprintf(sBuffer, "%d", i);
2427 memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by OLE_GetGrouping() ) */
2428 memcpy(sRule + j, "\0", 1);
2430 /* First, format the digits before the decimal. */
2431 if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
2433 /* Working on an inverted string is easier ! */
2434 OLE_InvertString(sDigitsBeforeDecimal);
2436 nStep = nCounter = i = j = 0;
2437 nRuleIndex = 1;
2438 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2439 if (nGrouping == 0) /* If the first grouping is zero */
2440 nGrouping = nNumberDecimal; /* Don't do grouping */
2442 /* Here, we will loop until we reach the end of the string.
2443 * An internal counter (j) is used in order to know when to
2444 * insert the "digit group symbol".
2446 while (nNumberDecimal > 0)
2448 i = nCounter + nStep;
2449 memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
2450 nCounter++;
2451 j++;
2452 if (j >= nGrouping)
2454 j = 0;
2455 if (nRuleIndex < sRule[0])
2456 nRuleIndex++;
2457 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2458 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
2459 nStep+= strlen(sDigitGroupSymbol);
2462 nNumberDecimal--;
2465 memcpy(sDestination + i+1, "\0", 1);
2466 /* Get the string in the right order ! */
2467 OLE_InvertString(sDestination);
2469 else
2471 nLZ = atoi(sILZero);
2472 if (nLZ != 0)
2474 /* Use 0.xxx instead of .xxx */
2475 memcpy(sDestination, "0", 1);
2476 memcpy(sDestination+1, "\0", 1);
2478 else
2479 memcpy(sDestination, "\0", 1);
2483 /* Second, format the digits after the decimal. */
2484 j = 0;
2485 nCounter = nNumberDigits;
2486 if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
2488 i = strlen(sNumber) - strlen(pStr) + 1;
2489 strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
2490 j = strlen(sDigitsAfterDecimal);
2491 if (j < nNumberDigits)
2492 nCounter = nNumberDigits-j;
2494 for (i=0;i<nCounter;i++)
2495 memcpy(sDigitsAfterDecimal+i+j, "0", 1);
2496 memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
2498 i = strlen(sDestination);
2499 j = strlen(sDigitsAfterDecimal);
2500 /* Finally, construct the resulting formatted string. */
2502 for (nCounter=0; nCounter<i; nCounter++)
2503 memcpy(sNumber + nCounter, sDestination + nCounter, 1);
2505 memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
2507 for (i=0; i<j; i++)
2508 memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
2509 memcpy(sNumber + nCounter+i+ (i ? strlen(sDecimalSymbol) : 0), "\0", 1);
2511 /* Is it a negative number */
2512 if (bNegative == TRUE)
2514 i = atoi(sNegNumber);
2515 pStr = sDestination;
2516 pTmpStr = sNumber;
2517 switch (i)
2519 case 0:
2520 *pStr++ = '(';
2521 while (*sNumber != '\0')
2522 *pStr++ = *pTmpStr++;
2523 *pStr++ = ')';
2524 break;
2525 case 1:
2526 *pStr++ = '-';
2527 while (*pTmpStr != '\0')
2528 *pStr++ = *pTmpStr++;
2529 break;
2530 case 2:
2531 *pStr++ = '-';
2532 *pStr++ = ' ';
2533 while (*pTmpStr != '\0')
2534 *pStr++ = *pTmpStr++;
2535 break;
2536 case 3:
2537 while (*pTmpStr != '\0')
2538 *pStr++ = *pTmpStr++;
2539 *pStr++ = '-';
2540 break;
2541 case 4:
2542 while (*pTmpStr != '\0')
2543 *pStr++ = *pTmpStr++;
2544 *pStr++ = ' ';
2545 *pStr++ = '-';
2546 break;
2547 default:
2548 while (*pTmpStr != '\0')
2549 *pStr++ = *pTmpStr++;
2550 break;
2553 else
2554 strcpy(sDestination, sNumber);
2556 /* If cchNumber is zero, then returns the number of bytes or characters
2557 * required to hold the formatted number string
2559 retVal = strlen(sDestination) + 1;
2560 if (cchNumber!=0)
2562 memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
2563 if (cchNumber < retVal) {
2564 retVal = 0;
2565 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2568 return retVal;
2571 /**************************************************************************
2572 * GetNumberFormatW (KERNEL32.@)
2574 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
2575 LPCWSTR lpvalue, const NUMBERFMTW * lpFormat,
2576 LPWSTR lpNumberStr, int cchNumber)
2578 FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
2580 lstrcpynW( lpNumberStr, lpvalue, cchNumber );
2581 return cchNumber? strlenW( lpNumberStr ) : 0;
2584 /**************************************************************************
2585 * GetCurrencyFormatA (KERNEL32.@)
2587 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
2588 LPCSTR lpvalue, const CURRENCYFMTA * lpFormat,
2589 LPSTR lpCurrencyStr, int cchCurrency)
2591 UINT nPosOrder, nNegOrder;
2592 INT retVal;
2593 char sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
2594 char *pDestination = sDestination;
2595 char sNumberFormated[128];
2596 char *pNumberFormated = sNumberFormated;
2597 LCID systemDefaultLCID;
2598 BOOL bIsPositive = FALSE, bValidFormat = FALSE;
2599 enum Operations
2601 USE_PARAMETER,
2602 USE_LOCALEINFO,
2603 USE_SYSTEMDEFAULT,
2604 RETURN_ERROR
2605 } used_operation;
2607 NUMBERFMTA numberFmt;
2609 /* Which source will we use to format the string */
2610 used_operation = RETURN_ERROR;
2611 if (lpFormat != NULL)
2613 if (dwflags == 0)
2614 used_operation = USE_PARAMETER;
2616 else
2618 if (dwflags & LOCALE_NOUSEROVERRIDE)
2619 used_operation = USE_SYSTEMDEFAULT;
2620 else
2621 used_operation = USE_LOCALEINFO;
2624 /* Load the fields we need */
2625 switch(used_operation)
2627 case USE_LOCALEINFO:
2628 /* Specific to CURRENCYFMTA */
2629 GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2630 GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2631 GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2633 nPosOrder = atoi(sPosOrder);
2634 nNegOrder = atoi(sNegOrder);
2635 break;
2636 case USE_PARAMETER:
2637 /* Specific to CURRENCYFMTA */
2638 nNegOrder = lpFormat->NegativeOrder;
2639 nPosOrder = lpFormat->PositiveOrder;
2640 strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
2641 break;
2642 case USE_SYSTEMDEFAULT:
2643 systemDefaultLCID = GetSystemDefaultLCID();
2644 /* Specific to CURRENCYFMTA */
2645 GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2646 GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2647 GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2649 nPosOrder = atoi(sPosOrder);
2650 nNegOrder = atoi(sNegOrder);
2651 break;
2652 default:
2653 SetLastError(ERROR_INVALID_PARAMETER);
2654 return 0;
2657 /* Construct a temporary number format structure */
2658 if (lpFormat != NULL)
2660 numberFmt.NumDigits = lpFormat->NumDigits;
2661 numberFmt.LeadingZero = lpFormat->LeadingZero;
2662 numberFmt.Grouping = lpFormat->Grouping;
2663 numberFmt.NegativeOrder = 0;
2664 numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
2665 numberFmt.lpThousandSep = lpFormat->lpThousandSep;
2666 bValidFormat = TRUE;
2669 /* Make a call to GetNumberFormatA() */
2670 if (*lpvalue == '-')
2672 bIsPositive = FALSE;
2673 retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2675 else
2677 bIsPositive = TRUE;
2678 retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2681 if (retVal == 0)
2682 return 0;
2684 /* construct the formatted string */
2685 if (bIsPositive)
2687 switch (nPosOrder)
2689 case 0: /* Prefix, no separation */
2690 strcpy (pDestination, sCurrencySymbol);
2691 strcat (pDestination, pNumberFormated);
2692 break;
2693 case 1: /* Suffix, no separation */
2694 strcpy (pDestination, pNumberFormated);
2695 strcat (pDestination, sCurrencySymbol);
2696 break;
2697 case 2: /* Prefix, 1 char separation */
2698 strcpy (pDestination, sCurrencySymbol);
2699 strcat (pDestination, " ");
2700 strcat (pDestination, pNumberFormated);
2701 break;
2702 case 3: /* Suffix, 1 char separation */
2703 strcpy (pDestination, pNumberFormated);
2704 strcat (pDestination, " ");
2705 strcat (pDestination, sCurrencySymbol);
2706 break;
2707 default:
2708 SetLastError(ERROR_INVALID_PARAMETER);
2709 return 0;
2712 else /* negative number */
2714 switch (nNegOrder)
2716 case 0: /* format: ($1.1) */
2717 strcpy (pDestination, "(");
2718 strcat (pDestination, sCurrencySymbol);
2719 strcat (pDestination, pNumberFormated);
2720 strcat (pDestination, ")");
2721 break;
2722 case 1: /* format: -$1.1 */
2723 strcpy (pDestination, "-");
2724 strcat (pDestination, sCurrencySymbol);
2725 strcat (pDestination, pNumberFormated);
2726 break;
2727 case 2: /* format: $-1.1 */
2728 strcpy (pDestination, sCurrencySymbol);
2729 strcat (pDestination, "-");
2730 strcat (pDestination, pNumberFormated);
2731 break;
2732 case 3: /* format: $1.1- */
2733 strcpy (pDestination, sCurrencySymbol);
2734 strcat (pDestination, pNumberFormated);
2735 strcat (pDestination, "-");
2736 break;
2737 case 4: /* format: (1.1$) */
2738 strcpy (pDestination, "(");
2739 strcat (pDestination, pNumberFormated);
2740 strcat (pDestination, sCurrencySymbol);
2741 strcat (pDestination, ")");
2742 break;
2743 case 5: /* format: -1.1$ */
2744 strcpy (pDestination, "-");
2745 strcat (pDestination, pNumberFormated);
2746 strcat (pDestination, sCurrencySymbol);
2747 break;
2748 case 6: /* format: 1.1-$ */
2749 strcpy (pDestination, pNumberFormated);
2750 strcat (pDestination, "-");
2751 strcat (pDestination, sCurrencySymbol);
2752 break;
2753 case 7: /* format: 1.1$- */
2754 strcpy (pDestination, pNumberFormated);
2755 strcat (pDestination, sCurrencySymbol);
2756 strcat (pDestination, "-");
2757 break;
2758 case 8: /* format: -1.1 $ */
2759 strcpy (pDestination, "-");
2760 strcat (pDestination, pNumberFormated);
2761 strcat (pDestination, " ");
2762 strcat (pDestination, sCurrencySymbol);
2763 break;
2764 case 9: /* format: -$ 1.1 */
2765 strcpy (pDestination, "-");
2766 strcat (pDestination, sCurrencySymbol);
2767 strcat (pDestination, " ");
2768 strcat (pDestination, pNumberFormated);
2769 break;
2770 case 10: /* format: 1.1 $- */
2771 strcpy (pDestination, pNumberFormated);
2772 strcat (pDestination, " ");
2773 strcat (pDestination, sCurrencySymbol);
2774 strcat (pDestination, "-");
2775 break;
2776 case 11: /* format: $ 1.1- */
2777 strcpy (pDestination, sCurrencySymbol);
2778 strcat (pDestination, " ");
2779 strcat (pDestination, pNumberFormated);
2780 strcat (pDestination, "-");
2781 break;
2782 case 12: /* format: $ -1.1 */
2783 strcpy (pDestination, sCurrencySymbol);
2784 strcat (pDestination, " ");
2785 strcat (pDestination, "-");
2786 strcat (pDestination, pNumberFormated);
2787 break;
2788 case 13: /* format: 1.1- $ */
2789 strcpy (pDestination, pNumberFormated);
2790 strcat (pDestination, "-");
2791 strcat (pDestination, " ");
2792 strcat (pDestination, sCurrencySymbol);
2793 break;
2794 case 14: /* format: ($ 1.1) */
2795 strcpy (pDestination, "(");
2796 strcat (pDestination, sCurrencySymbol);
2797 strcat (pDestination, " ");
2798 strcat (pDestination, pNumberFormated);
2799 strcat (pDestination, ")");
2800 break;
2801 case 15: /* format: (1.1 $) */
2802 strcpy (pDestination, "(");
2803 strcat (pDestination, pNumberFormated);
2804 strcat (pDestination, " ");
2805 strcat (pDestination, sCurrencySymbol);
2806 strcat (pDestination, ")");
2807 break;
2808 default:
2809 SetLastError(ERROR_INVALID_PARAMETER);
2810 return 0;
2814 retVal = strlen(pDestination) + 1;
2816 if (cchCurrency)
2818 memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
2819 if (cchCurrency < retVal) {
2820 retVal = 0;
2821 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2824 return retVal;
2827 /**************************************************************************
2828 * GetCurrencyFormatW (KERNEL32.@)
2830 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
2831 LPCWSTR lpvalue, const CURRENCYFMTW * lpFormat,
2832 LPWSTR lpCurrencyStr, int cchCurrency)
2834 FIXME("This API function is NOT implemented !\n");
2835 return 0;
2838 /******************************************************************************
2839 * OLE2NLS_CheckLocale [intern]
2841 static LCID OLE2NLS_CheckLocale (LCID locale)
2843 if (!locale)
2844 { locale = LOCALE_SYSTEM_DEFAULT;
2847 if (locale == LOCALE_SYSTEM_DEFAULT)
2848 { return GetSystemDefaultLCID();
2850 else if (locale == LOCALE_USER_DEFAULT)
2851 { return GetUserDefaultLCID();
2853 else
2854 { return locale;
2857 /******************************************************************************
2858 * GetTimeFormatA [KERNEL32.@]
2859 * Makes an ASCII string of the time
2861 * Formats date according to format, or locale default if format is
2862 * NULL. The format consists of literal characters and fields as follows:
2864 * h hours with no leading zero (12-hour)
2865 * hh hours with full two digits
2866 * H hours with no leading zero (24-hour)
2867 * HH hours with full two digits
2868 * m minutes with no leading zero
2869 * mm minutes with full two digits
2870 * s seconds with no leading zero
2871 * ss seconds with full two digits
2872 * t time marker (A or P)
2873 * tt time marker (AM, PM)
2876 INT WINAPI
2877 GetTimeFormatA(LCID locale, /* [in] */
2878 DWORD flags, /* [in] */
2879 const SYSTEMTIME* xtime, /* [in] */
2880 LPCSTR format, /* [in] */
2881 LPSTR timestr, /* [out] */
2882 INT timelen /* [in] */)
2884 INT ret;
2885 LPWSTR wformat = NULL;
2886 LPWSTR wtime = NULL;
2888 if (format)
2890 wformat = HeapAlloc(GetProcessHeap(), 0,
2891 (strlen(format) + 1) * sizeof(wchar_t));
2892 MultiByteToWideChar(CP_ACP, 0, format, -1, wformat, strlen(format) + 1);
2895 if (timestr && timelen)
2897 wtime = HeapAlloc(GetProcessHeap(), 0,
2898 (timelen + 1) * sizeof(wchar_t));
2901 ret = GetTimeFormatW(locale, flags, xtime, wformat, wtime, timelen);
2903 if (wtime)
2905 WideCharToMultiByte(CP_ACP, 0, wtime, ret, timestr, timelen, NULL, NULL);
2906 HeapFree(GetProcessHeap(), 0, wtime);
2909 if (wformat)
2911 HeapFree(GetProcessHeap(), 0, wformat);
2914 return ret;
2918 /******************************************************************************
2919 * GetTimeFormatW [KERNEL32.@]
2920 * Makes a Unicode string of the time
2922 * NOTE: See OLE_GetFormatW() for further documentation
2924 INT WINAPI
2925 GetTimeFormatW(LCID locale, /* [in] */
2926 DWORD flags, /* [in] */
2927 const SYSTEMTIME* xtime, /* [in] */
2928 LPCWSTR format, /* [in] */
2929 LPWSTR timestr, /* [out] */
2930 INT timelen /* [in] */)
2931 { WCHAR format_buf[40];
2932 LPCWSTR thisformat;
2933 SYSTEMTIME t;
2934 const SYSTEMTIME* thistime;
2935 LCID thislocale=0;
2936 DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
2937 INT ret;
2939 TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
2940 xtime,debugstr_w(format),timestr,timelen);
2942 thislocale = OLE2NLS_CheckLocale ( locale );
2944 /* if the user didn't specify a format we use the default */
2945 /* format for this locale */
2946 if (format == NULL)
2948 if (flags & LOCALE_NOUSEROVERRIDE) /* use system default */
2950 thislocale = GetSystemDefaultLCID();
2952 GetLocaleInfoW(thislocale, thisflags, format_buf, 40);
2953 thisformat = format_buf;
2955 else
2957 /* if non-null format and LOCALE_NOUSEROVERRIDE then fail */
2958 /* NOTE: this could be either invalid flags or invalid parameter */
2959 /* windows sets it to invalid flags */
2960 if (flags & LOCALE_NOUSEROVERRIDE)
2962 SetLastError(ERROR_INVALID_FLAGS);
2963 return 0;
2966 thisformat = format;
2969 if (xtime == NULL) /* NULL means use the current local time */
2970 { GetLocalTime(&t);
2971 thistime = &t;
2973 else
2975 /* check time values */
2976 if((xtime->wHour > 24) || (xtime->wMinute >= 60) || (xtime->wSecond >= 60))
2978 SetLastError(ERROR_INVALID_PARAMETER);
2979 return 0;
2982 thistime = xtime;
2985 ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat,
2986 timestr, timelen, 0);
2987 return ret;
2990 /******************************************************************************
2991 * EnumCalendarInfoA [KERNEL32.@]
2993 BOOL WINAPI EnumCalendarInfoA(
2994 CALINFO_ENUMPROCA calinfoproc,LCID locale,CALID calendar,CALTYPE caltype
2996 FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
2997 return FALSE;