Optimize invalidation on insert.
[wine] / ole / ole2nls.c
1 /*
2  *      National Language Support library
3  *
4  *      Copyright 1995  Martin von Loewis
5  *      Copyright 1998  David Lee Lambert
6  *      Copyright 2000  Julio César Gázquez
7  *
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.
12  *
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.
17  *
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
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <locale.h>
31
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"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(nls);
45
46
47 /******************************************************************************
48  *              ConvertDefaultLocale (KERNEL32.@)
49  */
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);
58         }
59         return MAKELANGID( PRIMARYLANGID(lcid), SUBLANG_NEUTRAL);
60 }
61
62
63 /******************************************************************************
64  *              IsValidLocale   [KERNEL32.@]
65  */
66 BOOL WINAPI IsValidLocale(LCID lcid,DWORD flags)
67 {
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;
73 }
74
75 static BOOL CALLBACK EnumResourceLanguagesProcW(HMODULE hModule, LPCWSTR type,
76                 LPCWSTR name, WORD LangID, LONG lParam)
77 {
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);
84 }
85
86 /******************************************************************************
87  *              EnumSystemLocalesW      [KERNEL32.@]
88  */
89 BOOL WINAPI EnumSystemLocalesW( LOCALE_ENUMPROCW lpfnLocaleEnum,
90                                     DWORD flags )
91 {
92     TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
93
94     EnumResourceLanguagesW(GetModuleHandleA("KERNEL32"), RT_STRINGW,
95             (LPCWSTR)LOCALE_ILANGUAGE, EnumResourceLanguagesProcW,
96             (LONG)lpfnLocaleEnum);
97
98     return TRUE;
99 }
100
101 static BOOL CALLBACK EnumResourceLanguagesProcA(HMODULE hModule, LPCSTR type,
102                 LPCSTR name, WORD LangID, LONG lParam)
103 {
104     CHAR bufA[20];
105     LOCALE_ENUMPROCA lpfnLocaleEnum = (LOCALE_ENUMPROCA)lParam;
106     sprintf(bufA, "%08x", (UINT)LangID);
107     return lpfnLocaleEnum(bufA);
108 }
109
110 /******************************************************************************
111  *              EnumSystemLocalesA      [KERNEL32.@]
112  */
113 BOOL WINAPI EnumSystemLocalesA(LOCALE_ENUMPROCA lpfnLocaleEnum,
114                                    DWORD flags)
115 {
116     TRACE("(%p,%08lx)\n", lpfnLocaleEnum,flags);
117
118     EnumResourceLanguagesA(GetModuleHandleA("KERNEL32"), RT_STRINGA,
119             (LPCSTR)LOCALE_ILANGUAGE, EnumResourceLanguagesProcA,
120             (LONG)lpfnLocaleEnum);
121
122     return TRUE;
123 }
124
125 /***********************************************************************
126  *           VerLanguageNameA              [KERNEL32.@]
127  */
128 DWORD WINAPI VerLanguageNameA( UINT wLang, LPSTR szLang, UINT nSize )
129 {
130     if(!szLang)
131         return 0;
132
133     return GetLocaleInfoA(MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize);
134 }
135
136 /***********************************************************************
137  *           VerLanguageNameW              [KERNEL32.@]
138  */
139 DWORD WINAPI VerLanguageNameW( UINT wLang, LPWSTR szLang, UINT nSize )
140 {
141     if(!szLang)
142         return 0;
143
144     return GetLocaleInfoW(MAKELCID(wLang, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLang, nSize);
145 }
146
147
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, /* \7f - 127 */
276   6      ,  30, /* \80 - 128 */
277   6      ,  31, /* \81 - 129 */
278   7      , 123, /* \82 - 130 */
279  14      ,  35, /* \83 - 131 */
280   7      , 127, /* \84 - 132 */
281  10      ,  21, /* \85 - 133 */
282  10      ,  15, /* \86 - 134 */
283  10      ,  16, /* \87 - 135 */
284   7      ,  67, /* \88 - 136 */
285  10      ,  22, /* \89 - 137 */
286  14      , 145, /* \8a - 138 */
287   7      , 136, /* \8b - 139 */
288  14 + 16 , 124, /* \8c - 140 */
289   6      ,  43, /* \8d - 141 */
290   6      ,  44, /* \8e - 142 */
291   6      ,  45, /* \8f - 143 */
292   6      ,  46, /* \90 - 144 */
293   7      , 121, /* \91 - 145 */
294   7      , 122, /* \92 - 146 */
295   7      , 125, /* \93 - 147 */
296   7      , 126, /* \94 - 148 */
297  10      ,  17, /* \95 - 149 */
298   6      , 137, /* \96 - 150 */
299   6      , 139, /* \97 - 151 */
300   7      ,  93, /* \98 - 152 */
301  14      , 156, /* \99 - 153 */
302  14      , 145, /* \9a - 154 */
303   7      , 137, /* \9b - 155 */
304  14 + 16 , 124, /* \9c - 156 */
305   6      ,  59, /* \9d - 157 */
306   6      ,  60, /* \9e - 158 */
307  14      , 167, /* \9f - 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 */ };
404
405 static const unsigned char LCM_Unicode_LUT_2[] = { 33, 44, 145 };
406
407 #define LCM_Diacritic_Start 131
408
409 static const unsigned char LCM_Diacritic_LUT[] = {
410 123,  /* \83 - 131 */
411   2,  /* \84 - 132 */
412   2,  /* \85 - 133 */
413   2,  /* \86 - 134 */
414   2,  /* \87 - 135 */
415   3,  /* \88 - 136 */
416   2,  /* \89 - 137 */
417  20,  /* \8a - 138 */
418   2,  /* \8b - 139 */
419   2,  /* \8c - 140 */
420   2,  /* \8d - 141 */
421   2,  /* \8e - 142 */
422   2,  /* \8f - 143 */
423   2,  /* \90 - 144 */
424   2,  /* \91 - 145 */
425   2,  /* \92 - 146 */
426   2,  /* \93 - 147 */
427   2,  /* \94 - 148 */
428   2,  /* \95 - 149 */
429   2,  /* \96 - 150 */
430   2,  /* \97 - 151 */
431   2,  /* \98 - 152 */
432   2,  /* \99 - 153 */
433  20,  /* \9a - 154 */
434   2,  /* \9b - 155 */
435   2,  /* \9c - 156 */
436   2,  /* \9d - 157 */
437   2,  /* \9e - 158 */
438  19,  /* \9f - 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 */
535 } ;
536
537 /******************************************************************************
538  * OLE2NLS_isPunctuation [INTERNAL]
539  */
540 static int OLE2NLS_isPunctuation(unsigned char c)
541 {
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". */
546
547   return (LCM_Unicode_LUT[-2+2*c]==6);
548 }
549
550 /******************************************************************************
551  * OLE2NLS_isNonSpacing [INTERNAL]
552  */
553 static int OLE2NLS_isNonSpacing(unsigned char c)
554 {
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));
559 }
560
561 /******************************************************************************
562  * OLE2NLS_isSymbol [INTERNAL]
563  * FIXME: handle current locale
564  */
565 static int OLE2NLS_isSymbol(unsigned char c)
566 {
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)) );
571 }
572
573 /******************************************************************************
574  *              identity        [Internal]
575  */
576 static int identity(int c)
577 {
578   return c;
579 }
580
581 /*************************************************************************
582  *              LCMapStringA                [KERNEL32.@]
583  *
584  * Convert a string, or generate a sort key from it.
585  *
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,...
589  *
590  * RETURNS
591  *    Error : 0.
592  *    Success : length of the result string.
593  *
594  * NOTES
595  *    If called with scrlen = -1, the function will compute the length
596  *      of the 0-terminated string strsrc by itself.
597  *
598  *    If called with dstlen = 0, returns the buffer length that
599  *      would be required.
600  *
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.
606  */
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 */
616 {
617   int i;
618
619   TRACE("(0x%04lx,0x%08lx,%s,%d,%p,%d)\n",
620         lcid,mapflags,debugstr_an(srcstr,srclen),srclen,dststr,dstlen);
621
622   if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
623   {
624     ERR("(src=%s,dest=%s): Invalid NULL string\n",
625         debugstr_an(srcstr,srclen), dststr);
626     SetLastError(ERROR_INVALID_PARAMETER);
627     return 0;
628   }
629   if (srclen == -1)
630     srclen = strlen(srcstr) + 1 ;    /* (include final '\0') */
631
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
642    */
643   if (mapflags & ~LCMAPSTRINGA_SUPPORTED_FLAGS)
644   {
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
654      );
655   }
656
657   if ( !(mapflags & LCMAP_SORTKEY) )
658   {
659     int i,j;
660     int (*f)(int) = identity;
661     int flag_ignorenonspace = mapflags & NORM_IGNORENONSPACE;
662     int flag_ignoresymbols = mapflags & NORM_IGNORESYMBOLS;
663
664     if (flag_ignorenonspace || flag_ignoresymbols)
665     {
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)
671       {
672         /* Compute required length */
673         for (i=j=0; i < srclen; i++)
674         {
675           if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
676                && !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
677             j++;
678         }
679         return j;
680       }
681     }
682     else
683     {
684       if (dstlen==0)
685         return srclen;
686       if (dstlen<srclen)
687            {
688              SetLastError(ERROR_INSUFFICIENT_BUFFER);
689              return 0;
690            }
691     }
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++)
698     {
699       if ( !(flag_ignorenonspace && OLE2NLS_isNonSpacing(srcstr[i]))
700            && !(flag_ignoresymbols && OLE2NLS_isSymbol(srcstr[i])) )
701       {
702         dststr[j] = (CHAR) f(srcstr[i]);
703         j++;
704       }
705     }
706     return j;
707   }
708
709   /* FIXME: This function completely ignores the "lcid" parameter. */
710   /* else ... (mapflags & LCMAP_SORTKEY)  */
711   {
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;
721
722     /* compute how much room we will need */
723     for (i=0;i<srclen;i++)
724     {
725       int ofs;
726       unsigned char source_char = srcstr[i];
727       if (source_char!='\0')
728       {
729         if (flag_stringsort || !OLE2NLS_isPunctuation(source_char))
730         {
731           unicode_len++;
732           if ( LCM_Unicode_LUT[-2+2*source_char] & ~15 )
733             unicode_len++;             /* double letter */
734         }
735         else
736         {
737           delayed_punctuation_len++;
738         }
739       }
740
741       if (isupper(source_char))
742         case_len=unicode_len;
743
744       ofs = source_char - LCM_Diacritic_Start;
745       if ((ofs>=0) && (LCM_Diacritic_LUT[ofs]!=2))
746         diacritic_len=unicode_len;
747     }
748
749     if (mapflags & NORM_IGNORECASE)
750       case_len=0;
751     if (mapflags & NORM_IGNORENONSPACE)
752       diacritic_len=0;
753
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)
763     {
764       SetLastError(ERROR_INSUFFICIENT_BUFFER);
765       return 0;
766     }
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;
773     }
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';
783
784     /* read source string char by char, write
785        corresponding weight in each component. */
786     for (i=0,count=0;i<srclen;i++)
787     {
788       unsigned char source_char=srcstr[i];
789       if (source_char!='\0')
790       {
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))
796         {
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 */
802
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 */
807         }
808         else
809         {
810           dststr[2*count] = type;
811           dststr[2*count+1] = LCM_Unicode_LUT[-1+2*source_char];
812           if (longcode)
813           {
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++;
820
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 */
826           }
827
828           if (count<case_len)
829             case_component[count] = ( isupper(source_char) ? 18 : 2 ) ;
830           if (count<diacritic_len)
831           {
832             int ofs = source_char - LCM_Diacritic_Start;
833             diacritic_component[count] = (ofs>=0 ? LCM_Diacritic_LUT[ofs] : 2);
834           }
835           count++;
836         }
837       }
838     }
839     dststr[room-1] = '\0';
840     return room;
841   }
842 }
843
844 /*************************************************************************
845  *              LCMapStringW                [KERNEL32.@]
846  *
847  * Convert a string, or generate a sort key from it.
848  *
849  * NOTE
850  *
851  * See LCMapStringA for documentation
852  */
853 INT WINAPI LCMapStringW(
854         LCID lcid,DWORD mapflags,LPCWSTR srcstr,INT srclen,LPWSTR dststr,
855         INT dstlen)
856 {
857   int i;
858
859   TRACE("(0x%04lx,0x%08lx,%p,%d,%p,%d)\n",
860                  lcid, mapflags, srcstr, srclen, dststr, dstlen);
861
862   if ( ((dstlen!=0) && (dststr==NULL)) || (srcstr==NULL) )
863   {
864     ERR("(src=%p,dst=%p): Invalid NULL string\n", srcstr, dststr);
865     SetLastError(ERROR_INVALID_PARAMETER);
866     return 0;
867   }
868   if (srclen==-1)
869     srclen = strlenW(srcstr)+1;
870
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)
876   {
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 */
880
881       /* General purpose index into strings of any type. */
882       int str_idx = 0;
883
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;
891
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;
896
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;
901
902       if(!(srcstr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
903                                        srclen * sizeof(wchar_t))))
904       {
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;
909       }
910
911       /* Convert source string to a libc Unicode string. */
912       for(str_idx = 0; str_idx < srclen; str_idx++)
913       {
914           srcstr_libc[str_idx] = srcstr[str_idx];
915       }
916
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)))
922       {
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;
927       }
928
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.
932        *
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.
938        *
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)))
943       {
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;
949       }
950
951       if(!(lc_collate_env = setlocale(LC_COLLATE, "")))
952       {
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;
961       }
962
963       TRACE("lc_collate_default = %s\n", lc_collate_default);
964       TRACE("lc_collate_env = %s\n", lc_collate_env);
965
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)
970       {
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;
978       }
979       else if(returned_len > src_native_len)
980       {
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);
985
986           /* Since this is an internal error I'm not sure what the correct
987            * error code is.  */
988           SetLastError(ERROR_NOT_ENOUGH_MEMORY);
989
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;
994       }
995       src_native_len = returned_len;
996
997       TRACE("src_native = %s  src_native_len = %d\n",
998              src_native, src_native_len);
999
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)))
1005       {
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;
1012       }
1013
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).
1019        *
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;
1024
1025       if(returned_len > dst_native_len)
1026       {
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);
1031
1032           /* Since this is an internal error I'm not sure what the correct
1033            * error code is.  */
1034           SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1035
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;
1041       }
1042       dst_native_len = returned_len;
1043
1044       TRACE("dst_native = %s  dst_native_len = %d\n",
1045              dst_native, dst_native_len);
1046
1047       dststr_libc_len = dst_native_len;
1048       if(!(dststr_libc = (wchar_t *)HeapAlloc(GetProcessHeap(), 0,
1049                                        dststr_libc_len * sizeof(wchar_t))))
1050       {
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;
1059       }
1060
1061       /* Convert the native multibyte string to a libc Unicode string. */
1062       returned_len = mbstowcs(dststr_libc, dst_native, dst_native_len) + 1;
1063
1064       /* Restore LC_COLLATE now that the last LC_COLLATE sensitive
1065        * function has returned. */
1066       setlocale(LC_COLLATE, lc_collate_default);
1067
1068       if(returned_len == 0)
1069       {
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;
1078       }
1079       if(dstlen)
1080       {
1081           if(returned_len > dstlen)
1082           {
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;
1092           }
1093           dstlen = returned_len;
1094
1095           /* Convert a libc Unicode string to the destination string. */
1096           for(str_idx = 0; str_idx < dstlen; str_idx++)
1097           {
1098               dststr[str_idx] = dststr_libc[str_idx];
1099           }
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));
1105       }
1106       else
1107       {
1108           dstlen = returned_len;
1109       }
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;
1116   }
1117   else
1118   {
1119     int (*f)(int)=identity;
1120
1121     if (dstlen==0)
1122         return srclen;
1123     if (dstlen<srclen)
1124     {
1125         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1126         return 0;
1127     }
1128
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;
1136   }
1137 }
1138
1139
1140 /***********************************************************************
1141  *           OLE2NLS_EstimateMappingLength
1142  *
1143  * Estimates the number of characters required to hold the string
1144  * computed by LCMapStringA.
1145  *
1146  * The size is always over-estimated, with a fixed limit on the
1147  * amount of estimation error.
1148  *
1149  * Note that len == -1 is not permitted.
1150  */
1151 static inline int OLE2NLS_EstimateMappingLength(LCID lcid, DWORD dwMapFlags,
1152                                                 LPCSTR str, DWORD len)
1153 {
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);
1158 }
1159
1160 /******************************************************************************
1161  *              CompareStringA  [KERNEL32.@]
1162  * Compares two strings using locale
1163  *
1164  * RETURNS
1165  *
1166  * success: CSTR_LESS_THAN, CSTR_EQUAL, CSTR_GREATER_THAN
1167  * failure: 0
1168  *
1169  * NOTES
1170  *
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.
1174  *
1175  * BUGS
1176  *
1177  * This implementation ignores the locale
1178  *
1179  * FIXME
1180  *
1181  * Quite inefficient.
1182  */
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 */
1190 {
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));
1197
1198   if ( (s1==NULL) || (s2==NULL) )
1199   {
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;
1204   }
1205
1206   if(fdwStyle & NORM_IGNORESYMBOLS)
1207     FIXME("IGNORESYMBOLS not supported\n");
1208
1209   if (l1 == -1) l1 = strlen(s1);
1210   if (l2 == -1) l2 = strlen(s2);
1211
1212   mapstring_flags = LCMAP_SORTKEY | fdwStyle ;
1213   len1 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s1, l1);
1214   len2 = OLE2NLS_EstimateMappingLength(lcid, mapstring_flags, s2, l2);
1215
1216   if ((len1==0)||(len2==0))
1217     return 0;     /* something wrong happened */
1218
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)) )
1223   {
1224     ERR("Bug in LCmapStringA.\n");
1225     result = 0;
1226   }
1227   else
1228   {
1229     /* strcmp doesn't necessarily return -1, 0, or 1 */
1230     result = strcmp(sk1,sk2);
1231   }
1232   HeapFree(GetProcessHeap(),0,sk1);
1233
1234   if (result < 0)
1235     return 1;
1236   if (result == 0)
1237     return 2;
1238
1239   /* must be greater, if we reach this point */
1240   return 3;
1241 }
1242
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.
1248  */
1249 int WINAPI CompareStringW(LCID lcid, DWORD fdwStyle,
1250                           LPCWSTR s1, int l1, LPCWSTR s2, int l2)
1251 {
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");
1257
1258         /* Is strcmp defaulting to string sort or to word sort?? */
1259         /* FIXME: Handle NORM_STRINGSORT */
1260         l1 = (l1==-1)?strlenW(s1):l1;
1261         l2 = (l2==-1)?strlenW(s2):l2;
1262         len = l1<l2 ? l1:l2;
1263         ret = (fdwStyle & NORM_IGNORECASE) ? strncmpiW(s1,s2,len) : strncmpW(s1,s2,len);
1264         /* not equal, return 1 or 3 */
1265         if(ret!=0) {
1266                 /* need to translate result */
1267                 return ((int)ret < 0) ? 1 : 3;
1268         }
1269         /* same len, return 2 */
1270         if(l1==l2) return 2;
1271         /* the longer one is lexically greater */
1272         return (l1<l2)? 1 : 3;
1273 }
1274
1275 /***********************************************************************
1276  *           lstrcmp    (KERNEL32.@)
1277  *           lstrcmpA   (KERNEL32.@)
1278  */
1279 INT WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 )
1280 {
1281     return CompareStringA(LOCALE_SYSTEM_DEFAULT,0,str1,-1,str2,-1) - 2 ;
1282 }
1283
1284
1285 /***********************************************************************
1286  *           lstrcmpW   (KERNEL32.@)
1287  * FIXME : should call CompareStringW, when it is implemented.
1288  *    This implementation is not "word sort", as it should.
1289  */
1290 INT WINAPI lstrcmpW( LPCWSTR str1, LPCWSTR str2 )
1291 {
1292     TRACE("%s and %s\n",
1293                    debugstr_w (str1), debugstr_w (str2));
1294     if (!str1 || !str2) {
1295         SetLastError(ERROR_INVALID_PARAMETER);
1296         return 0;
1297     }
1298     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
1299     return (INT)(*str1 - *str2);
1300 }
1301
1302
1303 /***********************************************************************
1304  *           lstrcmpi    (KERNEL32.@)
1305  *           lstrcmpiA   (KERNEL32.@)
1306  */
1307 INT WINAPI lstrcmpiA( LPCSTR str1, LPCSTR str2 )
1308 {    TRACE("strcmpi %s and %s\n",
1309                    debugstr_a (str1), debugstr_a (str2));
1310     return CompareStringA(LOCALE_SYSTEM_DEFAULT,NORM_IGNORECASE,str1,-1,str2,-1)-2;
1311 }
1312
1313
1314 /***********************************************************************
1315  *           lstrcmpiW   (KERNEL32.@)
1316  */
1317 INT WINAPI lstrcmpiW( LPCWSTR str1, LPCWSTR str2 )
1318 {
1319     if (!str1 || !str2) {
1320         SetLastError(ERROR_INVALID_PARAMETER);
1321         return 0;
1322     }
1323     return strcmpiW( str1, str2 );
1324 }
1325
1326
1327 /******************************************************************************
1328  *              OLE_GetFormatA  [Internal]
1329  *
1330  * FIXME
1331  *    If datelen == 0, it should return the reguired string length.
1332  *
1333  This function implements stuff for GetDateFormat() and
1334  GetTimeFormat().
1335
1336   d    single-digit (no leading zero) day (of month)
1337   dd   two-digit day (of month)
1338   ddd  short day-of-week name
1339   dddd long day-of-week name
1340   M    single-digit month
1341   MM   two-digit month
1342   MMM  short month name
1343   MMMM full month name
1344   y    two-digit year, no leading 0
1345   yy   two-digit year
1346   yyyy four-digit year
1347   gg   era string
1348   h    hours with no leading zero (12-hour)
1349   hh   hours with full two digits
1350   H    hours with no leading zero (24-hour)
1351   HH   hours with full two digits
1352   m    minutes with no leading zero
1353   mm   minutes with full two digits
1354   s    seconds with no leading zero
1355   ss   seconds with full two digits
1356   t    time marker (A or P)
1357   tt   time marker (AM, PM)
1358   ''   used to quote literal characters
1359   ''   (within a quoted string) indicates a literal '
1360
1361  These functions REQUIRE valid locale, date,  and format.
1362  */
1363 static INT OLE_GetFormatA(LCID locale,
1364                             DWORD flags,
1365                             DWORD tflags,
1366                             const SYSTEMTIME* xtime,
1367                             LPCSTR _format,     /*in*/
1368                             LPSTR date,         /*out*/
1369                             INT datelen)
1370 {
1371    INT inpos, outpos;
1372    int count, type, inquote, Overflow;
1373    char buf[40];
1374    char format[40];
1375    char * pos;
1376    int buflen;
1377
1378    const char * _dgfmt[] = { "%d", "%02d" };
1379    const char ** dgfmt = _dgfmt - 1;
1380
1381    /* report, for debugging */
1382    TRACE("(0x%lx,0x%lx, 0x%lx, time(y=%d m=%d wd=%d d=%d,h=%d,m=%d,s=%d), fmt=%p \'%s\' , %p, len=%d)\n",
1383          locale, flags, tflags,
1384          xtime->wYear,xtime->wMonth,xtime->wDayOfWeek,xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
1385          _format, _format, date, datelen);
1386
1387    if(datelen == 0) {
1388      FIXME("datelen = 0, returning 255\n");
1389      return 255;
1390    }
1391
1392    /* initalize state variables and output buffer */
1393    inpos = outpos = 0;
1394    count = 0; inquote = 0; Overflow = 0;
1395    type = '\0';
1396    date[0] = buf[0] = '\0';
1397
1398    strcpy(format,_format);
1399
1400    /* alter the formatstring, while it works for all languages now in wine
1401    its possible that it fails when the time looks like ss:mm:hh as example*/
1402    if (tflags & (TIME_NOMINUTESORSECONDS))
1403    { if ((pos = strstr ( format, ":mm")))
1404      { memcpy ( pos, pos+3, strlen(format)-(pos-format)-2 );
1405      }
1406    }
1407    if (tflags & (TIME_NOSECONDS))
1408    { if ((pos = strstr ( format, ":ss")))
1409      { memcpy ( pos, pos+3, strlen(format)-(pos-format)-2 );
1410      }
1411    }
1412
1413    for (inpos = 0;; inpos++) {
1414       /* TRACE("STATE inpos=%2d outpos=%2d count=%d inquote=%d type=%c buf,date = %c,%c\n", inpos, outpos, count, inquote, type, buf[inpos], date[outpos]); */
1415       if (inquote) {
1416          if (format[inpos] == '\'') {
1417             if (format[inpos+1] == '\'') {
1418                inpos += 1;
1419                date[outpos++] = '\'';
1420             } else {
1421                inquote = 0;
1422                continue; /* we did nothing to the output */
1423             }
1424          } else if (format[inpos] == '\0') {
1425             date[outpos++] = '\0';
1426             if (outpos > datelen) Overflow = 1;
1427             break;
1428          } else {
1429             date[outpos++] = format[inpos];
1430             if (outpos > datelen) {
1431                Overflow = 1;
1432                date[outpos-1] = '\0'; /* this is the last place where
1433                                          it's safe to write */
1434                break;
1435             }
1436          }
1437       } else if (  (count && (format[inpos] != type))
1438                    || count == 4
1439                    || (count == 2 && strchr("ghHmst", type)) ) {
1440             if (type == 'h' && (tflags & TIME_FORCE24HOURFORMAT)) type= 'H';
1441             if (type == 'd') {
1442                if (count == 4) {
1443                   GetLocaleInfoA(locale,
1444                                    LOCALE_SDAYNAME1
1445                                    + (xtime->wDayOfWeek+6)%7,
1446                                    buf, sizeof(buf));
1447                } else if (count == 3) {
1448                            GetLocaleInfoA(locale,
1449                                             LOCALE_SABBREVDAYNAME1
1450                                             + (xtime->wDayOfWeek+6)%7,
1451                                             buf, sizeof(buf));
1452                       } else {
1453                   sprintf(buf, dgfmt[count], xtime->wDay);
1454                }
1455             } else if (type == 'M') {
1456                if (count == 3) {
1457                   GetLocaleInfoA(locale,
1458                                    LOCALE_SABBREVMONTHNAME1
1459                                    + xtime->wMonth - 1,
1460                                    buf, sizeof(buf));
1461                } else if (count == 4) {
1462                   GetLocaleInfoA(locale,
1463                                    LOCALE_SMONTHNAME1
1464                                    + xtime->wMonth - 1,
1465                                    buf, sizeof(buf));
1466                  } else {
1467                   sprintf(buf, dgfmt[count], xtime->wMonth);
1468                }
1469             } else if (type == 'y') {
1470                if (count == 4) {
1471                       sprintf(buf, "%d", xtime->wYear);
1472                } else if (count == 3) {
1473                   strcpy(buf, "yyy");
1474                   WARN("unknown format, c=%c, n=%d\n",  type, count);
1475                  } else {
1476                   sprintf(buf, dgfmt[count], xtime->wYear % 100);
1477                }
1478             } else if (type == 'g') {
1479                if        (count == 2) {
1480                   FIXME("LOCALE_ICALENDARTYPE unimp.\n");
1481                   strcpy(buf, "AD");
1482             } else {
1483                   strcpy(buf, "g");
1484                   WARN("unknown format, c=%c, n=%d\n", type, count);
1485                }
1486             } else if (type == 'h') {
1487                /* gives us hours 1:00 -- 12:00 */
1488                sprintf(buf, dgfmt[count], (xtime->wHour-1)%12 +1);
1489             } else if (type == 'H') {
1490                /* 24-hour time */
1491                sprintf(buf, dgfmt[count], xtime->wHour);
1492             } else if ( type == 'm') {
1493                sprintf(buf, dgfmt[count], xtime->wMinute);
1494             } else if ( type == 's') {
1495                sprintf(buf, dgfmt[count], xtime->wSecond);
1496             } else if (type == 't') {
1497                if ((tflags & TIME_NOTIMEMARKER))
1498                   buf[0]='\0';
1499                else if (count == 1) {
1500                   sprintf(buf, "%c", (xtime->wHour < 12) ? 'A' : 'P');
1501                } else if (count == 2) {
1502                  /* sprintf(buf, "%s", (xtime->wHour < 12) ? "AM" : "PM"); */
1503                   GetLocaleInfoA(locale,
1504                            (xtime->wHour<12)
1505                            ? LOCALE_S1159 : LOCALE_S2359,
1506                            buf, sizeof(buf));
1507                }
1508             };
1509
1510             /* we need to check the next char in the format string
1511                again, no matter what happened */
1512             inpos--;
1513
1514             /* add the contents of buf to the output */
1515             buflen = strlen(buf);
1516             if (outpos + buflen < datelen) {
1517                date[outpos] = '\0'; /* for strcat to hook onto */
1518                  strcat(date, buf);
1519                outpos += buflen;
1520             } else {
1521                date[outpos] = '\0';
1522                strncat(date, buf, datelen - outpos);
1523                  date[datelen - 1] = '\0';
1524                  SetLastError(ERROR_INSUFFICIENT_BUFFER);
1525                WARN("insufficient buffer\n");
1526                  return 0;
1527             }
1528
1529             /* reset the variables we used to keep track of this item */
1530             count = 0;
1531             type = '\0';
1532          } else if (format[inpos] == '\0') {
1533             /* we can't check for this at the loop-head, because
1534                that breaks the printing of the last format-item */
1535             date[outpos] = '\0';
1536             break;
1537          } else if (count) {
1538             /* continuing a code for an item */
1539             count +=1;
1540             continue;
1541          } else if (strchr("hHmstyMdg", format[inpos])) {
1542             type = format[inpos];
1543             count = 1;
1544             continue;
1545          } else if (format[inpos] == '\'') {
1546             inquote = 1;
1547             continue;
1548        } else {
1549             date[outpos++] = format[inpos];
1550          }
1551       /* now deal with a possible buffer overflow */
1552       if (outpos >= datelen) {
1553        date[datelen - 1] = '\0';
1554        SetLastError(ERROR_INSUFFICIENT_BUFFER);
1555        return 0;
1556       }
1557    }
1558
1559    if (Overflow) {
1560       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1561    };
1562
1563    /* finish it off with a string terminator */
1564    outpos++;
1565    /* sanity check */
1566    if (outpos > datelen-1) outpos = datelen-1;
1567    date[outpos] = '\0';
1568
1569    TRACE("returns string '%s', len %d\n", date, outpos);
1570    return outpos;
1571 }
1572
1573 /******************************************************************************
1574  * OLE_GetFormatW [INTERNAL]
1575  */
1576 static INT OLE_GetFormatW(LCID locale, DWORD flags, DWORD tflags,
1577                             const SYSTEMTIME* xtime,
1578                             LPCWSTR format,
1579                             LPWSTR output, INT outlen)
1580 {
1581    INT   inpos, outpos;
1582    int     count, type=0, inquote;
1583    int     Overflow; /* loop check */
1584    char    tmp[16];
1585    WCHAR   buf[40];
1586    int     buflen=0;
1587    WCHAR   arg0[] = {0}, arg1[] = {'%','d',0};
1588    WCHAR   arg2[] = {'%','0','2','d',0};
1589    WCHAR  *argarr[3];
1590    int     datevars=0, timevars=0;
1591
1592    argarr[0] = arg0;
1593    argarr[1] = arg1;
1594    argarr[2] = arg2;
1595
1596    /* make a debug report */
1597    TRACE("args: 0x%lx, 0x%lx, 0x%lx, time(d=%d,h=%d,m=%d,s=%d), fmt:%s (at %p), "
1598               "%p with max len %d\n",
1599          locale, flags, tflags,
1600          xtime->wDay, xtime->wHour, xtime->wMinute, xtime->wSecond,
1601          debugstr_w(format), format, output, outlen);
1602
1603    if(outlen == 0) {
1604      FIXME("outlen = 0, returning 255\n");
1605      return 255;
1606    }
1607
1608    /* initialize state variables */
1609    inpos = outpos = 0;
1610    count = 0;
1611    inquote = Overflow = 0;
1612    /* this is really just a sanity check */
1613    output[0] = buf[0] = 0;
1614
1615    /* this loop is the core of the function */
1616    for (inpos = 0; /* we have several break points */ ; inpos++) {
1617       if (inquote) {
1618          if (format[inpos] == (WCHAR) '\'') {
1619             if (format[inpos+1] == '\'') {
1620                inpos++;
1621                output[outpos++] = '\'';
1622             } else {
1623                inquote = 0;
1624                continue;
1625             }
1626          } else if (format[inpos] == 0) {
1627             output[outpos++] = 0;
1628             if (outpos > outlen) Overflow = 1;
1629             break;  /*  normal exit (within a quote) */
1630          } else {
1631             output[outpos++] = format[inpos]; /* copy input */
1632             if (outpos > outlen) {
1633                Overflow = 1;
1634                output[outpos-1] = 0;
1635                break;
1636             }
1637          }
1638       } else if (  (count && (format[inpos] != type))
1639                    || ( (count==4 && type =='y') ||
1640                         (count==4 && type =='M') ||
1641                         (count==4 && type =='d') ||
1642                         (count==2 && type =='g') ||
1643                         (count==2 && type =='h') ||
1644                         (count==2 && type =='H') ||
1645                         (count==2 && type =='m') ||
1646                         (count==2 && type =='s') ||
1647                         (count==2 && type =='t') )  ) {
1648           switch(type)
1649           {
1650           case 'd':
1651             if        (count == 4) {
1652                GetLocaleInfoW(locale,
1653                              LOCALE_SDAYNAME1 + (xtime->wDayOfWeek +6)%7,
1654                              buf, sizeof(buf)/sizeof(WCHAR) );
1655             } else if (count == 3) {
1656                GetLocaleInfoW(locale,
1657                                 LOCALE_SABBREVDAYNAME1 +
1658                                 (xtime->wDayOfWeek +6)%7,
1659                                 buf, sizeof(buf)/sizeof(WCHAR) );
1660             } else {
1661                 sprintf( tmp, "%.*d", count, xtime->wDay );
1662                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1663             }
1664             break;
1665
1666           case 'M':
1667             if        (count == 4) {
1668                GetLocaleInfoW(locale,  LOCALE_SMONTHNAME1 +
1669                                 xtime->wMonth -1, buf,
1670                                 sizeof(buf)/sizeof(WCHAR) );
1671             } else if (count == 3) {
1672                GetLocaleInfoW(locale,  LOCALE_SABBREVMONTHNAME1 +
1673                                 xtime->wMonth -1, buf,
1674                                 sizeof(buf)/sizeof(WCHAR) );
1675             } else {
1676                 sprintf( tmp, "%.*d", count, xtime->wMonth );
1677                 MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1678             }
1679             break;
1680           case 'y':
1681             if        (count == 4) {
1682                 sprintf( tmp, "%d", xtime->wYear );
1683             } else if (count == 3) {
1684                 strcpy( tmp, "yyy" );
1685             } else {
1686                 sprintf( tmp, "%.*d", count, xtime->wYear % 100 );
1687             }
1688             MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1689             break;
1690
1691           case 'g':
1692             if        (count == 2) {
1693                FIXME("LOCALE_ICALENDARTYPE unimplemented\n");
1694                strcpy( tmp, "AD" );
1695             } else {
1696                /* Win API sez we copy it verbatim */
1697                 strcpy( tmp, "g" );
1698             }
1699             MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1700             break;
1701
1702           case 'h':
1703               /* hours 1:00-12:00 --- is this right? */
1704               sprintf( tmp, "%.*d", count, (xtime->wHour-1)%12 +1);
1705               MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1706               break;
1707
1708           case 'H':
1709               sprintf( tmp, "%.*d", count, xtime->wHour );
1710               MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1711               break;
1712
1713           case 'm':
1714               sprintf( tmp, "%.*d", count, xtime->wMinute );
1715               MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1716               break;
1717
1718           case 's':
1719               sprintf( tmp, "%.*d", count, xtime->wSecond );
1720               MultiByteToWideChar( CP_ACP, 0, tmp, -1, buf, sizeof(buf)/sizeof(WCHAR) );
1721               break;
1722
1723           case 't':
1724             GetLocaleInfoW(locale, (xtime->wHour < 12) ?
1725                              LOCALE_S1159 : LOCALE_S2359,
1726                              buf, sizeof(buf) );
1727             if        (count == 1) {
1728                buf[1] = 0;
1729             }
1730             break;
1731           }
1732
1733          /* no matter what happened,  we need to check this next
1734             character the next time we loop through */
1735          inpos--;
1736
1737          /* cat buf onto the output */
1738          outlen = strlenW(buf);
1739          if (outpos + buflen < outlen) {
1740             strcpyW( output + outpos, buf );
1741             outpos += buflen;
1742          } else {
1743             lstrcpynW( output + outpos, buf, outlen - outpos );
1744             Overflow = 1;
1745             break; /* Abnormal exit */
1746          }
1747
1748          /* reset the variables we used this time */
1749          count = 0;
1750          type = '\0';
1751       } else if (format[inpos] == 0) {
1752          /* we can't check for this at the beginning,  because that
1753          would keep us from printing a format spec that ended the
1754          string */
1755          output[outpos] = 0;
1756          break;  /*  NORMAL EXIT  */
1757       } else if (count) {
1758          /* how we keep track of the middle of a format spec */
1759          count++;
1760          continue;
1761       } else if ( (datevars && (format[inpos]=='d' ||
1762                                 format[inpos]=='M' ||
1763                                 format[inpos]=='y' ||
1764                                 format[inpos]=='g')  ) ||
1765                   (timevars && (format[inpos]=='H' ||
1766                                 format[inpos]=='h' ||
1767                                 format[inpos]=='m' ||
1768                                 format[inpos]=='s' ||
1769                                 format[inpos]=='t') )    ) {
1770          type = format[inpos];
1771          count = 1;
1772          continue;
1773       } else if (format[inpos] == '\'') {
1774          inquote = 1;
1775          continue;
1776       } else {
1777          /* unquoted literals */
1778          output[outpos++] = format[inpos];
1779       }
1780    }
1781
1782    if (Overflow) {
1783       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1784       WARN(" buffer overflow\n");
1785    };
1786
1787    /* final string terminator and sanity check */
1788    outpos++;
1789    if (outpos > outlen-1) outpos = outlen-1;
1790    output[outpos] = '0';
1791
1792    TRACE(" returning %s\n", debugstr_w(output));
1793
1794    return (!Overflow) ? outlen : 0;
1795
1796 }
1797
1798
1799 /******************************************************************************
1800  *              GetDateFormatA  [KERNEL32.@]
1801  * Makes an ASCII string of the date
1802  *
1803  * This function uses format to format the date,  or,  if format
1804  * is NULL, uses the default for the locale.  format is a string
1805  * of literal fields and characters as follows:
1806  *
1807  * - d    single-digit (no leading zero) day (of month)
1808  * - dd   two-digit day (of month)
1809  * - ddd  short day-of-week name
1810  * - dddd long day-of-week name
1811  * - M    single-digit month
1812  * - MM   two-digit month
1813  * - MMM  short month name
1814  * - MMMM full month name
1815  * - y    two-digit year, no leading 0
1816  * - yy   two-digit year
1817  * - yyyy four-digit year
1818  * - gg   era string
1819  *
1820  */
1821 INT WINAPI GetDateFormatA(LCID locale,DWORD flags,
1822                               const SYSTEMTIME* xtime,
1823                               LPCSTR format, LPSTR date,INT datelen)
1824 {
1825
1826   char format_buf[40];
1827   LPCSTR thisformat;
1828   SYSTEMTIME t;
1829   LPSYSTEMTIME thistime;
1830   LCID thislocale;
1831   INT ret;
1832   FILETIME ft;
1833   BOOL res;
1834
1835   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
1836               locale,flags,xtime,format,date,datelen);
1837
1838   if (!locale) {
1839      locale = LOCALE_SYSTEM_DEFAULT;
1840      };
1841
1842   if (locale == LOCALE_SYSTEM_DEFAULT) {
1843      thislocale = GetSystemDefaultLCID();
1844   } else if (locale == LOCALE_USER_DEFAULT) {
1845      thislocale = GetUserDefaultLCID();
1846   } else {
1847      thislocale = locale;
1848    };
1849
1850   if (xtime == NULL) {
1851      GetSystemTime(&t);
1852   } else {
1853       /* Silently correct wDayOfWeek by transforming to FileTime and back again */
1854       res=SystemTimeToFileTime(xtime,&ft);
1855       /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER  on error */
1856       /*FIXME: SystemTimeToFileTime doesn't yet do that check */
1857       if(!res)
1858         {
1859           SetLastError(ERROR_INVALID_PARAMETER);
1860           return 0;
1861         }
1862       FileTimeToSystemTime(&ft,&t);
1863
1864   };
1865   thistime = &t;
1866
1867   if (format == NULL) {
1868      GetLocaleInfoA(thislocale, ((flags&DATE_LONGDATE)
1869                                    ? LOCALE_SLONGDATE
1870                                    : LOCALE_SSHORTDATE),
1871                       format_buf, sizeof(format_buf));
1872      thisformat = format_buf;
1873   } else {
1874      thisformat = format;
1875   };
1876
1877
1878   ret = OLE_GetFormatA(thislocale, flags, 0, thistime, thisformat,
1879                        date, datelen);
1880
1881
1882    TRACE(
1883                "GetDateFormatA() returning %d, with data=%s\n",
1884                ret, date);
1885   return ret;
1886 }
1887
1888 /******************************************************************************
1889  *              GetDateFormatW  [KERNEL32.@]
1890  * Makes a Unicode string of the date
1891  *
1892  * Acts the same as GetDateFormatA(),  except that it's Unicode.
1893  * Accepts & returns sizes as counts of Unicode characters.
1894  *
1895  */
1896 INT WINAPI GetDateFormatW(LCID locale,DWORD flags,
1897                               const SYSTEMTIME* xtime,
1898                               LPCWSTR format,
1899                               LPWSTR date, INT datelen)
1900 {
1901     WCHAR format_buf[40];
1902     LPCWSTR thisformat;
1903     SYSTEMTIME t;
1904     LPSYSTEMTIME thistime;
1905     LCID thislocale;
1906     INT ret;
1907     FILETIME ft;
1908     BOOL res;
1909     
1910     TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",
1911           locale,flags,xtime,debugstr_w(format),date,datelen);
1912     
1913     if (!locale) {
1914         locale = LOCALE_SYSTEM_DEFAULT;
1915     };
1916     
1917     if (locale == LOCALE_SYSTEM_DEFAULT) {
1918         thislocale = GetSystemDefaultLCID();
1919     } else if (locale == LOCALE_USER_DEFAULT) {
1920         thislocale = GetUserDefaultLCID();
1921     } else {
1922         thislocale = locale;
1923     };
1924     
1925     if (xtime == NULL) {
1926         GetSystemTime(&t);
1927     } else {
1928         /* Silently correct wDayOfWeek by transforming to FileTime and back again */
1929         res=SystemTimeToFileTime(xtime,&ft);
1930         /* Check year(?)/month and date for range and set ERROR_INVALID_PARAMETER  on error */
1931         /*FIXME: SystemTimeToFileTime doesn't yet do that check */
1932         if(!res) {
1933             SetLastError(ERROR_INVALID_PARAMETER);
1934             return 0;
1935         }
1936         FileTimeToSystemTime(&ft,&t);
1937         
1938     };
1939     thistime = &t;
1940     
1941     if (format == NULL) {
1942         GetLocaleInfoW(thislocale, ((flags&DATE_LONGDATE)
1943                                     ? LOCALE_SLONGDATE
1944                                     : LOCALE_SSHORTDATE),
1945                        format_buf, sizeof(format_buf)/sizeof(*format_buf));
1946         thisformat = format_buf;
1947     } else {
1948         thisformat = format;
1949     };
1950     
1951     
1952     ret = OLE_GetFormatW(thislocale, flags, 0, thistime, thisformat,
1953                          date, datelen);
1954     
1955     
1956     TRACE("GetDateFormatW() returning %d, with data=%s\n",
1957           ret, debugstr_w(date));
1958     return ret;
1959 }
1960
1961 /**************************************************************************
1962  *              EnumDateFormatsA        (KERNEL32.@)
1963  */
1964 BOOL WINAPI EnumDateFormatsA(
1965   DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale,  DWORD dwFlags)
1966 {
1967   LCID Loc = GetUserDefaultLCID();
1968   if(!lpDateFmtEnumProc)
1969     {
1970       SetLastError(ERROR_INVALID_PARAMETER);
1971       return FALSE;
1972     }
1973
1974   switch( Loc )
1975  {
1976
1977    case 0x00000407:  /* (Loc,"de_DE") */
1978    {
1979     switch(dwFlags)
1980     {
1981       case DATE_SHORTDATE:
1982         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1983         if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1984         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1985         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1986         return TRUE;
1987       case DATE_LONGDATE:
1988         if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1989         if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1990         if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1991         return TRUE;
1992       default:
1993         FIXME("Unknown date format (%ld)\n", dwFlags);
1994         SetLastError(ERROR_INVALID_PARAMETER);
1995         return FALSE;
1996      }
1997    }
1998
1999    case 0x0000040c:  /* (Loc,"fr_FR") */
2000    {
2001     switch(dwFlags)
2002     {
2003       case DATE_SHORTDATE:
2004         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2005         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
2006         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
2007         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2008         return TRUE;
2009       case DATE_LONGDATE:
2010         if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
2011         if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
2012         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2013         return TRUE;
2014       default:
2015         FIXME("Unknown date format (%ld)\n", dwFlags);
2016         SetLastError(ERROR_INVALID_PARAMETER);
2017         return FALSE;
2018      }
2019    }
2020
2021    case 0x00000c0c:  /* (Loc,"fr_CA") */
2022    {
2023     switch(dwFlags)
2024     {
2025       case DATE_SHORTDATE:
2026         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
2027         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
2028         if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
2029         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2030         return TRUE;
2031       case DATE_LONGDATE:
2032         if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
2033         if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
2034         return TRUE;
2035       default:
2036         FIXME("Unknown date format (%ld)\n", dwFlags);
2037         SetLastError(ERROR_INVALID_PARAMETER);
2038         return FALSE;
2039      }
2040    }
2041
2042    case 0x00000809:  /* (Loc,"en_UK") */
2043   {
2044    switch(dwFlags)
2045     {
2046       case DATE_SHORTDATE:
2047         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2048         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2049         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2050         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2051         return TRUE;
2052       case DATE_LONGDATE:
2053         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2054         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2055         return TRUE;
2056       default:
2057         FIXME("Unknown date format (%ld)\n", dwFlags);
2058         SetLastError(ERROR_INVALID_PARAMETER);
2059         return FALSE;
2060     }
2061   }
2062
2063    case 0x00000c09:  /* (Loc,"en_AU") */
2064   {
2065    switch(dwFlags)
2066     {
2067       case DATE_SHORTDATE:
2068         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
2069         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2070         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2071         return TRUE;
2072       case DATE_LONGDATE:
2073         if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
2074         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2075         return TRUE;
2076       default:
2077         FIXME("Unknown date format (%ld)\n", dwFlags);
2078         SetLastError(ERROR_INVALID_PARAMETER);
2079         return FALSE;
2080     }
2081   }
2082
2083    case 0x00001009:  /* (Loc,"en_CA") */
2084   {
2085    switch(dwFlags)
2086     {
2087       case DATE_SHORTDATE:
2088         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2089         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2090         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
2091         if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
2092         return TRUE;
2093       case DATE_LONGDATE:
2094         if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
2095         if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
2096         return TRUE;
2097       default:
2098         FIXME("Unknown date format (%ld)\n", dwFlags);
2099         SetLastError(ERROR_INVALID_PARAMETER);
2100         return FALSE;
2101     }
2102   }
2103
2104    case 0x00001409:  /* (Loc,"en_NZ") */
2105   {
2106    switch(dwFlags)
2107     {
2108       case DATE_SHORTDATE:
2109         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
2110         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2111         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
2112         return TRUE;
2113       case DATE_LONGDATE:
2114         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2115         if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
2116         return TRUE;
2117       default:
2118         FIXME("Unknown date format (%ld)\n", dwFlags);
2119         SetLastError(ERROR_INVALID_PARAMETER);
2120         return FALSE;
2121     }
2122   }
2123
2124    case 0x00001809:  /* (Loc,"en_IE") */
2125   {
2126    switch(dwFlags)
2127     {
2128       case DATE_SHORTDATE:
2129         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2130         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2131         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2132         return TRUE;
2133       case DATE_LONGDATE:
2134         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2135         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2136         return TRUE;
2137       default:
2138         FIXME("Unknown date format (%ld)\n", dwFlags);
2139         SetLastError(ERROR_INVALID_PARAMETER);
2140         return FALSE;
2141     }
2142   }
2143
2144    case 0x00001c09:  /* (Loc,"en_ZA") */
2145   {
2146    switch(dwFlags)
2147     {
2148       case DATE_SHORTDATE:
2149         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2150         return TRUE;
2151       case DATE_LONGDATE:
2152         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2153         return TRUE;
2154       default:
2155         FIXME("Unknown date format (%ld)\n", dwFlags);
2156         SetLastError(ERROR_INVALID_PARAMETER);
2157         return FALSE;
2158     }
2159   }
2160
2161    case 0x00002009:  /* (Loc,"en_JM") */
2162   {
2163    switch(dwFlags)
2164     {
2165       case DATE_SHORTDATE:
2166         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2167         return TRUE;
2168       case DATE_LONGDATE:
2169         if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
2170         if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
2171         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
2172         if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
2173         return TRUE;
2174       default:
2175         FIXME("Unknown date format (%ld)\n", dwFlags);
2176         SetLastError(ERROR_INVALID_PARAMETER);
2177         return FALSE;
2178     }
2179   }
2180
2181    case 0x00002809:  /* (Loc,"en_BZ") */
2182    case 0x00002c09:  /* (Loc,"en_TT") */
2183   {
2184    switch(dwFlags)
2185     {
2186       case DATE_SHORTDATE:
2187         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2188         return TRUE;
2189       case DATE_LONGDATE:
2190         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
2191         return TRUE;
2192       default:
2193         FIXME("Unknown date format (%ld)\n", dwFlags);
2194         SetLastError(ERROR_INVALID_PARAMETER);
2195         return FALSE;
2196     }
2197   }
2198
2199    default:  /* default to US English "en_US" */
2200   {
2201    switch(dwFlags)
2202     {
2203       case DATE_SHORTDATE:
2204         if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
2205         if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
2206         if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
2207         if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
2208         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2209         if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
2210         return TRUE;
2211       case DATE_LONGDATE:
2212         if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
2213         if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
2214         if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
2215         if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
2216         return TRUE;
2217       default:
2218         FIXME("Unknown date format (%ld)\n", dwFlags);
2219         SetLastError(ERROR_INVALID_PARAMETER);
2220         return FALSE;
2221     }
2222   }
2223  }
2224 }
2225
2226 /**************************************************************************
2227  *              EnumDateFormatsW        (KERNEL32.@)
2228  */
2229 BOOL WINAPI EnumDateFormatsW(
2230   DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
2231 {
2232   FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
2233   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2234   return FALSE;
2235 }
2236
2237 /**************************************************************************
2238  *              EnumTimeFormatsA        (KERNEL32.@)
2239  */
2240 BOOL WINAPI EnumTimeFormatsA(
2241   TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2242 {
2243   LCID Loc = GetUserDefaultLCID();
2244   if(!lpTimeFmtEnumProc)
2245     {
2246       SetLastError(ERROR_INVALID_PARAMETER);
2247       return FALSE;
2248     }
2249   if(dwFlags)
2250     {
2251       FIXME("Unknown time format (%ld)\n", dwFlags);
2252     }
2253
2254   switch( Loc )
2255  {
2256    case 0x00000407:  /* (Loc,"de_DE") */
2257    {
2258     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2259     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2260     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2261     if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
2262     if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
2263     return TRUE;
2264    }
2265
2266    case 0x0000040c:  /* (Loc,"fr_FR") */
2267    case 0x00000c0c:  /* (Loc,"fr_CA") */
2268    {
2269     if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
2270     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2271     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2272     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2273     if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
2274     return TRUE;
2275    }
2276
2277    case 0x00000809:  /* (Loc,"en_UK") */
2278    case 0x00000c09:  /* (Loc,"en_AU") */
2279    case 0x00001409:  /* (Loc,"en_NZ") */
2280    case 0x00001809:  /* (Loc,"en_IE") */
2281    {
2282     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2283     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2284     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2285     return TRUE;
2286    }
2287
2288    case 0x00001c09:  /* (Loc,"en_ZA") */
2289    case 0x00002809:  /* (Loc,"en_BZ") */
2290    case 0x00002c09:  /* (Loc,"en_TT") */
2291    {
2292     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2293     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2294     return TRUE;
2295    }
2296
2297    default:  /* default to US style "en_US" */
2298    {
2299     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2300     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2301     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2302     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2303     return TRUE;
2304    }
2305  }
2306 }
2307
2308 /**************************************************************************
2309  *              EnumTimeFormatsW        (KERNEL32.@)
2310  */
2311 BOOL WINAPI EnumTimeFormatsW(
2312   TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2313 {
2314   FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
2315   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2316   return FALSE;
2317 }
2318
2319 /**************************************************************************
2320  *           This function is used just locally !
2321  *  Description: Inverts a string.
2322  */
2323 static void OLE_InvertString(char* string)
2324 {
2325     char    sTmpArray[128];
2326     INT     counter, i = 0;
2327
2328     for (counter = strlen(string); counter > 0; counter--)
2329     {
2330         memcpy(sTmpArray + i, string + counter-1, 1);
2331         i++;
2332     }
2333     memcpy(sTmpArray + i, "\0", 1);
2334     strcpy(string, sTmpArray);
2335 }
2336
2337 /***************************************************************************************
2338  *           This function is used just locally !
2339  *  Description: Test if the given string (psNumber) is valid or not.
2340  *               The valid characters are the following:
2341  *               - Characters '0' through '9'.
2342  *               - One decimal point (dot) if the number is a floating-point value.
2343  *               - A minus sign in the first character position if the number is
2344  *                 a negative value.
2345  *              If the function succeeds, psBefore/psAfter will point to the string
2346  *              on the right/left of the decimal symbol. pbNegative indicates if the
2347  *              number is negative.
2348  */
2349 static INT OLE_GetNumberComponents(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
2350 {
2351 char    sNumberSet[] = "0123456789";
2352 BOOL    bInDecimal = FALSE;
2353
2354         /* Test if we do have a minus sign */
2355         if ( *pInput == '-' )
2356         {
2357                 *pbNegative = TRUE;
2358                 pInput++; /* Jump to the next character. */
2359         }
2360
2361         while(*pInput != '\0')
2362         {
2363                 /* Do we have a valid numeric character */
2364                 if ( strchr(sNumberSet, *pInput) != NULL )
2365                 {
2366                         if (bInDecimal == TRUE)
2367                 *psAfter++ = *pInput;
2368                         else
2369                 *psBefore++ = *pInput;
2370                 }
2371                 else
2372                 {
2373                         /* Is this a decimal point (dot) */
2374                         if ( *pInput == '.' )
2375                         {
2376                                 /* Is it the first time we find it */
2377                                 if ((bInDecimal == FALSE))
2378                                         bInDecimal = TRUE;
2379                                 else
2380                                         return -1; /* ERROR: Invalid parameter */
2381                         }
2382                         else
2383                         {
2384                                 /* It's neither a numeric character, nor a decimal point.
2385                                  * Thus, return an error.
2386                  */
2387                                 return -1;
2388                         }
2389                 }
2390         pInput++;
2391         }
2392
2393         /* Add an End of Line character to the output buffers */
2394         *psBefore = '\0';
2395         *psAfter = '\0';
2396
2397         return 0;
2398 }
2399
2400 /**************************************************************************
2401  *           This function is used just locally !
2402  *  Description: A number could be formatted using different numbers
2403  *               of "digits in group" (example: 4;3;2;0).
2404  *               The first parameter of this function is an array
2405  *               containing the rule to be used. Its format is the following:
2406  *               |NDG|DG1|DG2|...|0|
2407  *               where NDG is the number of used "digits in group" and DG1, DG2,
2408  *               are the corresponding "digits in group".
2409  *               Thus, this function returns the grouping value in the array
2410  *               pointed by the second parameter.
2411  */
2412 static INT OLE_GetGrouping(char* sRule, INT index)
2413 {
2414     char    sData[2], sRuleSize[2];
2415     INT     nData, nRuleSize;
2416
2417     memcpy(sRuleSize, sRule, 1);
2418     memcpy(sRuleSize+1, "\0", 1);
2419     nRuleSize = atoi(sRuleSize);
2420
2421     if (index > 0 && index < nRuleSize)
2422     {
2423         memcpy(sData, sRule+index, 1);
2424         memcpy(sData+1, "\0", 1);
2425         nData = atoi(sData);
2426     }
2427
2428     else
2429     {
2430         memcpy(sData, sRule+nRuleSize-1, 1);
2431         memcpy(sData+1, "\0", 1);
2432         nData = atoi(sData);
2433     }
2434
2435     return nData;
2436 }
2437
2438 /**************************************************************************
2439  *              GetNumberFormatA        (KERNEL32.@)
2440  */
2441 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
2442                                LPCSTR lpvalue,   const NUMBERFMTA * lpFormat,
2443                                LPSTR lpNumberStr, int cchNumber)
2444 {
2445     char   sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
2446     INT    nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
2447     char   sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
2448     char   sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
2449     char   *pStr = NULL, *pTmpStr = NULL;
2450     LCID   systemDefaultLCID;
2451     BOOL   bNegative = FALSE;
2452     enum   Operations
2453     {
2454         USE_PARAMETER,
2455         USE_LOCALEINFO,
2456         USE_SYSTEMDEFAULT,
2457         RETURN_ERROR
2458     } used_operation;
2459
2460     strncpy(sNumber, lpvalue, 128);
2461     sNumber[127] = '\0';
2462
2463     /* Make sure we have a valid input string, get the number
2464      * of digits before and after the decimal symbol, and check
2465      * if this is a negative number.
2466      */
2467     if ( OLE_GetNumberComponents(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
2468     {
2469         nNumberDecimal = strlen(sDigitsBeforeDecimal);
2470         nDigits = strlen(sDigitsAfterDecimal);
2471     }
2472     else
2473     {
2474         SetLastError(ERROR_INVALID_PARAMETER);
2475         return 0;
2476     }
2477
2478     /* Which source will we use to format the string */
2479     used_operation = RETURN_ERROR;
2480     if (lpFormat != NULL)
2481     {
2482         if (dwflags == 0)
2483             used_operation = USE_PARAMETER;
2484     }
2485     else
2486     {
2487         if (dwflags & LOCALE_NOUSEROVERRIDE)
2488             used_operation = USE_SYSTEMDEFAULT;
2489         else
2490             used_operation = USE_LOCALEINFO;
2491     }
2492
2493     /* Load the fields we need */
2494     switch(used_operation)
2495     {
2496     case USE_LOCALEINFO:
2497         GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2498         GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2499         GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2500         GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2501         GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2502         GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2503         break;
2504     case USE_PARAMETER:
2505         sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
2506         strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
2507         sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
2508         strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
2509         sprintf(sILZero, "%d",lpFormat->LeadingZero);
2510         sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
2511         break;
2512     case USE_SYSTEMDEFAULT:
2513         systemDefaultLCID = GetSystemDefaultLCID();
2514         GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2515         GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2516         GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2517         GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2518         GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2519         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2520         break;
2521     default:
2522         SetLastError(ERROR_INVALID_PARAMETER);
2523         return 0;
2524     }
2525
2526     nNumberDigits = atoi(sNumberDigits);
2527
2528     /* Remove the ";" */
2529     i=0;
2530     j = 1;
2531     for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
2532     {
2533         if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
2534         {
2535             memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
2536             i++;
2537             j++;
2538         }
2539     }
2540     sprintf(sBuffer, "%d", i);
2541     memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by OLE_GetGrouping() ) */
2542     memcpy(sRule + j, "\0", 1);
2543
2544     /* First, format the digits before the decimal. */
2545     if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
2546     {
2547         /* Working on an inverted string is easier ! */
2548         OLE_InvertString(sDigitsBeforeDecimal);
2549
2550         nStep = nCounter = i = j = 0;
2551         nRuleIndex = 1;
2552         nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2553
2554         /* Here, we will loop until we reach the end of the string.
2555          * An internal counter (j) is used in order to know when to
2556          * insert the "digit group symbol".
2557          */
2558         while (nNumberDecimal > 0)
2559         {
2560             i = nCounter + nStep;
2561             memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
2562             nCounter++;
2563             j++;
2564             if (j >= nGrouping)
2565             {
2566                 j = 0;
2567                 if (nRuleIndex < sRule[0])
2568                     nRuleIndex++;
2569                 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2570                 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
2571                 nStep+= strlen(sDigitGroupSymbol);
2572             }
2573
2574             nNumberDecimal--;
2575         }
2576
2577         memcpy(sDestination + i+1, "\0", 1);
2578         /* Get the string in the right order ! */
2579         OLE_InvertString(sDestination);
2580      }
2581      else
2582      {
2583         nLZ = atoi(sILZero);
2584         if (nLZ != 0)
2585         {
2586             /* Use 0.xxx instead of .xxx */
2587             memcpy(sDestination, "0", 1);
2588             memcpy(sDestination+1, "\0", 1);
2589         }
2590         else
2591             memcpy(sDestination, "\0", 1);
2592
2593      }
2594
2595     /* Second, format the digits after the decimal. */
2596     j = 0;
2597     nCounter = nNumberDigits;
2598     if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
2599     {
2600         i = strlen(sNumber) - strlen(pStr) + 1;
2601         strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
2602         j = strlen(sDigitsAfterDecimal);
2603         if (j < nNumberDigits)
2604             nCounter = nNumberDigits-j;
2605     }
2606     for (i=0;i<nCounter;i++)
2607          memcpy(sDigitsAfterDecimal+i+j, "0", 1);
2608     memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
2609
2610     i = strlen(sDestination);
2611     j = strlen(sDigitsAfterDecimal);
2612     /* Finally, construct the resulting formatted string. */
2613
2614     for (nCounter=0; nCounter<i; nCounter++)
2615         memcpy(sNumber + nCounter, sDestination + nCounter, 1);
2616
2617     memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
2618
2619     for (i=0; i<j; i++)
2620         memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
2621     memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), "\0", 1);
2622
2623     /* Is it a negative number */
2624     if (bNegative == TRUE)
2625     {
2626         i = atoi(sNegNumber);
2627         pStr = sDestination;
2628         pTmpStr = sNumber;
2629         switch (i)
2630         {
2631         case 0:
2632             *pStr++ = '(';
2633             while (*sNumber != '\0')
2634                 *pStr++ =  *pTmpStr++;
2635             *pStr++ = ')';
2636             break;
2637         case 1:
2638             *pStr++ = '-';
2639             while (*pTmpStr != '\0')
2640                 *pStr++ =  *pTmpStr++;
2641             break;
2642         case 2:
2643             *pStr++ = '-';
2644             *pStr++ = ' ';
2645             while (*pTmpStr != '\0')
2646                 *pStr++ =  *pTmpStr++;
2647             break;
2648         case 3:
2649             while (*pTmpStr != '\0')
2650                 *pStr++ =  *pTmpStr++;
2651             *pStr++ = '-';
2652             break;
2653         case 4:
2654             while (*pTmpStr != '\0')
2655                 *pStr++ =  *pTmpStr++;
2656             *pStr++ = ' ';
2657             *pStr++ = '-';
2658             break;
2659         default:
2660             while (*pTmpStr != '\0')
2661                 *pStr++ =  *pTmpStr++;
2662             break;
2663         }
2664     }
2665     else
2666         strcpy(sDestination, sNumber);
2667
2668     /* If cchNumber is zero, then returns the number of bytes or characters
2669      * required to hold the formatted number string
2670      */
2671     retVal = strlen(sDestination) + 1;
2672     if (cchNumber!=0)
2673     {
2674         memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
2675         if (cchNumber < retVal) retVal = 0;
2676     }
2677     return retVal;
2678 }
2679
2680 /**************************************************************************
2681  *              GetNumberFormatW        (KERNEL32.@)
2682  */
2683 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
2684                                LPCWSTR lpvalue,  const NUMBERFMTW * lpFormat,
2685                                LPWSTR lpNumberStr, int cchNumber)
2686 {
2687  FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
2688
2689  lstrcpynW( lpNumberStr, lpvalue, cchNumber );
2690  return cchNumber? strlenW( lpNumberStr ) : 0;
2691 }
2692
2693 /**************************************************************************
2694  *              GetCurrencyFormatA      (KERNEL32.@)
2695  */
2696 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
2697                                LPCSTR lpvalue,   const CURRENCYFMTA * lpFormat,
2698                                LPSTR lpCurrencyStr, int cchCurrency)
2699 {
2700     UINT   nPosOrder, nNegOrder;
2701     INT    retVal;
2702     char   sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
2703     char   *pDestination = sDestination;
2704     char   sNumberFormated[128];
2705     char   *pNumberFormated = sNumberFormated;
2706     LCID   systemDefaultLCID;
2707     BOOL   bIsPositive = FALSE, bValidFormat = FALSE;
2708     enum   Operations
2709     {
2710         USE_PARAMETER,
2711         USE_LOCALEINFO,
2712         USE_SYSTEMDEFAULT,
2713         RETURN_ERROR
2714     } used_operation;
2715
2716     NUMBERFMTA  numberFmt;
2717
2718     /* Which source will we use to format the string */
2719     used_operation = RETURN_ERROR;
2720     if (lpFormat != NULL)
2721     {
2722         if (dwflags == 0)
2723             used_operation = USE_PARAMETER;
2724     }
2725     else
2726     {
2727         if (dwflags & LOCALE_NOUSEROVERRIDE)
2728             used_operation = USE_SYSTEMDEFAULT;
2729         else
2730             used_operation = USE_LOCALEINFO;
2731     }
2732
2733     /* Load the fields we need */
2734     switch(used_operation)
2735     {
2736     case USE_LOCALEINFO:
2737         /* Specific to CURRENCYFMTA */
2738         GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2739         GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2740         GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2741
2742         nPosOrder = atoi(sPosOrder);
2743         nNegOrder = atoi(sNegOrder);
2744         break;
2745     case USE_PARAMETER:
2746         /* Specific to CURRENCYFMTA */
2747         nNegOrder = lpFormat->NegativeOrder;
2748         nPosOrder = lpFormat->PositiveOrder;
2749         strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
2750         break;
2751     case USE_SYSTEMDEFAULT:
2752         systemDefaultLCID = GetSystemDefaultLCID();
2753         /* Specific to CURRENCYFMTA */
2754         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2755         GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2756         GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2757
2758         nPosOrder = atoi(sPosOrder);
2759         nNegOrder = atoi(sNegOrder);
2760         break;
2761     default:
2762         SetLastError(ERROR_INVALID_PARAMETER);
2763         return 0;
2764     }
2765
2766     /* Construct a temporary number format structure */
2767     if (lpFormat != NULL)
2768     {
2769         numberFmt.NumDigits     = lpFormat->NumDigits;
2770         numberFmt.LeadingZero   = lpFormat->LeadingZero;
2771         numberFmt.Grouping      = lpFormat->Grouping;
2772         numberFmt.NegativeOrder = 0;
2773         numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
2774         numberFmt.lpThousandSep = lpFormat->lpThousandSep;
2775         bValidFormat = TRUE;
2776     }
2777
2778     /* Make a call to GetNumberFormatA() */
2779     if (*lpvalue == '-')
2780     {
2781         bIsPositive = FALSE;
2782         retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2783     }
2784     else
2785     {
2786         bIsPositive = TRUE;
2787         retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2788     }
2789
2790     if (retVal == 0)
2791         return 0;
2792
2793     /* construct the formatted string */
2794     if (bIsPositive)
2795     {
2796         switch (nPosOrder)
2797         {
2798         case 0:   /* Prefix, no separation */
2799             strcpy (pDestination, sCurrencySymbol);
2800             strcat (pDestination, pNumberFormated);
2801             break;
2802         case 1:   /* Suffix, no separation */
2803             strcpy (pDestination, pNumberFormated);
2804             strcat (pDestination, sCurrencySymbol);
2805             break;
2806         case 2:   /* Prefix, 1 char separation */
2807             strcpy (pDestination, sCurrencySymbol);
2808             strcat (pDestination, " ");
2809             strcat (pDestination, pNumberFormated);
2810             break;
2811         case 3:   /* Suffix, 1 char separation */
2812             strcpy (pDestination, pNumberFormated);
2813             strcat (pDestination, " ");
2814             strcat (pDestination, sCurrencySymbol);
2815             break;
2816         default:
2817             SetLastError(ERROR_INVALID_PARAMETER);
2818             return 0;
2819         }
2820     }
2821     else  /* negative number */
2822     {
2823         switch (nNegOrder)
2824         {
2825         case 0:   /* format: ($1.1) */
2826             strcpy (pDestination, "(");
2827             strcat (pDestination, sCurrencySymbol);
2828             strcat (pDestination, pNumberFormated);
2829             strcat (pDestination, ")");
2830             break;
2831         case 1:   /* format: -$1.1 */
2832             strcpy (pDestination, "-");
2833             strcat (pDestination, sCurrencySymbol);
2834             strcat (pDestination, pNumberFormated);
2835             break;
2836         case 2:   /* format: $-1.1 */
2837             strcpy (pDestination, sCurrencySymbol);
2838             strcat (pDestination, "-");
2839             strcat (pDestination, pNumberFormated);
2840             break;
2841         case 3:   /* format: $1.1- */
2842             strcpy (pDestination, sCurrencySymbol);
2843             strcat (pDestination, pNumberFormated);
2844             strcat (pDestination, "-");
2845             break;
2846         case 4:   /* format: (1.1$) */
2847             strcpy (pDestination, "(");
2848             strcat (pDestination, pNumberFormated);
2849             strcat (pDestination, sCurrencySymbol);
2850             strcat (pDestination, ")");
2851             break;
2852         case 5:   /* format: -1.1$ */
2853             strcpy (pDestination, "-");
2854             strcat (pDestination, pNumberFormated);
2855             strcat (pDestination, sCurrencySymbol);
2856             break;
2857         case 6:   /* format: 1.1-$ */
2858             strcpy (pDestination, pNumberFormated);
2859             strcat (pDestination, "-");
2860             strcat (pDestination, sCurrencySymbol);
2861             break;
2862         case 7:   /* format: 1.1$- */
2863             strcpy (pDestination, pNumberFormated);
2864             strcat (pDestination, sCurrencySymbol);
2865             strcat (pDestination, "-");
2866             break;
2867         case 8:   /* format: -1.1 $ */
2868             strcpy (pDestination, "-");
2869             strcat (pDestination, pNumberFormated);
2870             strcat (pDestination, " ");
2871             strcat (pDestination, sCurrencySymbol);
2872             break;
2873         case 9:   /* format: -$ 1.1 */
2874             strcpy (pDestination, "-");
2875             strcat (pDestination, sCurrencySymbol);
2876             strcat (pDestination, " ");
2877             strcat (pDestination, pNumberFormated);
2878             break;
2879         case 10:   /* format: 1.1 $- */
2880             strcpy (pDestination, pNumberFormated);
2881             strcat (pDestination, " ");
2882             strcat (pDestination, sCurrencySymbol);
2883             strcat (pDestination, "-");
2884             break;
2885         case 11:   /* format: $ 1.1- */
2886             strcpy (pDestination, sCurrencySymbol);
2887             strcat (pDestination, " ");
2888             strcat (pDestination, pNumberFormated);
2889             strcat (pDestination, "-");
2890             break;
2891         case 12:   /* format: $ -1.1 */
2892             strcpy (pDestination, sCurrencySymbol);
2893             strcat (pDestination, " ");
2894             strcat (pDestination, "-");
2895             strcat (pDestination, pNumberFormated);
2896             break;
2897         case 13:   /* format: 1.1- $ */
2898             strcpy (pDestination, pNumberFormated);
2899             strcat (pDestination, "-");
2900             strcat (pDestination, " ");
2901             strcat (pDestination, sCurrencySymbol);
2902             break;
2903         case 14:   /* format: ($ 1.1) */
2904             strcpy (pDestination, "(");
2905             strcat (pDestination, sCurrencySymbol);
2906             strcat (pDestination, " ");
2907             strcat (pDestination, pNumberFormated);
2908             strcat (pDestination, ")");
2909             break;
2910         case 15:   /* format: (1.1 $) */
2911             strcpy (pDestination, "(");
2912             strcat (pDestination, pNumberFormated);
2913             strcat (pDestination, " ");
2914             strcat (pDestination, sCurrencySymbol);
2915             strcat (pDestination, ")");
2916             break;
2917         default:
2918             SetLastError(ERROR_INVALID_PARAMETER);
2919             return 0;
2920         }
2921     }
2922
2923     retVal = strlen(pDestination) + 1;
2924
2925     if (cchCurrency)
2926     {
2927         memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
2928         if (cchCurrency < retVal) retVal = 0;
2929     }
2930     return retVal;
2931 }
2932
2933 /**************************************************************************
2934  *              GetCurrencyFormatW      (KERNEL32.@)
2935  */
2936 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
2937                                LPCWSTR lpvalue,   const CURRENCYFMTW * lpFormat,
2938                                LPWSTR lpCurrencyStr, int cchCurrency)
2939 {
2940     FIXME("This API function is NOT implemented !\n");
2941     return 0;
2942 }
2943
2944 /******************************************************************************
2945  *              OLE2NLS_CheckLocale     [intern]
2946  */
2947 static LCID OLE2NLS_CheckLocale (LCID locale)
2948 {
2949         if (!locale)
2950         { locale = LOCALE_SYSTEM_DEFAULT;
2951         }
2952
2953         if (locale == LOCALE_SYSTEM_DEFAULT)
2954         { return GetSystemDefaultLCID();
2955         }
2956         else if (locale == LOCALE_USER_DEFAULT)
2957         { return GetUserDefaultLCID();
2958         }
2959         else
2960         { return locale;
2961         }
2962 }
2963 /******************************************************************************
2964  *              GetTimeFormatA  [KERNEL32.@]
2965  * Makes an ASCII string of the time
2966  *
2967  * Formats date according to format,  or locale default if format is
2968  * NULL. The format consists of literal characters and fields as follows:
2969  *
2970  * h  hours with no leading zero (12-hour)
2971  * hh hours with full two digits
2972  * H  hours with no leading zero (24-hour)
2973  * HH hours with full two digits
2974  * m  minutes with no leading zero
2975  * mm minutes with full two digits
2976  * s  seconds with no leading zero
2977  * ss seconds with full two digits
2978  * t  time marker (A or P)
2979  * tt time marker (AM, PM)
2980  *
2981  */
2982 INT WINAPI
2983 GetTimeFormatA(LCID locale,        /* [in]  */
2984                DWORD flags,        /* [in]  */
2985                const SYSTEMTIME* xtime, /* [in]  */
2986                LPCSTR format,      /* [in]  */
2987                LPSTR timestr,      /* [out] */
2988                INT timelen         /* [in]  */)
2989 { char format_buf[40];
2990   LPCSTR thisformat;
2991   SYSTEMTIME t;
2992   const SYSTEMTIME* thistime;
2993   LCID thislocale=0;
2994   DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
2995   INT ret;
2996
2997   TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,xtime,format,timestr,timelen);
2998
2999   thislocale = OLE2NLS_CheckLocale ( locale );
3000
3001   if (format == NULL)
3002   { if (flags & LOCALE_NOUSEROVERRIDE)  /* use system default */
3003     { thislocale = GetSystemDefaultLCID();
3004     }
3005     GetLocaleInfoA(thislocale, thisflags, format_buf, sizeof(format_buf));
3006     thisformat = format_buf;
3007   }
3008   else
3009   { thisformat = format;
3010   }
3011
3012   if (xtime == NULL) /* NULL means use the current local time */
3013   { GetLocalTime(&t);
3014     thistime = &t;
3015   }
3016   else
3017   { thistime = xtime;
3018   /* Check that hour,min and sec is in range */
3019   }
3020   ret = OLE_GetFormatA(thislocale, thisflags, flags, thistime, thisformat,
3021                          timestr, timelen);
3022   return ret;
3023 }
3024
3025
3026 /******************************************************************************
3027  *              GetTimeFormatW  [KERNEL32.@]
3028  * Makes a Unicode string of the time
3029  */
3030 INT WINAPI
3031 GetTimeFormatW(LCID locale,        /* [in]  */
3032                DWORD flags,        /* [in]  */
3033                const SYSTEMTIME* xtime, /* [in]  */
3034                LPCWSTR format,     /* [in]  */
3035                LPWSTR timestr,     /* [out] */
3036                INT timelen         /* [in]  */)
3037 {       WCHAR format_buf[40];
3038         LPCWSTR thisformat;
3039         SYSTEMTIME t;
3040         const SYSTEMTIME* thistime;
3041         LCID thislocale=0;
3042         DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
3043         INT ret;
3044
3045         TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
3046         xtime,debugstr_w(format),timestr,timelen);
3047
3048         thislocale = OLE2NLS_CheckLocale ( locale );
3049
3050         if (format == NULL)
3051         { if (flags & LOCALE_NOUSEROVERRIDE)  /* use system default */
3052           { thislocale = GetSystemDefaultLCID();
3053           }
3054           GetLocaleInfoW(thislocale, thisflags, format_buf, 40);
3055           thisformat = format_buf;
3056         }
3057         else
3058         { thisformat = format;
3059         }
3060
3061         if (xtime == NULL) /* NULL means use the current local time */
3062         { GetLocalTime(&t);
3063           thistime = &t;
3064         }
3065         else
3066         { thistime = xtime;
3067         }
3068
3069         ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat,
3070                          timestr, timelen);
3071         return ret;
3072 }
3073
3074 /******************************************************************************
3075  *              EnumCalendarInfoA       [KERNEL32.@]
3076  */
3077 BOOL WINAPI EnumCalendarInfoA(
3078         CALINFO_ENUMPROCA calinfoproc,LCID locale,CALID calendar,CALTYPE caltype
3079 ) {
3080         FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
3081         return FALSE;
3082 }