- Added support for %struct16 (used by CLSID).
[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    unsigned short datearr[] = {'1','9','9','4','-','1','-','1',0};
1902
1903    FIXME("STUB (should call OLE_GetFormatW)\n");
1904    lstrcpynW(date, datearr, datelen);
1905    return (  datelen < 9) ? datelen : 9;
1906
1907
1908 }
1909
1910 /**************************************************************************
1911  *              EnumDateFormatsA        (KERNEL32.@)
1912  */
1913 BOOL WINAPI EnumDateFormatsA(
1914   DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale,  DWORD dwFlags)
1915 {
1916   LCID Loc = GetUserDefaultLCID();
1917   if(!lpDateFmtEnumProc)
1918     {
1919       SetLastError(ERROR_INVALID_PARAMETER);
1920       return FALSE;
1921     }
1922
1923   switch( Loc )
1924  {
1925
1926    case 0x00000407:  /* (Loc,"de_DE") */
1927    {
1928     switch(dwFlags)
1929     {
1930       case DATE_SHORTDATE:
1931         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1932         if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1933         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1934         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1935         return TRUE;
1936       case DATE_LONGDATE:
1937         if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1938         if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1939         if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1940         return TRUE;
1941       default:
1942         FIXME("Unknown date format (%ld)\n", dwFlags);
1943         SetLastError(ERROR_INVALID_PARAMETER);
1944         return FALSE;
1945      }
1946    }
1947
1948    case 0x0000040c:  /* (Loc,"fr_FR") */
1949    {
1950     switch(dwFlags)
1951     {
1952       case DATE_SHORTDATE:
1953         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1954         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1955         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1956         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1957         return TRUE;
1958       case DATE_LONGDATE:
1959         if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
1960         if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
1961         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1962         return TRUE;
1963       default:
1964         FIXME("Unknown date format (%ld)\n", dwFlags);
1965         SetLastError(ERROR_INVALID_PARAMETER);
1966         return FALSE;
1967      }
1968    }
1969
1970    case 0x00000c0c:  /* (Loc,"fr_CA") */
1971    {
1972     switch(dwFlags)
1973     {
1974       case DATE_SHORTDATE:
1975         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1976         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1977         if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
1978         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1979         return TRUE;
1980       case DATE_LONGDATE:
1981         if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
1982         if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
1983         return TRUE;
1984       default:
1985         FIXME("Unknown date format (%ld)\n", dwFlags);
1986         SetLastError(ERROR_INVALID_PARAMETER);
1987         return FALSE;
1988      }
1989    }
1990
1991    case 0x00000809:  /* (Loc,"en_UK") */
1992   {
1993    switch(dwFlags)
1994     {
1995       case DATE_SHORTDATE:
1996         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1997         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1998         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1999         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2000         return TRUE;
2001       case DATE_LONGDATE:
2002         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2003         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2004         return TRUE;
2005       default:
2006         FIXME("Unknown date format (%ld)\n", dwFlags);
2007         SetLastError(ERROR_INVALID_PARAMETER);
2008         return FALSE;
2009     }
2010   }
2011
2012    case 0x00000c09:  /* (Loc,"en_AU") */
2013   {
2014    switch(dwFlags)
2015     {
2016       case DATE_SHORTDATE:
2017         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
2018         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2019         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2020         return TRUE;
2021       case DATE_LONGDATE:
2022         if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
2023         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2024         return TRUE;
2025       default:
2026         FIXME("Unknown date format (%ld)\n", dwFlags);
2027         SetLastError(ERROR_INVALID_PARAMETER);
2028         return FALSE;
2029     }
2030   }
2031
2032    case 0x00001009:  /* (Loc,"en_CA") */
2033   {
2034    switch(dwFlags)
2035     {
2036       case DATE_SHORTDATE:
2037         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2038         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2039         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
2040         if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
2041         return TRUE;
2042       case DATE_LONGDATE:
2043         if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
2044         if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
2045         return TRUE;
2046       default:
2047         FIXME("Unknown date format (%ld)\n", dwFlags);
2048         SetLastError(ERROR_INVALID_PARAMETER);
2049         return FALSE;
2050     }
2051   }
2052
2053    case 0x00001409:  /* (Loc,"en_NZ") */
2054   {
2055    switch(dwFlags)
2056     {
2057       case DATE_SHORTDATE:
2058         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
2059         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2060         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
2061         return TRUE;
2062       case DATE_LONGDATE:
2063         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2064         if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
2065         return TRUE;
2066       default:
2067         FIXME("Unknown date format (%ld)\n", dwFlags);
2068         SetLastError(ERROR_INVALID_PARAMETER);
2069         return FALSE;
2070     }
2071   }
2072
2073    case 0x00001809:  /* (Loc,"en_IE") */
2074   {
2075    switch(dwFlags)
2076     {
2077       case DATE_SHORTDATE:
2078         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
2079         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
2080         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
2081         return TRUE;
2082       case DATE_LONGDATE:
2083         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2084         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
2085         return TRUE;
2086       default:
2087         FIXME("Unknown date format (%ld)\n", dwFlags);
2088         SetLastError(ERROR_INVALID_PARAMETER);
2089         return FALSE;
2090     }
2091   }
2092
2093    case 0x00001c09:  /* (Loc,"en_ZA") */
2094   {
2095    switch(dwFlags)
2096     {
2097       case DATE_SHORTDATE:
2098         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2099         return TRUE;
2100       case DATE_LONGDATE:
2101         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
2102         return TRUE;
2103       default:
2104         FIXME("Unknown date format (%ld)\n", dwFlags);
2105         SetLastError(ERROR_INVALID_PARAMETER);
2106         return FALSE;
2107     }
2108   }
2109
2110    case 0x00002009:  /* (Loc,"en_JM") */
2111   {
2112    switch(dwFlags)
2113     {
2114       case DATE_SHORTDATE:
2115         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2116         return TRUE;
2117       case DATE_LONGDATE:
2118         if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
2119         if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
2120         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
2121         if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
2122         return TRUE;
2123       default:
2124         FIXME("Unknown date format (%ld)\n", dwFlags);
2125         SetLastError(ERROR_INVALID_PARAMETER);
2126         return FALSE;
2127     }
2128   }
2129
2130    case 0x00002809:  /* (Loc,"en_BZ") */
2131    case 0x00002c09:  /* (Loc,"en_TT") */
2132   {
2133    switch(dwFlags)
2134     {
2135       case DATE_SHORTDATE:
2136         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
2137         return TRUE;
2138       case DATE_LONGDATE:
2139         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
2140         return TRUE;
2141       default:
2142         FIXME("Unknown date format (%ld)\n", dwFlags);
2143         SetLastError(ERROR_INVALID_PARAMETER);
2144         return FALSE;
2145     }
2146   }
2147
2148    default:  /* default to US English "en_US" */
2149   {
2150    switch(dwFlags)
2151     {
2152       case DATE_SHORTDATE:
2153         if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
2154         if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
2155         if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
2156         if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
2157         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
2158         if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
2159         return TRUE;
2160       case DATE_LONGDATE:
2161         if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
2162         if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
2163         if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
2164         if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
2165         return TRUE;
2166       default:
2167         FIXME("Unknown date format (%ld)\n", dwFlags);
2168         SetLastError(ERROR_INVALID_PARAMETER);
2169         return FALSE;
2170     }
2171   }
2172  }
2173 }
2174
2175 /**************************************************************************
2176  *              EnumDateFormatsW        (KERNEL32.@)
2177  */
2178 BOOL WINAPI EnumDateFormatsW(
2179   DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
2180 {
2181   FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
2182   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2183   return FALSE;
2184 }
2185
2186 /**************************************************************************
2187  *              EnumTimeFormatsA        (KERNEL32.@)
2188  */
2189 BOOL WINAPI EnumTimeFormatsA(
2190   TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2191 {
2192   LCID Loc = GetUserDefaultLCID();
2193   if(!lpTimeFmtEnumProc)
2194     {
2195       SetLastError(ERROR_INVALID_PARAMETER);
2196       return FALSE;
2197     }
2198   if(dwFlags)
2199     {
2200       FIXME("Unknown time format (%ld)\n", dwFlags);
2201     }
2202
2203   switch( Loc )
2204  {
2205    case 0x00000407:  /* (Loc,"de_DE") */
2206    {
2207     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2208     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2209     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2210     if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
2211     if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
2212     return TRUE;
2213    }
2214
2215    case 0x0000040c:  /* (Loc,"fr_FR") */
2216    case 0x00000c0c:  /* (Loc,"fr_CA") */
2217    {
2218     if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
2219     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2220     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2221     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
2222     if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
2223     return TRUE;
2224    }
2225
2226    case 0x00000809:  /* (Loc,"en_UK") */
2227    case 0x00000c09:  /* (Loc,"en_AU") */
2228    case 0x00001409:  /* (Loc,"en_NZ") */
2229    case 0x00001809:  /* (Loc,"en_IE") */
2230    {
2231     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2232     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2233     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2234     return TRUE;
2235    }
2236
2237    case 0x00001c09:  /* (Loc,"en_ZA") */
2238    case 0x00002809:  /* (Loc,"en_BZ") */
2239    case 0x00002c09:  /* (Loc,"en_TT") */
2240    {
2241     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2242     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2243     return TRUE;
2244    }
2245
2246    default:  /* default to US style "en_US" */
2247    {
2248     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
2249     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
2250     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
2251     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
2252     return TRUE;
2253    }
2254  }
2255 }
2256
2257 /**************************************************************************
2258  *              EnumTimeFormatsW        (KERNEL32.@)
2259  */
2260 BOOL WINAPI EnumTimeFormatsW(
2261   TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags)
2262 {
2263   FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
2264   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2265   return FALSE;
2266 }
2267
2268 /**************************************************************************
2269  *           This function is used just locally !
2270  *  Description: Inverts a string.
2271  */
2272 static void OLE_InvertString(char* string)
2273 {
2274     char    sTmpArray[128];
2275     INT     counter, i = 0;
2276
2277     for (counter = strlen(string); counter > 0; counter--)
2278     {
2279         memcpy(sTmpArray + i, string + counter-1, 1);
2280         i++;
2281     }
2282     memcpy(sTmpArray + i, "\0", 1);
2283     strcpy(string, sTmpArray);
2284 }
2285
2286 /***************************************************************************************
2287  *           This function is used just locally !
2288  *  Description: Test if the given string (psNumber) is valid or not.
2289  *               The valid characters are the following:
2290  *               - Characters '0' through '9'.
2291  *               - One decimal point (dot) if the number is a floating-point value.
2292  *               - A minus sign in the first character position if the number is
2293  *                 a negative value.
2294  *              If the function succeeds, psBefore/psAfter will point to the string
2295  *              on the right/left of the decimal symbol. pbNegative indicates if the
2296  *              number is negative.
2297  */
2298 static INT OLE_GetNumberComponents(char* pInput, char* psBefore, char* psAfter, BOOL* pbNegative)
2299 {
2300 char    sNumberSet[] = "0123456789";
2301 BOOL    bInDecimal = FALSE;
2302
2303         /* Test if we do have a minus sign */
2304         if ( *pInput == '-' )
2305         {
2306                 *pbNegative = TRUE;
2307                 pInput++; /* Jump to the next character. */
2308         }
2309
2310         while(*pInput != '\0')
2311         {
2312                 /* Do we have a valid numeric character */
2313                 if ( strchr(sNumberSet, *pInput) != NULL )
2314                 {
2315                         if (bInDecimal == TRUE)
2316                 *psAfter++ = *pInput;
2317                         else
2318                 *psBefore++ = *pInput;
2319                 }
2320                 else
2321                 {
2322                         /* Is this a decimal point (dot) */
2323                         if ( *pInput == '.' )
2324                         {
2325                                 /* Is it the first time we find it */
2326                                 if ((bInDecimal == FALSE))
2327                                         bInDecimal = TRUE;
2328                                 else
2329                                         return -1; /* ERROR: Invalid parameter */
2330                         }
2331                         else
2332                         {
2333                                 /* It's neither a numeric character, nor a decimal point.
2334                                  * Thus, return an error.
2335                  */
2336                                 return -1;
2337                         }
2338                 }
2339         pInput++;
2340         }
2341
2342         /* Add an End of Line character to the output buffers */
2343         *psBefore = '\0';
2344         *psAfter = '\0';
2345
2346         return 0;
2347 }
2348
2349 /**************************************************************************
2350  *           This function is used just locally !
2351  *  Description: A number could be formatted using different numbers
2352  *               of "digits in group" (example: 4;3;2;0).
2353  *               The first parameter of this function is an array
2354  *               containing the rule to be used. Its format is the following:
2355  *               |NDG|DG1|DG2|...|0|
2356  *               where NDG is the number of used "digits in group" and DG1, DG2,
2357  *               are the corresponding "digits in group".
2358  *               Thus, this function returns the grouping value in the array
2359  *               pointed by the second parameter.
2360  */
2361 static INT OLE_GetGrouping(char* sRule, INT index)
2362 {
2363     char    sData[2], sRuleSize[2];
2364     INT     nData, nRuleSize;
2365
2366     memcpy(sRuleSize, sRule, 1);
2367     memcpy(sRuleSize+1, "\0", 1);
2368     nRuleSize = atoi(sRuleSize);
2369
2370     if (index > 0 && index < nRuleSize)
2371     {
2372         memcpy(sData, sRule+index, 1);
2373         memcpy(sData+1, "\0", 1);
2374         nData = atoi(sData);
2375     }
2376
2377     else
2378     {
2379         memcpy(sData, sRule+nRuleSize-1, 1);
2380         memcpy(sData+1, "\0", 1);
2381         nData = atoi(sData);
2382     }
2383
2384     return nData;
2385 }
2386
2387 /**************************************************************************
2388  *              GetNumberFormatA        (KERNEL32.@)
2389  */
2390 INT WINAPI GetNumberFormatA(LCID locale, DWORD dwflags,
2391                                LPCSTR lpvalue,   const NUMBERFMTA * lpFormat,
2392                                LPSTR lpNumberStr, int cchNumber)
2393 {
2394     char   sNumberDigits[3], sDecimalSymbol[5], sDigitsInGroup[11], sDigitGroupSymbol[5], sILZero[2];
2395     INT    nNumberDigits, nNumberDecimal, i, j, nCounter, nStep, nRuleIndex, nGrouping, nDigits, retVal, nLZ;
2396     char   sNumber[128], sDestination[128], sDigitsAfterDecimal[10], sDigitsBeforeDecimal[128];
2397     char   sRule[10], sSemiColumn[]=";", sBuffer[5], sNegNumber[2];
2398     char   *pStr = NULL, *pTmpStr = NULL;
2399     LCID   systemDefaultLCID;
2400     BOOL   bNegative = FALSE;
2401     enum   Operations
2402     {
2403         USE_PARAMETER,
2404         USE_LOCALEINFO,
2405         USE_SYSTEMDEFAULT,
2406         RETURN_ERROR
2407     } used_operation;
2408
2409     strncpy(sNumber, lpvalue, 128);
2410     sNumber[127] = '\0';
2411
2412     /* Make sure we have a valid input string, get the number
2413      * of digits before and after the decimal symbol, and check
2414      * if this is a negative number.
2415      */
2416     if ( OLE_GetNumberComponents(sNumber, sDigitsBeforeDecimal, sDigitsAfterDecimal, &bNegative) != -1)
2417     {
2418         nNumberDecimal = strlen(sDigitsBeforeDecimal);
2419         nDigits = strlen(sDigitsAfterDecimal);
2420     }
2421     else
2422     {
2423         SetLastError(ERROR_INVALID_PARAMETER);
2424         return 0;
2425     }
2426
2427     /* Which source will we use to format the string */
2428     used_operation = RETURN_ERROR;
2429     if (lpFormat != NULL)
2430     {
2431         if (dwflags == 0)
2432             used_operation = USE_PARAMETER;
2433     }
2434     else
2435     {
2436         if (dwflags & LOCALE_NOUSEROVERRIDE)
2437             used_operation = USE_SYSTEMDEFAULT;
2438         else
2439             used_operation = USE_LOCALEINFO;
2440     }
2441
2442     /* Load the fields we need */
2443     switch(used_operation)
2444     {
2445     case USE_LOCALEINFO:
2446         GetLocaleInfoA(locale, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2447         GetLocaleInfoA(locale, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2448         GetLocaleInfoA(locale, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2449         GetLocaleInfoA(locale, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2450         GetLocaleInfoA(locale, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2451         GetLocaleInfoA(locale, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2452         break;
2453     case USE_PARAMETER:
2454         sprintf(sNumberDigits, "%d",lpFormat->NumDigits);
2455         strcpy(sDecimalSymbol, lpFormat->lpDecimalSep);
2456         sprintf(sDigitsInGroup, "%d;0",lpFormat->Grouping);
2457         strcpy(sDigitGroupSymbol, lpFormat->lpThousandSep);
2458         sprintf(sILZero, "%d",lpFormat->LeadingZero);
2459         sprintf(sNegNumber, "%d",lpFormat->NegativeOrder);
2460         break;
2461     case USE_SYSTEMDEFAULT:
2462         systemDefaultLCID = GetSystemDefaultLCID();
2463         GetLocaleInfoA(systemDefaultLCID, LOCALE_IDIGITS, sNumberDigits, sizeof(sNumberDigits));
2464         GetLocaleInfoA(systemDefaultLCID, LOCALE_SDECIMAL, sDecimalSymbol, sizeof(sDecimalSymbol));
2465         GetLocaleInfoA(systemDefaultLCID, LOCALE_SGROUPING, sDigitsInGroup, sizeof(sDigitsInGroup));
2466         GetLocaleInfoA(systemDefaultLCID, LOCALE_STHOUSAND, sDigitGroupSymbol, sizeof(sDigitGroupSymbol));
2467         GetLocaleInfoA(systemDefaultLCID, LOCALE_ILZERO, sILZero, sizeof(sILZero));
2468         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGNUMBER, sNegNumber, sizeof(sNegNumber));
2469         break;
2470     default:
2471         SetLastError(ERROR_INVALID_PARAMETER);
2472         return 0;
2473     }
2474
2475     nNumberDigits = atoi(sNumberDigits);
2476
2477     /* Remove the ";" */
2478     i=0;
2479     j = 1;
2480     for (nCounter=0; nCounter<strlen(sDigitsInGroup); nCounter++)
2481     {
2482         if ( memcmp(sDigitsInGroup + nCounter, sSemiColumn, 1) != 0 )
2483         {
2484             memcpy(sRule + j, sDigitsInGroup + nCounter, 1);
2485             i++;
2486             j++;
2487         }
2488     }
2489     sprintf(sBuffer, "%d", i);
2490     memcpy(sRule, sBuffer, 1); /* Number of digits in the groups ( used by OLE_GetGrouping() ) */
2491     memcpy(sRule + j, "\0", 1);
2492
2493     /* First, format the digits before the decimal. */
2494     if ((nNumberDecimal>0) && (atoi(sDigitsBeforeDecimal) != 0))
2495     {
2496         /* Working on an inverted string is easier ! */
2497         OLE_InvertString(sDigitsBeforeDecimal);
2498
2499         nStep = nCounter = i = j = 0;
2500         nRuleIndex = 1;
2501         nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2502
2503         /* Here, we will loop until we reach the end of the string.
2504          * An internal counter (j) is used in order to know when to
2505          * insert the "digit group symbol".
2506          */
2507         while (nNumberDecimal > 0)
2508         {
2509             i = nCounter + nStep;
2510             memcpy(sDestination + i, sDigitsBeforeDecimal + nCounter, 1);
2511             nCounter++;
2512             j++;
2513             if (j >= nGrouping)
2514             {
2515                 j = 0;
2516                 if (nRuleIndex < sRule[0])
2517                     nRuleIndex++;
2518                 nGrouping = OLE_GetGrouping(sRule, nRuleIndex);
2519                 memcpy(sDestination + i+1, sDigitGroupSymbol, strlen(sDigitGroupSymbol));
2520                 nStep+= strlen(sDigitGroupSymbol);
2521             }
2522
2523             nNumberDecimal--;
2524         }
2525
2526         memcpy(sDestination + i+1, "\0", 1);
2527         /* Get the string in the right order ! */
2528         OLE_InvertString(sDestination);
2529      }
2530      else
2531      {
2532         nLZ = atoi(sILZero);
2533         if (nLZ != 0)
2534         {
2535             /* Use 0.xxx instead of .xxx */
2536             memcpy(sDestination, "0", 1);
2537             memcpy(sDestination+1, "\0", 1);
2538         }
2539         else
2540             memcpy(sDestination, "\0", 1);
2541
2542      }
2543
2544     /* Second, format the digits after the decimal. */
2545     j = 0;
2546     nCounter = nNumberDigits;
2547     if ( (nDigits>0) && (pStr = strstr (sNumber, ".")) )
2548     {
2549         i = strlen(sNumber) - strlen(pStr) + 1;
2550         strncpy ( sDigitsAfterDecimal, sNumber + i, nNumberDigits);
2551         j = strlen(sDigitsAfterDecimal);
2552         if (j < nNumberDigits)
2553             nCounter = nNumberDigits-j;
2554     }
2555     for (i=0;i<nCounter;i++)
2556          memcpy(sDigitsAfterDecimal+i+j, "0", 1);
2557     memcpy(sDigitsAfterDecimal + nNumberDigits, "\0", 1);
2558
2559     i = strlen(sDestination);
2560     j = strlen(sDigitsAfterDecimal);
2561     /* Finally, construct the resulting formatted string. */
2562
2563     for (nCounter=0; nCounter<i; nCounter++)
2564         memcpy(sNumber + nCounter, sDestination + nCounter, 1);
2565
2566     memcpy(sNumber + nCounter, sDecimalSymbol, strlen(sDecimalSymbol));
2567
2568     for (i=0; i<j; i++)
2569         memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), sDigitsAfterDecimal + i, 1);
2570     memcpy(sNumber + nCounter+i+strlen(sDecimalSymbol), "\0", 1);
2571
2572     /* Is it a negative number */
2573     if (bNegative == TRUE)
2574     {
2575         i = atoi(sNegNumber);
2576         pStr = sDestination;
2577         pTmpStr = sNumber;
2578         switch (i)
2579         {
2580         case 0:
2581             *pStr++ = '(';
2582             while (*sNumber != '\0')
2583                 *pStr++ =  *pTmpStr++;
2584             *pStr++ = ')';
2585             break;
2586         case 1:
2587             *pStr++ = '-';
2588             while (*pTmpStr != '\0')
2589                 *pStr++ =  *pTmpStr++;
2590             break;
2591         case 2:
2592             *pStr++ = '-';
2593             *pStr++ = ' ';
2594             while (*pTmpStr != '\0')
2595                 *pStr++ =  *pTmpStr++;
2596             break;
2597         case 3:
2598             while (*pTmpStr != '\0')
2599                 *pStr++ =  *pTmpStr++;
2600             *pStr++ = '-';
2601             break;
2602         case 4:
2603             while (*pTmpStr != '\0')
2604                 *pStr++ =  *pTmpStr++;
2605             *pStr++ = ' ';
2606             *pStr++ = '-';
2607             break;
2608         default:
2609             while (*pTmpStr != '\0')
2610                 *pStr++ =  *pTmpStr++;
2611             break;
2612         }
2613     }
2614     else
2615         strcpy(sDestination, sNumber);
2616
2617     /* If cchNumber is zero, then returns the number of bytes or characters
2618      * required to hold the formatted number string
2619      */
2620     retVal = strlen(sDestination) + 1;
2621     if (cchNumber!=0)
2622     {
2623         memcpy( lpNumberStr, sDestination, min(cchNumber, retVal) );
2624         if (cchNumber < retVal) retVal = 0;
2625     }
2626     return retVal;
2627 }
2628
2629 /**************************************************************************
2630  *              GetNumberFormatW        (KERNEL32.@)
2631  */
2632 INT WINAPI GetNumberFormatW(LCID locale, DWORD dwflags,
2633                                LPCWSTR lpvalue,  const NUMBERFMTW * lpFormat,
2634                                LPWSTR lpNumberStr, int cchNumber)
2635 {
2636  FIXME("%s: stub, no reformatting done\n",debugstr_w(lpvalue));
2637
2638  lstrcpynW( lpNumberStr, lpvalue, cchNumber );
2639  return cchNumber? strlenW( lpNumberStr ) : 0;
2640 }
2641
2642 /**************************************************************************
2643  *              GetCurrencyFormatA      (KERNEL32.@)
2644  */
2645 INT WINAPI GetCurrencyFormatA(LCID locale, DWORD dwflags,
2646                                LPCSTR lpvalue,   const CURRENCYFMTA * lpFormat,
2647                                LPSTR lpCurrencyStr, int cchCurrency)
2648 {
2649     UINT   nPosOrder, nNegOrder;
2650     INT    retVal;
2651     char   sDestination[128], sNegOrder[8], sPosOrder[8], sCurrencySymbol[8];
2652     char   *pDestination = sDestination;
2653     char   sNumberFormated[128];
2654     char   *pNumberFormated = sNumberFormated;
2655     LCID   systemDefaultLCID;
2656     BOOL   bIsPositive = FALSE, bValidFormat = FALSE;
2657     enum   Operations
2658     {
2659         USE_PARAMETER,
2660         USE_LOCALEINFO,
2661         USE_SYSTEMDEFAULT,
2662         RETURN_ERROR
2663     } used_operation;
2664
2665     NUMBERFMTA  numberFmt;
2666
2667     /* Which source will we use to format the string */
2668     used_operation = RETURN_ERROR;
2669     if (lpFormat != NULL)
2670     {
2671         if (dwflags == 0)
2672             used_operation = USE_PARAMETER;
2673     }
2674     else
2675     {
2676         if (dwflags & LOCALE_NOUSEROVERRIDE)
2677             used_operation = USE_SYSTEMDEFAULT;
2678         else
2679             used_operation = USE_LOCALEINFO;
2680     }
2681
2682     /* Load the fields we need */
2683     switch(used_operation)
2684     {
2685     case USE_LOCALEINFO:
2686         /* Specific to CURRENCYFMTA */
2687         GetLocaleInfoA(locale, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2688         GetLocaleInfoA(locale, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2689         GetLocaleInfoA(locale, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2690
2691         nPosOrder = atoi(sPosOrder);
2692         nNegOrder = atoi(sNegOrder);
2693         break;
2694     case USE_PARAMETER:
2695         /* Specific to CURRENCYFMTA */
2696         nNegOrder = lpFormat->NegativeOrder;
2697         nPosOrder = lpFormat->PositiveOrder;
2698         strcpy(sCurrencySymbol, lpFormat->lpCurrencySymbol);
2699         break;
2700     case USE_SYSTEMDEFAULT:
2701         systemDefaultLCID = GetSystemDefaultLCID();
2702         /* Specific to CURRENCYFMTA */
2703         GetLocaleInfoA(systemDefaultLCID, LOCALE_INEGCURR, sNegOrder, sizeof(sNegOrder));
2704         GetLocaleInfoA(systemDefaultLCID, LOCALE_ICURRENCY, sPosOrder, sizeof(sPosOrder));
2705         GetLocaleInfoA(systemDefaultLCID, LOCALE_SCURRENCY, sCurrencySymbol, sizeof(sCurrencySymbol));
2706
2707         nPosOrder = atoi(sPosOrder);
2708         nNegOrder = atoi(sNegOrder);
2709         break;
2710     default:
2711         SetLastError(ERROR_INVALID_PARAMETER);
2712         return 0;
2713     }
2714
2715     /* Construct a temporary number format structure */
2716     if (lpFormat != NULL)
2717     {
2718         numberFmt.NumDigits     = lpFormat->NumDigits;
2719         numberFmt.LeadingZero   = lpFormat->LeadingZero;
2720         numberFmt.Grouping      = lpFormat->Grouping;
2721         numberFmt.NegativeOrder = 0;
2722         numberFmt.lpDecimalSep = lpFormat->lpDecimalSep;
2723         numberFmt.lpThousandSep = lpFormat->lpThousandSep;
2724         bValidFormat = TRUE;
2725     }
2726
2727     /* Make a call to GetNumberFormatA() */
2728     if (*lpvalue == '-')
2729     {
2730         bIsPositive = FALSE;
2731         retVal = GetNumberFormatA(locale,0,lpvalue+1,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2732     }
2733     else
2734     {
2735         bIsPositive = TRUE;
2736         retVal = GetNumberFormatA(locale,0,lpvalue,(bValidFormat)?&numberFmt:NULL,pNumberFormated,128);
2737     }
2738
2739     if (retVal == 0)
2740         return 0;
2741
2742     /* construct the formatted string */
2743     if (bIsPositive)
2744     {
2745         switch (nPosOrder)
2746         {
2747         case 0:   /* Prefix, no separation */
2748             strcpy (pDestination, sCurrencySymbol);
2749             strcat (pDestination, pNumberFormated);
2750             break;
2751         case 1:   /* Suffix, no separation */
2752             strcpy (pDestination, pNumberFormated);
2753             strcat (pDestination, sCurrencySymbol);
2754             break;
2755         case 2:   /* Prefix, 1 char separation */
2756             strcpy (pDestination, sCurrencySymbol);
2757             strcat (pDestination, " ");
2758             strcat (pDestination, pNumberFormated);
2759             break;
2760         case 3:   /* Suffix, 1 char separation */
2761             strcpy (pDestination, pNumberFormated);
2762             strcat (pDestination, " ");
2763             strcat (pDestination, sCurrencySymbol);
2764             break;
2765         default:
2766             SetLastError(ERROR_INVALID_PARAMETER);
2767             return 0;
2768         }
2769     }
2770     else  /* negative number */
2771     {
2772         switch (nNegOrder)
2773         {
2774         case 0:   /* format: ($1.1) */
2775             strcpy (pDestination, "(");
2776             strcat (pDestination, sCurrencySymbol);
2777             strcat (pDestination, pNumberFormated);
2778             strcat (pDestination, ")");
2779             break;
2780         case 1:   /* format: -$1.1 */
2781             strcpy (pDestination, "-");
2782             strcat (pDestination, sCurrencySymbol);
2783             strcat (pDestination, pNumberFormated);
2784             break;
2785         case 2:   /* format: $-1.1 */
2786             strcpy (pDestination, sCurrencySymbol);
2787             strcat (pDestination, "-");
2788             strcat (pDestination, pNumberFormated);
2789             break;
2790         case 3:   /* format: $1.1- */
2791             strcpy (pDestination, sCurrencySymbol);
2792             strcat (pDestination, pNumberFormated);
2793             strcat (pDestination, "-");
2794             break;
2795         case 4:   /* format: (1.1$) */
2796             strcpy (pDestination, "(");
2797             strcat (pDestination, pNumberFormated);
2798             strcat (pDestination, sCurrencySymbol);
2799             strcat (pDestination, ")");
2800             break;
2801         case 5:   /* format: -1.1$ */
2802             strcpy (pDestination, "-");
2803             strcat (pDestination, pNumberFormated);
2804             strcat (pDestination, sCurrencySymbol);
2805             break;
2806         case 6:   /* format: 1.1-$ */
2807             strcpy (pDestination, pNumberFormated);
2808             strcat (pDestination, "-");
2809             strcat (pDestination, sCurrencySymbol);
2810             break;
2811         case 7:   /* format: 1.1$- */
2812             strcpy (pDestination, pNumberFormated);
2813             strcat (pDestination, sCurrencySymbol);
2814             strcat (pDestination, "-");
2815             break;
2816         case 8:   /* format: -1.1 $ */
2817             strcpy (pDestination, "-");
2818             strcat (pDestination, pNumberFormated);
2819             strcat (pDestination, " ");
2820             strcat (pDestination, sCurrencySymbol);
2821             break;
2822         case 9:   /* format: -$ 1.1 */
2823             strcpy (pDestination, "-");
2824             strcat (pDestination, sCurrencySymbol);
2825             strcat (pDestination, " ");
2826             strcat (pDestination, pNumberFormated);
2827             break;
2828         case 10:   /* format: 1.1 $- */
2829             strcpy (pDestination, pNumberFormated);
2830             strcat (pDestination, " ");
2831             strcat (pDestination, sCurrencySymbol);
2832             strcat (pDestination, "-");
2833             break;
2834         case 11:   /* format: $ 1.1- */
2835             strcpy (pDestination, sCurrencySymbol);
2836             strcat (pDestination, " ");
2837             strcat (pDestination, pNumberFormated);
2838             strcat (pDestination, "-");
2839             break;
2840         case 12:   /* format: $ -1.1 */
2841             strcpy (pDestination, sCurrencySymbol);
2842             strcat (pDestination, " ");
2843             strcat (pDestination, "-");
2844             strcat (pDestination, pNumberFormated);
2845             break;
2846         case 13:   /* format: 1.1- $ */
2847             strcpy (pDestination, pNumberFormated);
2848             strcat (pDestination, "-");
2849             strcat (pDestination, " ");
2850             strcat (pDestination, sCurrencySymbol);
2851             break;
2852         case 14:   /* format: ($ 1.1) */
2853             strcpy (pDestination, "(");
2854             strcat (pDestination, sCurrencySymbol);
2855             strcat (pDestination, " ");
2856             strcat (pDestination, pNumberFormated);
2857             strcat (pDestination, ")");
2858             break;
2859         case 15:   /* format: (1.1 $) */
2860             strcpy (pDestination, "(");
2861             strcat (pDestination, pNumberFormated);
2862             strcat (pDestination, " ");
2863             strcat (pDestination, sCurrencySymbol);
2864             strcat (pDestination, ")");
2865             break;
2866         default:
2867             SetLastError(ERROR_INVALID_PARAMETER);
2868             return 0;
2869         }
2870     }
2871
2872     retVal = strlen(pDestination) + 1;
2873
2874     if (cchCurrency)
2875     {
2876         memcpy( lpCurrencyStr, pDestination, min(cchCurrency, retVal) );
2877         if (cchCurrency < retVal) retVal = 0;
2878     }
2879     return retVal;
2880 }
2881
2882 /**************************************************************************
2883  *              GetCurrencyFormatW      (KERNEL32.@)
2884  */
2885 INT WINAPI GetCurrencyFormatW(LCID locale, DWORD dwflags,
2886                                LPCWSTR lpvalue,   const CURRENCYFMTW * lpFormat,
2887                                LPWSTR lpCurrencyStr, int cchCurrency)
2888 {
2889     FIXME("This API function is NOT implemented !\n");
2890     return 0;
2891 }
2892
2893 /******************************************************************************
2894  *              OLE2NLS_CheckLocale     [intern]
2895  */
2896 static LCID OLE2NLS_CheckLocale (LCID locale)
2897 {
2898         if (!locale)
2899         { locale = LOCALE_SYSTEM_DEFAULT;
2900         }
2901
2902         if (locale == LOCALE_SYSTEM_DEFAULT)
2903         { return GetSystemDefaultLCID();
2904         }
2905         else if (locale == LOCALE_USER_DEFAULT)
2906         { return GetUserDefaultLCID();
2907         }
2908         else
2909         { return locale;
2910         }
2911 }
2912 /******************************************************************************
2913  *              GetTimeFormatA  [KERNEL32.@]
2914  * Makes an ASCII string of the time
2915  *
2916  * Formats date according to format,  or locale default if format is
2917  * NULL. The format consists of literal characters and fields as follows:
2918  *
2919  * h  hours with no leading zero (12-hour)
2920  * hh hours with full two digits
2921  * H  hours with no leading zero (24-hour)
2922  * HH hours with full two digits
2923  * m  minutes with no leading zero
2924  * mm minutes with full two digits
2925  * s  seconds with no leading zero
2926  * ss seconds with full two digits
2927  * t  time marker (A or P)
2928  * tt time marker (AM, PM)
2929  *
2930  */
2931 INT WINAPI
2932 GetTimeFormatA(LCID locale,        /* [in]  */
2933                DWORD flags,        /* [in]  */
2934                const SYSTEMTIME* xtime, /* [in]  */
2935                LPCSTR format,      /* [in]  */
2936                LPSTR timestr,      /* [out] */
2937                INT timelen         /* [in]  */)
2938 { char format_buf[40];
2939   LPCSTR thisformat;
2940   SYSTEMTIME t;
2941   const SYSTEMTIME* thistime;
2942   LCID thislocale=0;
2943   DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
2944   INT ret;
2945
2946   TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,xtime,format,timestr,timelen);
2947
2948   thislocale = OLE2NLS_CheckLocale ( locale );
2949
2950   if (format == NULL)
2951   { if (flags & LOCALE_NOUSEROVERRIDE)  /* use system default */
2952     { thislocale = GetSystemDefaultLCID();
2953     }
2954     GetLocaleInfoA(thislocale, thisflags, format_buf, sizeof(format_buf));
2955     thisformat = format_buf;
2956   }
2957   else
2958   { thisformat = format;
2959   }
2960
2961   if (xtime == NULL) /* NULL means use the current local time */
2962   { GetLocalTime(&t);
2963     thistime = &t;
2964   }
2965   else
2966   { thistime = xtime;
2967   /* Check that hour,min and sec is in range */
2968   }
2969   ret = OLE_GetFormatA(thislocale, thisflags, flags, thistime, thisformat,
2970                          timestr, timelen);
2971   return ret;
2972 }
2973
2974
2975 /******************************************************************************
2976  *              GetTimeFormatW  [KERNEL32.@]
2977  * Makes a Unicode string of the time
2978  */
2979 INT WINAPI
2980 GetTimeFormatW(LCID locale,        /* [in]  */
2981                DWORD flags,        /* [in]  */
2982                const SYSTEMTIME* xtime, /* [in]  */
2983                LPCWSTR format,     /* [in]  */
2984                LPWSTR timestr,     /* [out] */
2985                INT timelen         /* [in]  */)
2986 {       WCHAR format_buf[40];
2987         LPCWSTR thisformat;
2988         SYSTEMTIME t;
2989         const SYSTEMTIME* thistime;
2990         LCID thislocale=0;
2991         DWORD thisflags=LOCALE_STIMEFORMAT; /* standard timeformat */
2992         INT ret;
2993
2994         TRACE("GetTimeFormat(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",locale,flags,
2995         xtime,debugstr_w(format),timestr,timelen);
2996
2997         thislocale = OLE2NLS_CheckLocale ( locale );
2998
2999         if (format == NULL)
3000         { if (flags & LOCALE_NOUSEROVERRIDE)  /* use system default */
3001           { thislocale = GetSystemDefaultLCID();
3002           }
3003           GetLocaleInfoW(thislocale, thisflags, format_buf, 40);
3004           thisformat = format_buf;
3005         }
3006         else
3007         { thisformat = format;
3008         }
3009
3010         if (xtime == NULL) /* NULL means use the current local time */
3011         { GetLocalTime(&t);
3012           thistime = &t;
3013         }
3014         else
3015         { thistime = xtime;
3016         }
3017
3018         ret = OLE_GetFormatW(thislocale, thisflags, flags, thistime, thisformat,
3019                          timestr, timelen);
3020         return ret;
3021 }
3022
3023 /******************************************************************************
3024  *              EnumCalendarInfoA       [KERNEL32.@]
3025  */
3026 BOOL WINAPI EnumCalendarInfoA(
3027         CALINFO_ENUMPROCA calinfoproc,LCID locale,CALID calendar,CALTYPE caltype
3028 ) {
3029         FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
3030         return FALSE;
3031 }