Re-implement and optimise the NLS formatting functions (many fixes).
[wine] / dlls / kernel / lcformat.c
1 /*
2  * Locale-dependent format handling
3  *
4  * Copyright 1995 Martin von Loewis
5  * Copyright 1998 David Lee Lambert
6  * Copyright 2000 Julio César Gázquez
7  * Copyright 2003 Jon Griffiths
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36 #include "winreg.h"
37 #include "winternl.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(nls);
40
41 #define DATE_DATEVARSONLY 0x0100  /* only date stuff: yMdg */
42 #define TIME_TIMEVARSONLY 0x0200  /* only time stuff: hHmst */
43
44 /* Since calculating the formatting data for each locale is time-consuming,
45  * we get the format data for each locale only once and cache it in memory.
46  * We cache both the system default and user overridden data, after converting
47  * them into the formats that the functions here expect. Since these functions
48  * will typically be called with only a small number of the total locales
49  * installed, the memory overhead is minimal while the speedup is significant.
50  *
51  * Our cache takes the form of a singly linked list, whose node is below:
52  */
53 #define NLS_NUM_CACHED_STRINGS 45
54
55 typedef struct _NLS_FORMAT_NODE
56 {
57   LCID  lcid;         /* Locale Id */
58   DWORD dwFlags;      /* 0 or LOCALE_NOUSEROVERRIDE */
59   DWORD dwCodePage;   /* Default code page (if LOCALE_USE_ANSI_CP not given) */
60   NUMBERFMTW   fmt;   /* Default format for numbers */
61   CURRENCYFMTW cyfmt; /* Default format for currencies */
62   LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
63   WCHAR szShortAM[2]; /* Short 'AM' marker */
64   WCHAR szShortPM[2]; /* Short 'PM' marker */
65   struct _NLS_FORMAT_NODE *next;
66 } NLS_FORMAT_NODE;
67
68 /* Macros to get particular data strings from a format node */
69 #define GetNegative(fmt)  fmt->lppszStrings[0]
70 #define GetLongDate(fmt)  fmt->lppszStrings[1]
71 #define GetShortDate(fmt) fmt->lppszStrings[2]
72 #define GetTime(fmt)      fmt->lppszStrings[3]
73 #define GetAM(fmt)        fmt->lppszStrings[42]
74 #define GetPM(fmt)        fmt->lppszStrings[43]
75 #define GetYearMonth(fmt) fmt->lppszStrings[44]
76
77 #define GetLongDay(fmt,day)    fmt->lppszStrings[4 + day]
78 #define GetShortDay(fmt,day)   fmt->lppszStrings[11 + day]
79 #define GetLongMonth(fmt,mth)  fmt->lppszStrings[18 + mth]
80 #define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
81
82 /* Write access to the cache is protected by this critical section */
83 static CRITICAL_SECTION NLS_FormatsCS;
84 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
85 {
86     0, 0, &NLS_FormatsCS,
87     { &NLS_FormatsCS_debug.ProcessLocksList,
88       &NLS_FormatsCS_debug.ProcessLocksList },
89       0, 0, { 0, (DWORD)(__FILE__ ": NLS_Formats") }
90 };
91 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
92
93 /**************************************************************************
94  * NLS_GetLocaleNumber <internal>
95  *
96  * Get a numeric locale format value.
97  */
98 static WINAPI DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
99 {
100   WCHAR szBuff[80];
101   DWORD dwVal = 0;
102
103   szBuff[0] = '\0';
104   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
105
106   if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
107     dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
108   else
109   {
110     const WCHAR* iter = szBuff;
111     dwVal = 0;
112     while(*iter >= '0' && *iter <= '9')
113       dwVal = dwVal * 10 + (*iter++ - '0');
114   }
115   return dwVal;
116 }
117
118 /**************************************************************************
119  * NLS_GetLocaleString <internal>
120  *
121  * Get a string locale format value.
122  */
123 static WINAPI WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
124 {
125   WCHAR szBuff[80], *str;
126   DWORD dwLen;
127
128   szBuff[0] = '\0';
129   GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
130   dwLen = strlenW(szBuff) + 1;
131   str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
132   if (str)
133     memcpy(str, szBuff, dwLen * sizeof(WCHAR));
134   return str;
135 }
136
137 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
138   TRACE( #type ": %ld (%08lx)\n", (DWORD)num, (DWORD)num)
139
140 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
141   TRACE( #type ": '%s'\n", debugstr_w(str))
142
143 /**************************************************************************
144  * NLS_GetFormats <internal>
145  *
146  * Calculate (and cache) the number formats for a locale.
147  */
148 static WINAPI const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
149 {
150   /* GetLocaleInfo() identifiers for cached formatting strings */
151   static const USHORT NLS_LocaleIndices[] = {
152     LOCALE_SNEGATIVESIGN,
153     LOCALE_SLONGDATE,   LOCALE_SSHORTDATE,
154     LOCALE_STIMEFORMAT,
155     LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
156     LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
157     LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
158     LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
159     LOCALE_SABBREVDAYNAME7,
160     LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
161     LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
162     LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
163     LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
164     LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
165     LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
166     LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
167     LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
168     LOCALE_S1159, LOCALE_S2359,
169     LOCALE_SYEARMONTH
170   };
171   static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
172   NLS_FORMAT_NODE *node = NLS_CachedFormats;
173
174   dwFlags &= LOCALE_NOUSEROVERRIDE;
175
176   TRACE("(0x%04lx,0x%08lx)\n", lcid, dwFlags);
177
178   /* See if we have already cached the locales number format */
179   while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
180     node = node->next;
181
182   if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
183   {
184     NLS_FORMAT_NODE *new_node;
185     DWORD i;
186
187     TRACE("Creating new cache entry\n");
188
189     if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
190       return NULL;
191
192     GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
193
194     /* Number Format */
195     new_node->lcid = lcid;
196     new_node->dwFlags = dwFlags;
197     new_node->next = NULL;
198
199     GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
200     GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
201     GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
202
203     GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
204     if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
205     {
206       WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
207            new_node->fmt.Grouping);
208       new_node->fmt.Grouping = 0;
209     }
210
211     GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
212     GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
213
214     /* Currency Format */
215     new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
216     new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
217
218     GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
219
220     if (new_node->cyfmt.Grouping > 9)
221     {
222       WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
223            new_node->cyfmt.Grouping);
224       new_node->cyfmt.Grouping = 0;
225     }
226
227     GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
228     if (new_node->cyfmt.NegativeOrder > 15)
229     {
230       WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
231            new_node->cyfmt.NegativeOrder);
232       new_node->cyfmt.NegativeOrder = 0;
233     }
234     GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
235     if (new_node->cyfmt.PositiveOrder > 3)
236     {
237       WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
238            new_node->cyfmt.PositiveOrder);
239       new_node->cyfmt.PositiveOrder = 0;
240     }
241     GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
242     GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
243     GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
244
245     /* Date/Time Format info, negative character, etc */
246     for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
247     {
248       GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
249     }
250     new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
251     new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
252
253     /* Now add the computed format to the cache */
254     RtlEnterCriticalSection(&NLS_FormatsCS);
255
256     /* Search again: We may have raced to add the node */
257     node = NLS_CachedFormats;
258     while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
259       node = node->next;
260
261     if (!node)
262     {
263       node = NLS_CachedFormats = new_node; /* Empty list */
264       new_node = NULL;
265     }
266     else if (node->lcid != lcid || node->dwFlags != dwFlags)
267     {
268       node->next = new_node; /* Not in the list, add to end */
269       node = new_node;
270       new_node = NULL;
271     }
272
273     RtlLeaveCriticalSection(&NLS_FormatsCS);
274
275     if (new_node)
276     {
277       /* We raced and lost: The node was already added by another thread.
278        * node points to the currently cached node, so free new_node.
279        */
280       for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
281         HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
282       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
283       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
284       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
285       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
286       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
287       HeapFree(GetProcessHeap(), 0, new_node);
288     }
289   }
290   return node;
291 }
292
293 /**************************************************************************
294  * NLS_IsUnicodeOnlyLcid <internal>
295  *
296  * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
297  */
298 BOOL WINAPI NLS_IsUnicodeOnlyLcid(LCID lcid)
299 {
300   switch (PRIMARYLANGID(lcid))
301   {
302   case LANG_ARMENIAN:
303   case LANG_DIVEHI:
304   case LANG_GEORGIAN:
305   case LANG_GUJARATI:
306   case LANG_HINDI:
307   case LANG_KANNADA:
308   case LANG_KONKANI:
309   case LANG_MARATHI:
310   case LANG_PUNJABI:
311   case LANG_SANSKRIT:
312     TRACE("lcid 0x%08lx: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
313     return TRUE;
314   default:
315     return FALSE;
316   }
317 }
318
319 /*
320  * Formatting of dates, times, numbers and currencies.
321  */
322
323 #define IsLiteralMarker(p) (p == '\'')
324 #define IsDateFmtChar(p)   (p == 'd'||p == 'M'||p == 'y'||p == 'g')
325 #define IsTimeFmtChar(p)   (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
326
327 /* Only the following flags can be given if a date/time format is specified */
328 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY|LOCALE_NOUSEROVERRIDE)
329 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
330                            TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
331                            TIME_NOTIMEMARKER|LOCALE_NOUSEROVERRIDE)
332
333 /******************************************************************************
334  * NLS_GetDateTimeFormatW <internal>
335  *
336  * Performs the formatting for GetDateFormatW/GetTimeFormatW.
337  *
338  * FIXME
339  * DATE_USE_ALT_CALENDAR           - Requires GetCalendarInfo to work first.
340  * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
341  */
342 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
343                                   const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
344                                   LPWSTR lpStr, INT cchOut)
345 {
346   const NLS_FORMAT_NODE *node;
347   SYSTEMTIME st;
348   INT cchWritten = 0;
349   INT lastFormatPos = 0;
350   BOOL bSkipping = FALSE; /* Skipping text around marker? */
351
352   /* Verify our arguments */
353   if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
354   {
355 NLS_GetDateTimeFormatW_InvalidParameter:
356     SetLastError(ERROR_INVALID_PARAMETER);
357     return 0;
358   }
359
360   if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
361   {
362     if (lpFormat &&
363         ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
364          (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
365     {
366 NLS_GetDateTimeFormatW_InvalidFlags:
367       SetLastError(ERROR_INVALID_FLAGS);
368       return 0;
369     }
370
371     if (dwFlags & DATE_DATEVARSONLY)
372     {
373       if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
374         goto NLS_GetDateTimeFormatW_InvalidFlags;
375       else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
376         FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
377
378       switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
379       {
380       case 0:
381         break;
382       case DATE_SHORTDATE:
383       case DATE_LONGDATE:
384       case DATE_YEARMONTH:
385         if (lpFormat)
386           goto NLS_GetDateTimeFormatW_InvalidFlags;
387         break;
388       default:
389         goto NLS_GetDateTimeFormatW_InvalidFlags;
390       }
391     }
392   }
393
394   if (!lpFormat)
395   {
396     /* Use the appropriate default format */
397     if (dwFlags & DATE_DATEVARSONLY)
398     {
399       if (dwFlags & DATE_YEARMONTH)
400         lpFormat = GetYearMonth(node);
401       else if (dwFlags & DATE_LONGDATE)
402         lpFormat = GetLongDate(node);
403       else
404         lpFormat = GetShortDate(node);
405     }
406     else
407       lpFormat = GetTime(node);
408   }
409
410   if (!lpTime)
411   {
412     GetSystemTime(&st); /* Default to current time */
413     lpTime = &st;
414   }
415   else
416   {
417     if (dwFlags & DATE_DATEVARSONLY)
418     {
419       FILETIME ftTmp;
420
421       /* Verify the date and correct the D.O.W. if needed */
422       memset(&st, 0, sizeof(st));
423       st.wYear = lpTime->wYear;
424       st.wMonth = lpTime->wMonth;
425       st.wDay = lpTime->wDay;
426
427       if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
428         goto NLS_GetDateTimeFormatW_InvalidParameter;
429
430       FileTimeToSystemTime(&ftTmp, &st);
431       lpTime = &st;
432     }
433
434     if (dwFlags & TIME_TIMEVARSONLY)
435     {
436       /* Verify the time */
437       if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
438         goto NLS_GetDateTimeFormatW_InvalidParameter;
439     }
440   }
441
442   /* Format the output */
443   while (*lpFormat)
444   {
445     if (IsLiteralMarker(*lpFormat))
446     {
447       /* Start of a literal string */
448       lpFormat++;
449
450       /* Loop until the end of the literal marker or end of the string */
451       while (*lpFormat)
452       {
453         if (IsLiteralMarker(*lpFormat))
454         {
455           lpFormat++;
456           if (!IsLiteralMarker(*lpFormat))
457             break; /* Terminating literal marker */
458         }
459
460         if (!cchOut)
461           cchWritten++;   /* Count size only */
462         else if (cchWritten >= cchOut)
463           goto NLS_GetDateTimeFormatW_Overrun;
464         else if (!bSkipping)
465         {
466           lpStr[cchWritten] = *lpFormat;
467           cchWritten++;
468         }
469         lpFormat++;
470       }
471     }
472     else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
473              (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
474     {
475       char  buffA[32];
476       WCHAR buff[32], fmtChar;
477       LPCWSTR szAdd = NULL;
478       DWORD dwVal = 0;
479       int   count = 0, dwLen;
480
481       bSkipping = FALSE;
482
483       fmtChar = *lpFormat;
484       while (*lpFormat == fmtChar)
485       {
486         count++;
487         lpFormat++;
488       }
489       buff[0] = '\0';
490
491       switch(fmtChar)
492       {
493       case 'd':
494         if (count >= 4)
495           szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
496         else if (count == 3)
497           szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
498         else
499         {
500           dwVal = lpTime->wDay;
501           szAdd = buff;
502         }
503         break;
504
505       case 'M':
506         if (count >= 4)
507           szAdd = GetLongMonth(node, lpTime->wMonth - 1);
508         else if (count == 3)
509           szAdd = GetShortMonth(node, lpTime->wMonth - 1);
510         else
511         {
512           dwVal = lpTime->wMonth;
513           szAdd = buff;
514         }
515         break;
516
517       case 'y':
518         if (count >= 4)
519         {
520           count = 4;
521           dwVal = lpTime->wYear;
522         }
523         else
524         {
525           count = count > 2 ? 2 : count;
526           dwVal = lpTime->wYear % 100;
527         }
528         szAdd = buff;
529         break;
530
531       case 'g':
532         if (count == 2)
533         {
534           /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
535            *        When it is fixed, this string should be cached in 'node'.
536            */
537           FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
538           buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
539         }
540         else
541         {
542           buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
543         }
544         szAdd = buff;
545         break;
546
547       case 'h':
548         if (!(dwFlags & TIME_FORCE24HOURFORMAT))
549         {
550           count = count > 2 ? 2 : count;
551           dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
552           szAdd = buff;
553           break;
554         }
555         /* .. fall through if we are forced to output in 24 hour format */
556
557       case 'H':
558         count = count > 2 ? 2 : count;
559         dwVal = lpTime->wHour;
560         szAdd = buff;
561         break;
562
563       case 'm':
564         if (dwFlags & TIME_NOMINUTESORSECONDS)
565         {
566           cchWritten = lastFormatPos; /* Skip */
567           bSkipping = TRUE;
568         }
569         else
570         {
571           count = count > 2 ? 2 : count;
572           dwVal = lpTime->wMinute;
573           szAdd = buff;
574         }
575         break;
576
577       case 's':
578         if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
579         {
580           cchWritten = lastFormatPos; /* Skip */
581           bSkipping = TRUE;
582         }
583         else
584         {
585           count = count > 2 ? 2 : count;
586           dwVal = lpTime->wSecond;
587           szAdd = buff;
588         }
589         break;
590
591       case 't':
592         if (dwFlags & TIME_NOTIMEMARKER)
593         {
594           cchWritten = lastFormatPos; /* Skip */
595           bSkipping = TRUE;
596         }
597         else
598         {
599           if (count == 1)
600             szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
601           else
602             szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
603         }
604         break;
605       }
606
607       if (szAdd == buff && buff[0] == '\0')
608       {
609         /* We have a numeric value to add */
610         sprintf(buffA, "%.*ld", count, dwVal);
611         MultiByteToWideChar(CP_ACP, 0, buffA, -1, buff, sizeof(buff)/sizeof(WCHAR));
612       }
613
614       dwLen = szAdd ? strlenW(szAdd) : 0;
615
616       if (cchOut && dwLen)
617       {
618         if (cchWritten + dwLen < cchOut)
619           memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
620         else
621         {
622           memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
623           goto NLS_GetDateTimeFormatW_Overrun;
624         }
625       }
626       cchWritten += dwLen;
627       lastFormatPos = cchWritten; /* Save position of last output format text */
628     }
629     else
630     {
631       /* Literal character */
632       if (!cchOut)
633         cchWritten++;   /* Count size only */
634       else if (cchWritten >= cchOut)
635         goto NLS_GetDateTimeFormatW_Overrun;
636       else if (!bSkipping || *lpFormat == ' ')
637       {
638         lpStr[cchWritten] = *lpFormat;
639         cchWritten++;
640       }
641       lpFormat++;
642     }
643   }
644
645   /* Final string terminator and sanity check */
646   if (cchOut)
647   {
648    if (cchWritten >= cchOut)
649      goto NLS_GetDateTimeFormatW_Overrun;
650    else
651      lpStr[cchWritten] = '\0';
652   }
653   cchWritten++; /* Include terminating NUL */
654
655   TRACE("returning length=%d, ouput='%s'\n", cchWritten, debugstr_w(lpStr));
656   return cchWritten;
657
658 NLS_GetDateTimeFormatW_Overrun:
659   TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
660   SetLastError(ERROR_INSUFFICIENT_BUFFER);
661   return 0;
662 }
663
664 /******************************************************************************
665  * NLS_GetDateTimeFormatA <internal>
666  *
667  * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
668  */
669 static INT WINAPI NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
670                                          const SYSTEMTIME* lpTime,
671                                          LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
672 {
673   DWORD cp = CP_ACP;
674   WCHAR szFormat[128], szOut[128];
675   INT iRet;
676
677   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
678         debugstr_a(lpFormat), lpStr, cchOut);
679
680   if (NLS_IsUnicodeOnlyLcid(lcid))
681   {
682 GetDateTimeFormatA_InvalidParameter:
683     SetLastError(ERROR_INVALID_PARAMETER);
684     return 0;
685   }
686
687   if (!(dwFlags & LOCALE_USE_CP_ACP))
688   {
689     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
690     if (!node)
691       goto GetDateTimeFormatA_InvalidParameter;
692     cp = node->dwCodePage;
693   }
694
695   if (lpFormat)
696     MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
697
698   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
699     cchOut = sizeof(szOut)/sizeof(WCHAR);
700
701   szOut[0] = '\0';
702
703   iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
704                                 lpStr ? szOut : NULL, cchOut);
705
706   if (lpStr)
707   {
708     if (szOut[0])
709       WideCharToMultiByte(cp, 0, szOut, -1, lpStr, cchOut, 0, 0);
710     else if (cchOut && iRet)
711       *lpStr = '\0';
712   }
713   return iRet;
714 }
715
716 /******************************************************************************
717  * GetDateFormatA [KERNEL32.@]
718  *
719  * Format a date for a given locale.
720  *
721  * PARAMS
722  *  lcid      [I] Locale to format for
723  *  dwFlags   [I] LOCALE_ and DATE_ flags from "winnls.h"
724  *  lpTime    [I] Date to format
725  *  lpFormat  [I] Format string, or NULL to use the system defaults
726  *  lpDateStr [O] Destination for formatted string
727  *  cchOut    [I] Size of lpDateStr, or 0 to calculate the resulting size
728  *
729  * NOTES
730  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
731  *    details returned by GetLocaleInfoA() and modified by dwFlags.
732  *  - lpFormat is a string of characters and formatting tokens. Any characters
733  *    in the string are copied verbatim to lpDateStr, with tokens being replaced
734  *    by the date values they represent.
735  *  - The following tokens have special meanings in a date format string:
736  *|  Token  Meaning
737  *|  -----  -------
738  *|  d      Single digit day of the month (no leading 0)
739  *|  dd     Double digit day of the month
740  *|  ddd    Short name for the day of the week
741  *|  dddd   Long name for the day of the week
742  *|  M      Single digit month of the year (no leading 0)
743  *|  MM     Double digit month of the year
744  *|  MMM    Short name for the month of the year
745  *|  MMMM   Long name for the month of the year
746  *|  y      Double digit year number (no leading 0)
747  *|  yy     Double digit year number
748  *|  yyyy   Four digit year number
749  *|  gg     Era string, for example 'AD'.
750  *  - To output any literal character that could be misidentified as a token,
751  *    enclose it in single quotes.
752  *  - The Ascii version of this function fails if lcid is Unicode only.
753  *
754  * RETURNS
755  *  Success: The number of character written to lpDateStr, or that would
756  *           have been written, if cchOut is 0.
757  *  Failure: 0. Use GetLastError() to determine the cause.
758  */
759 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
760                            LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
761 {
762   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
763         debugstr_a(lpFormat), lpDateStr, cchOut);
764
765   return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
766                                 lpFormat, lpDateStr, cchOut);
767 }
768
769
770 /******************************************************************************
771  * GetDateFormatW       [KERNEL32.@]
772  *
773  * See GetDateFormatA.
774  */
775 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
776                           LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
777 {
778   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
779         debugstr_w(lpFormat), lpDateStr, cchOut);
780
781   return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
782                                 lpFormat, lpDateStr, cchOut);
783 }
784
785 /******************************************************************************
786  *              GetTimeFormatA  [KERNEL32.@]
787  *
788  * Format a time for a given locale.
789  *
790  * PARAMS
791  *  lcid      [I] Locale to format for
792  *  dwFlags   [I] LOCALE_ and TIME_ flags from "winnls.h"
793  *  lpTime    [I] Time to format
794  *  lpFormat  [I] Formatting overrides
795  *  lpTimeStr [O] Destination for formatted string
796  *  cchOut    [I] Size of lpTimeStr, or 0 to calculate the resulting size
797  *
798  * NOTES
799  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
800  *    details returned by GetLocaleInfoA() and modified by dwFlags.
801  *  - lpFormat is a string of characters and formatting tokens. Any characters
802  *    in the string are copied verbatim to lpTimeStr, with tokens being replaced
803  *    by the time values they represent.
804  *  - The following tokens have special meanings in a time format string:
805  *|  Token  Meaning
806  *|  -----  -------
807  *|  h      Hours with no leading zero (12-hour clock)
808  *|  hh     Hours with full two digits (12-hour clock)
809  *|  H      Hours with no leading zero (24-hour clock)
810  *|  HH     Hours with full two digits (24-hour clock)
811  *|  m      Minutes with no leading zero
812  *|  mm     Minutes with full two digits
813  *|  s      Seconds with no leading zero
814  *|  ss     Seconds with full two digits
815  *|  t      Short time marker (e.g. "A" or "P")
816  *|  tt     Long time marker (e.g. "AM", "PM")
817  *  - To output any literal character that could be misidentified as a token,
818  *    enclose it in single quotes.
819  *  - The Ascii version of this function fails if lcid is Unicode only.
820  *
821  * RETURNS
822  *  Success: The number of character written to lpTimeStr, or that would
823  *           have been written, if cchOut is 0.
824  *  Failure: 0. Use GetLastError() to determine the cause.
825  */
826 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
827                           LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
828 {
829   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
830         debugstr_a(lpFormat), lpTimeStr, cchOut);
831
832   return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
833                                 lpFormat, lpTimeStr, cchOut);
834 }
835
836 /******************************************************************************
837  *              GetTimeFormatW  [KERNEL32.@]
838  *
839  * See GetTimeFormatA.
840  */
841 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
842                           LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
843 {
844   TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
845         debugstr_w(lpFormat), lpTimeStr, cchOut);
846
847   return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
848                                 lpFormat, lpTimeStr, cchOut);
849 }
850
851 /**************************************************************************
852  *              GetNumberFormatA        (KERNEL32.@)
853  *
854  * Format a number string for a given locale.
855  *
856  * PARAMS
857  *  lcid        [I] Locale to format for
858  *  dwFlags     [I] LOCALE_ flags from "winnls.h"
859  *  lpszValue   [I] String to format
860  *  lpFormat    [I] Formatting overrides
861  *  lpNumberStr [O] Destination for formatted string
862  *  cchOut      [I] Size of lpNumberStr, or 0 to calculate the resulting size
863  *
864  * NOTES
865  *  - lpszValue can contain only '0' - '9', '-' and '.'.
866  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
867  *    be formatted according to the format details returned by GetLocaleInfoA().
868  *  - This function rounds the number string if the number of decimals exceeds the
869  *    locales normal number of decimal places.
870  *  - If cchOut is 0, this function does not write to lpNumberStr.
871  *  - The Ascii version of this function fails if lcid is Unicode only.
872  *
873  * RETURNS
874  *  Success: The number of character written to lpNumberStr, or that would
875  *           have been written, if cchOut is 0.
876  *  Failure: 0. Use GetLastError() to determine the cause.
877  */
878 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
879                             LPCSTR lpszValue,  const NUMBERFMTA *lpFormat,
880                             LPSTR lpNumberStr, int cchOut)
881 {
882   DWORD cp = CP_ACP;
883   WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
884   NUMBERFMTW fmt;
885   const NUMBERFMTW *pfmt = NULL;
886   INT iRet;
887
888   TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
889         lpFormat, lpNumberStr, cchOut);
890
891   if (NLS_IsUnicodeOnlyLcid(lcid))
892   {
893 GetNumberFormatA_InvalidParameter:
894     SetLastError(ERROR_INVALID_PARAMETER);
895     return 0;
896   }
897
898   if (!(dwFlags & LOCALE_USE_CP_ACP))
899   {
900     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
901     if (!node)
902       goto GetNumberFormatA_InvalidParameter;
903     cp = node->dwCodePage;
904   }
905
906   if (lpFormat)
907   {
908     memcpy(&fmt, lpFormat, sizeof(fmt));
909     pfmt = &fmt;
910     if (lpFormat->lpDecimalSep)
911     {
912       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
913       fmt.lpDecimalSep = szDec;
914     }
915     if (lpFormat->lpThousandSep)
916     {
917       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
918       fmt.lpThousandSep = szGrp;
919     }
920   }
921
922   if (lpszValue)
923     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
924
925   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
926     cchOut = sizeof(szOut)/sizeof(WCHAR);
927
928   szOut[0] = '\0';
929
930   iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
931                           lpNumberStr ? szOut : NULL, cchOut);
932
933   if (szOut[0] && lpNumberStr)
934     WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
935   return iRet;
936 }
937
938 /* Number parsing state flags */
939 #define NF_ISNEGATIVE 0x1  /* '-' found */
940 #define NF_ISREAL     0x2  /* '.' found */
941 #define NF_DIGITS     0x4  /* '0'-'9' found */
942 #define NF_DIGITS_OUT 0x8  /* Digits before the '.' found */
943 #define NF_ROUND      0x10 /* Number needs to be rounded */
944
945 /* Formatting options for Numbers */
946 #define NLS_NEG_PARENS      0 /* "(1.1)" */
947 #define NLS_NEG_LEFT        1 /* "-1.1"  */
948 #define NLS_NEG_LEFT_SPACE  2 /* "- 1.1" */
949 #define NLS_NEG_RIGHT       3 /* "1.1-"  */
950 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
951
952 /**************************************************************************
953  *              GetNumberFormatW        (KERNEL32.@)
954  *
955  * See GetNumberFormatA.
956  */
957 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
958                             LPCWSTR lpszValue,  const NUMBERFMTW *lpFormat,
959                             LPWSTR lpNumberStr, int cchOut)
960 {
961   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
962   WCHAR szNegBuff[8];
963   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
964   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
965   INT iRet;
966
967   TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
968         lpFormat, lpNumberStr, cchOut);
969
970   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
971       !IsValidLocale(lcid, 0) ||
972       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
973   {
974 GetNumberFormatW_Error:
975     SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
976     return 0;
977   }
978
979   if (!lpFormat)
980   {
981     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
982
983     if (!node)
984       goto GetNumberFormatW_Error;
985     lpFormat = &node->fmt;
986     lpszNegStart = lpszNeg = GetNegative(node);
987   }
988   else
989   {
990     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
991                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
992     lpszNegStart = lpszNeg = szNegBuff;
993   }
994   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
995
996   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
997
998   /* Format the number backwards into a temporary buffer */
999
1000   szSrc = lpszValue;
1001   *szOut-- = '\0';
1002
1003   /* Check the number for validity */
1004   while (*szSrc)
1005   {
1006     if (*szSrc >= '0' && *szSrc <= '9')
1007     {
1008       dwState |= NF_DIGITS;
1009       if (dwState & NF_ISREAL)
1010         dwDecimals++;
1011     }
1012     else if (*szSrc == '-')
1013     {
1014       if (dwState)
1015         goto GetNumberFormatW_Error; /* '-' not first character */
1016       dwState |= NF_ISNEGATIVE;
1017     }
1018     else if (*szSrc == '.')
1019     {
1020       if (dwState & NF_ISREAL)
1021         goto GetNumberFormatW_Error; /* More than one '.' */
1022       dwState |= NF_ISREAL;
1023     }
1024     else
1025       goto GetNumberFormatW_Error; /* Invalid char */
1026     szSrc++;
1027   }
1028   szSrc--; /* Point to last character */
1029
1030   if (!(dwState & NF_DIGITS))
1031     goto GetNumberFormatW_Error; /* No digits */
1032
1033   /* Add any trailing negative sign */
1034   if (dwState & NF_ISNEGATIVE)
1035   {
1036     switch (lpFormat->NegativeOrder)
1037     {
1038     case NLS_NEG_PARENS:
1039       *szOut-- = ')';
1040       break;
1041     case NLS_NEG_RIGHT:
1042     case NLS_NEG_RIGHT_SPACE:
1043       while (lpszNeg >= lpszNegStart)
1044         *szOut-- = *lpszNeg--;
1045      if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
1046        *szOut-- = ' ';
1047       break;
1048     }
1049   }
1050
1051   /* Copy all digits up to the decimal point */
1052   if (!lpFormat->NumDigits)
1053   {
1054     if (dwState & NF_ISREAL)
1055     {
1056       while (*szSrc != '.') /* Don't write any decimals or a seperator */
1057       {
1058         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1059           dwState |= NF_ROUND;
1060         else
1061           dwState &= ~NF_ROUND;
1062         szSrc--;
1063       }
1064       szSrc--;
1065     }
1066   }
1067   else
1068   {
1069     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1070
1071     if (dwDecimals <= lpFormat->NumDigits)
1072     {
1073       dwDecimals = lpFormat->NumDigits - dwDecimals;
1074       while (dwDecimals--)
1075         *szOut-- = '0'; /* Pad to correct number of dp */
1076     }
1077     else
1078     {
1079       dwDecimals -= lpFormat->NumDigits;
1080       /* Skip excess decimals, and determine if we have to round the number */
1081       while (dwDecimals--)
1082       {
1083         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1084           dwState |= NF_ROUND;
1085         else
1086           dwState &= ~NF_ROUND;
1087         szSrc--;
1088       }
1089     }
1090
1091     if (dwState & NF_ISREAL)
1092     {
1093       while (*szSrc != '.')
1094       {
1095         if (dwState & NF_ROUND)
1096         {
1097           if (*szSrc == '9')
1098             *szOut-- = '0'; /* continue rounding */
1099           else
1100           {
1101             dwState &= ~NF_ROUND;
1102             *szOut-- = (*szSrc)+1;
1103           }
1104           szSrc--;
1105         }
1106         else
1107           *szOut-- = *szSrc--; /* Write existing decimals */
1108       }
1109       szSrc--; /* Skip '.' */
1110     }
1111
1112     while (lpszDec >= lpFormat->lpDecimalSep)
1113       *szOut-- = *lpszDec--; /* Write decimal seperator */
1114   }
1115
1116   dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1117
1118   /* Write the remaining whole number digits, including grouping chars */
1119   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1120   {
1121     if (dwState & NF_ROUND)
1122     {
1123       if (*szSrc == '9')
1124         *szOut-- = '0'; /* continue rounding */
1125       else
1126       {
1127         dwState &= ~NF_ROUND;
1128         *szOut-- = (*szSrc)+1;
1129       }
1130       szSrc--;
1131     }
1132     else
1133       *szOut-- = *szSrc--;
1134
1135     dwState |= NF_DIGITS_OUT;
1136     dwCurrentGroupCount++;
1137     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount)
1138     {
1139       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1140
1141       while (lpszGrp >= lpFormat->lpThousandSep)
1142         *szOut-- = *lpszGrp--; /* Write grouping char */
1143
1144       dwCurrentGroupCount = 0;
1145       if (lpFormat->Grouping == 32)
1146         dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1147     }
1148   }
1149   if (dwState & NF_ROUND)
1150   {
1151     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1152   }
1153   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1154     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1155
1156   /* Add any leading negative sign */
1157   if (dwState & NF_ISNEGATIVE)
1158   {
1159     switch (lpFormat->NegativeOrder)
1160     {
1161     case NLS_NEG_PARENS:
1162       *szOut-- = '(';
1163       break;
1164     case NLS_NEG_LEFT_SPACE:
1165       *szOut-- = ' ';
1166       /* Fall through */
1167     case NLS_NEG_LEFT:
1168       while (lpszNeg >= lpszNegStart)
1169         *szOut-- = *lpszNeg--;
1170       break;
1171     }
1172   }
1173   szOut++;
1174
1175   iRet = strlenW(szOut) + 1;
1176   if (cchOut)
1177   {
1178     if (iRet <= cchOut)
1179       memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
1180     else
1181     {
1182       memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
1183       lpNumberStr[cchOut - 1] = '\0';
1184       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1185       iRet = 0;
1186     }
1187   }
1188   return iRet;
1189 }
1190
1191 /**************************************************************************
1192  *              GetCurrencyFormatA      (KERNEL32.@)
1193  *
1194  * Format a currency string for a given locale.
1195  *
1196  * PARAMS
1197  *  lcid          [I] Locale to format for
1198  *  dwFlags       [I] LOCALE_ flags from "winnls.h"
1199  *  lpszValue     [I] String to format
1200  *  lpFormat      [I] Formatting overrides
1201  *  lpCurrencyStr [O] Destination for formatted string
1202  *  cchOut        [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1203  *
1204  * NOTES
1205  *  - lpszValue can contain only '0' - '9', '-' and '.'.
1206  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1207  *    be formatted according to the format details returned by GetLocaleInfoA().
1208  *  - This function rounds the currency if the number of decimals exceeds the
1209  *    locales number of currency decimal places.
1210  *  - If cchOut is 0, this function does not write to lpCurrencyStr.
1211  *  - The Ascii version of this function fails if lcid is Unicode only.
1212  *
1213  * RETURNS
1214  *  Success: The number of character written to lpNumberStr, or that would
1215  *           have been written, if cchOut is 0.
1216  *  Failure: 0. Use GetLastError() to determine the cause.
1217  */
1218 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
1219                               LPCSTR lpszValue,  const CURRENCYFMTA *lpFormat,
1220                               LPSTR lpCurrencyStr, int cchOut)
1221 {
1222   DWORD cp = CP_ACP;
1223   WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
1224   CURRENCYFMTW fmt;
1225   const CURRENCYFMTW *pfmt = NULL;
1226   INT iRet;
1227
1228   TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
1229         lpFormat, lpCurrencyStr, cchOut);
1230
1231   if (NLS_IsUnicodeOnlyLcid(lcid))
1232   {
1233 GetCurrencyFormatA_InvalidParameter:
1234     SetLastError(ERROR_INVALID_PARAMETER);
1235     return 0;
1236   }
1237
1238   if (!(dwFlags & LOCALE_USE_CP_ACP))
1239   {
1240     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1241     if (!node)
1242       goto GetCurrencyFormatA_InvalidParameter;
1243     cp = node->dwCodePage;
1244   }
1245
1246   if (lpFormat)
1247   {
1248     memcpy(&fmt, lpFormat, sizeof(fmt));
1249     pfmt = &fmt;
1250     if (lpFormat->lpDecimalSep)
1251     {
1252       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
1253       fmt.lpDecimalSep = szDec;
1254     }
1255     if (lpFormat->lpThousandSep)
1256     {
1257       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
1258       fmt.lpThousandSep = szGrp;
1259     }
1260     if (lpFormat->lpCurrencySymbol)
1261     {
1262       MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR));
1263       fmt.lpCurrencySymbol = szCy;
1264     }
1265   }
1266
1267   if (lpszValue)
1268     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
1269
1270   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1271     cchOut = sizeof(szOut)/sizeof(WCHAR);
1272
1273   szOut[0] = '\0';
1274
1275   iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1276                             lpCurrencyStr ? szOut : NULL, cchOut);
1277
1278   if (szOut[0] && lpCurrencyStr)
1279     WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
1280   return iRet;
1281 }
1282
1283 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1284 #define CF_PARENS       0x1  /* Parentheses      */
1285 #define CF_MINUS_LEFT   0x2  /* '-' to the left  */
1286 #define CF_MINUS_RIGHT  0x4  /* '-' to the right */
1287 #define CF_MINUS_BEFORE 0x8  /* '-' before '$'   */
1288 #define CF_CY_LEFT      0x10 /* '$' to the left  */
1289 #define CF_CY_RIGHT     0x20 /* '$' to the right */
1290 #define CF_CY_SPACE     0x40 /* ' ' by '$'       */
1291
1292 /**************************************************************************
1293  *              GetCurrencyFormatW      (KERNEL32.@)
1294  *
1295  * See GetCurrencyFormatA.
1296  */
1297 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
1298                               LPCWSTR lpszValue,  const CURRENCYFMTW *lpFormat,
1299                               LPWSTR lpCurrencyStr, int cchOut)
1300 {
1301   static const BYTE NLS_NegCyFormats[16] =
1302   {
1303     CF_PARENS|CF_CY_LEFT,                       /* ($1.1) */
1304     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT,   /* -$1.1  */
1305     CF_MINUS_LEFT|CF_CY_LEFT,                   /* $-1.1  */
1306     CF_MINUS_RIGHT|CF_CY_LEFT,                  /* $1.1-  */
1307     CF_PARENS|CF_CY_RIGHT,                      /* (1.1$) */
1308     CF_MINUS_LEFT|CF_CY_RIGHT,                  /* -1.1$  */
1309     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$  */
1310     CF_MINUS_RIGHT|CF_CY_RIGHT,                 /* 1.1$-  */
1311     CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE,      /* -1.1 $ */
1312     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE,   /* -$ 1.1 */
1313     CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE,     /* 1.1 $-  */
1314     CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE,      /* $ 1.1-  */
1315     CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE,       /* $ -1.1  */
1316     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
1317     CF_PARENS|CF_CY_LEFT|CF_CY_SPACE,           /* ($ 1.1) */
1318     CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE,          /* (1.1 $) */
1319   };
1320   static const BYTE NLS_PosCyFormats[4] =
1321   {
1322     CF_CY_LEFT,              /* $1.1  */
1323     CF_CY_RIGHT,             /* 1.1$  */
1324     CF_CY_LEFT|CF_CY_SPACE,  /* $ 1.1 */
1325     CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
1326   };
1327   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1328   WCHAR szNegBuff[8];
1329   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
1330   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
1331   INT iRet;
1332
1333   TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1334         lpFormat, lpCurrencyStr, cchOut);
1335
1336   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
1337       !IsValidLocale(lcid, 0) ||
1338       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
1339       !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
1340       lpFormat->PositiveOrder > 3)))
1341   {
1342 GetCurrencyFormatW_Error:
1343     SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1344     return 0;
1345   }
1346
1347   if (!lpFormat)
1348   {
1349     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1350
1351     if (!node)
1352       goto GetCurrencyFormatW_Error;
1353     lpFormat = &node->cyfmt;
1354     lpszNegStart = lpszNeg = GetNegative(node);
1355   }
1356   else
1357   {
1358     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1359                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1360     lpszNegStart = lpszNeg = szNegBuff;
1361   }
1362   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1363
1364   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1365   lpszCyStart = lpFormat->lpCurrencySymbol;
1366   lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
1367
1368   /* Format the currency backwards into a temporary buffer */
1369
1370   szSrc = lpszValue;
1371   *szOut-- = '\0';
1372
1373   /* Check the number for validity */
1374   while (*szSrc)
1375   {
1376     if (*szSrc >= '0' && *szSrc <= '9')
1377     {
1378       dwState |= NF_DIGITS;
1379       if (dwState & NF_ISREAL)
1380         dwDecimals++;
1381     }
1382     else if (*szSrc == '-')
1383     {
1384       if (dwState)
1385         goto GetCurrencyFormatW_Error; /* '-' not first character */
1386       dwState |= NF_ISNEGATIVE;
1387     }
1388     else if (*szSrc == '.')
1389     {
1390       if (dwState & NF_ISREAL)
1391         goto GetCurrencyFormatW_Error; /* More than one '.' */
1392       dwState |= NF_ISREAL;
1393     }
1394     else
1395       goto GetCurrencyFormatW_Error; /* Invalid char */
1396     szSrc++;
1397   }
1398   szSrc--; /* Point to last character */
1399
1400   if (!(dwState & NF_DIGITS))
1401     goto GetCurrencyFormatW_Error; /* No digits */
1402
1403   if (dwState & NF_ISNEGATIVE)
1404     dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
1405   else
1406     dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
1407
1408   /* Add any trailing negative or currency signs */
1409   if (dwFmt & CF_PARENS)
1410     *szOut-- = ')';
1411
1412   while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
1413   {
1414     switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
1415     {
1416     case CF_MINUS_RIGHT:
1417     case CF_MINUS_RIGHT|CF_CY_RIGHT:
1418       while (lpszNeg >= lpszNegStart)
1419         *szOut-- = *lpszNeg--;
1420       dwFmt &= ~CF_MINUS_RIGHT;
1421       break;
1422
1423     case CF_CY_RIGHT:
1424     case CF_MINUS_BEFORE|CF_CY_RIGHT:
1425     case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
1426       while (lpszCy >= lpszCyStart)
1427         *szOut-- = *lpszCy--;
1428       if (dwFmt & CF_CY_SPACE)
1429         *szOut-- = ' ';
1430       dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
1431       break;
1432     }
1433   }
1434
1435   /* Copy all digits up to the decimal point */
1436   if (!lpFormat->NumDigits)
1437   {
1438     if (dwState & NF_ISREAL)
1439     {
1440       while (*szSrc != '.') /* Don't write any decimals or a seperator */
1441       {
1442         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1443           dwState |= NF_ROUND;
1444         else
1445           dwState &= ~NF_ROUND;
1446         szSrc--;
1447       }
1448       szSrc--;
1449     }
1450   }
1451   else
1452   {
1453     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1454
1455     if (dwDecimals <= lpFormat->NumDigits)
1456     {
1457       dwDecimals = lpFormat->NumDigits - dwDecimals;
1458       while (dwDecimals--)
1459         *szOut-- = '0'; /* Pad to correct number of dp */
1460     }
1461     else
1462     {
1463       dwDecimals -= lpFormat->NumDigits;
1464       /* Skip excess decimals, and determine if we have to round the number */
1465       while (dwDecimals--)
1466       {
1467         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1468           dwState |= NF_ROUND;
1469         else
1470           dwState &= ~NF_ROUND;
1471         szSrc--;
1472       }
1473     }
1474
1475     if (dwState & NF_ISREAL)
1476     {
1477       while (*szSrc != '.')
1478       {
1479         if (dwState & NF_ROUND)
1480         {
1481           if (*szSrc == '9')
1482             *szOut-- = '0'; /* continue rounding */
1483           else
1484           {
1485             dwState &= ~NF_ROUND;
1486             *szOut-- = (*szSrc)+1;
1487           }
1488           szSrc--;
1489         }
1490         else
1491           *szOut-- = *szSrc--; /* Write existing decimals */
1492       }
1493       szSrc--; /* Skip '.' */
1494     }
1495     while (lpszDec >= lpFormat->lpDecimalSep)
1496       *szOut-- = *lpszDec--; /* Write decimal seperator */
1497   }
1498
1499   dwGroupCount = lpFormat->Grouping;
1500
1501   /* Write the remaining whole number digits, including grouping chars */
1502   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1503   {
1504     if (dwState & NF_ROUND)
1505     {
1506       if (*szSrc == '9')
1507         *szOut-- = '0'; /* continue rounding */
1508       else
1509       {
1510         dwState &= ~NF_ROUND;
1511         *szOut-- = (*szSrc)+1;
1512       }
1513       szSrc--;
1514     }
1515     else
1516       *szOut-- = *szSrc--;
1517
1518     dwState |= NF_DIGITS_OUT;
1519     dwCurrentGroupCount++;
1520     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount)
1521     {
1522       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1523
1524       while (lpszGrp >= lpFormat->lpThousandSep)
1525         *szOut-- = *lpszGrp--; /* Write grouping char */
1526
1527       dwCurrentGroupCount = 0;
1528     }
1529   }
1530   if (dwState & NF_ROUND)
1531     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1532   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1533     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1534
1535   /* Add any leading negative or currency sign */
1536   while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
1537   {
1538     switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
1539     {
1540     case CF_MINUS_LEFT:
1541     case CF_MINUS_LEFT|CF_CY_LEFT:
1542       while (lpszNeg >= lpszNegStart)
1543         *szOut-- = *lpszNeg--;
1544       dwFmt &= ~CF_MINUS_LEFT;
1545       break;
1546
1547     case CF_CY_LEFT:
1548     case CF_CY_LEFT|CF_MINUS_BEFORE:
1549     case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
1550       if (dwFmt & CF_CY_SPACE)
1551         *szOut-- = ' ';
1552       while (lpszCy >= lpszCyStart)
1553         *szOut-- = *lpszCy--;
1554       dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
1555       break;
1556     }
1557   }
1558   if (dwFmt & CF_PARENS)
1559     *szOut-- = '(';
1560   szOut++;
1561
1562   iRet = strlenW(szOut) + 1;
1563   if (cchOut)
1564   {
1565     if (iRet <= cchOut)
1566       memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
1567     else
1568     {
1569       memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
1570       lpCurrencyStr[cchOut - 1] = '\0';
1571       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1572       iRet = 0;
1573     }
1574   }
1575   return iRet;
1576 }
1577
1578 /* FIXME: Everything below here needs to move somewhere else along with the
1579  *        other EnumXXX functions, when a method for storing resources for
1580  *        alternate calendars is determined.
1581  */
1582
1583 /**************************************************************************
1584  *              EnumDateFormatsA        (KERNEL32.@)
1585  */
1586 BOOL WINAPI EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale,  DWORD dwFlags)
1587 {
1588   LCID Loc = GetUserDefaultLCID();
1589   if(!lpDateFmtEnumProc)
1590     {
1591       SetLastError(ERROR_INVALID_PARAMETER);
1592       return FALSE;
1593     }
1594
1595   switch( Loc )
1596  {
1597
1598    case 0x00000407:  /* (Loc,"de_DE") */
1599    {
1600     switch(dwFlags)
1601     {
1602       case DATE_SHORTDATE:
1603         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1604         if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1605         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1606         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1607         return TRUE;
1608       case DATE_LONGDATE:
1609         if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1610         if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1611         if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1612         return TRUE;
1613       default:
1614         FIXME("Unknown date format (%ld)\n", dwFlags);
1615         SetLastError(ERROR_INVALID_PARAMETER);
1616         return FALSE;
1617      }
1618    }
1619
1620    case 0x0000040c:  /* (Loc,"fr_FR") */
1621    {
1622     switch(dwFlags)
1623     {
1624       case DATE_SHORTDATE:
1625         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1626         if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1627         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1628         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1629         return TRUE;
1630       case DATE_LONGDATE:
1631         if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
1632         if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
1633         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1634         return TRUE;
1635       default:
1636         FIXME("Unknown date format (%ld)\n", dwFlags);
1637         SetLastError(ERROR_INVALID_PARAMETER);
1638         return FALSE;
1639      }
1640    }
1641
1642    case 0x00000c0c:  /* (Loc,"fr_CA") */
1643    {
1644     switch(dwFlags)
1645     {
1646       case DATE_SHORTDATE:
1647         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1648         if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1649         if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
1650         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1651         return TRUE;
1652       case DATE_LONGDATE:
1653         if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
1654         if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
1655         return TRUE;
1656       default:
1657         FIXME("Unknown date format (%ld)\n", dwFlags);
1658         SetLastError(ERROR_INVALID_PARAMETER);
1659         return FALSE;
1660      }
1661    }
1662
1663    case 0x00000809:  /* (Loc,"en_UK") */
1664   {
1665    switch(dwFlags)
1666     {
1667       case DATE_SHORTDATE:
1668         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1669         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1670         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1671         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1672         return TRUE;
1673       case DATE_LONGDATE:
1674         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1675         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1676         return TRUE;
1677       default:
1678         FIXME("Unknown date format (%ld)\n", dwFlags);
1679         SetLastError(ERROR_INVALID_PARAMETER);
1680         return FALSE;
1681     }
1682   }
1683
1684    case 0x00000c09:  /* (Loc,"en_AU") */
1685   {
1686    switch(dwFlags)
1687     {
1688       case DATE_SHORTDATE:
1689         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1690         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1691         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1692         return TRUE;
1693       case DATE_LONGDATE:
1694         if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
1695         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1696         return TRUE;
1697       default:
1698         FIXME("Unknown date format (%ld)\n", dwFlags);
1699         SetLastError(ERROR_INVALID_PARAMETER);
1700         return FALSE;
1701     }
1702   }
1703
1704    case 0x00001009:  /* (Loc,"en_CA") */
1705   {
1706    switch(dwFlags)
1707     {
1708       case DATE_SHORTDATE:
1709         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1710         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1711         if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1712         if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
1713         return TRUE;
1714       case DATE_LONGDATE:
1715         if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
1716         if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
1717         return TRUE;
1718       default:
1719         FIXME("Unknown date format (%ld)\n", dwFlags);
1720         SetLastError(ERROR_INVALID_PARAMETER);
1721         return FALSE;
1722     }
1723   }
1724
1725    case 0x00001409:  /* (Loc,"en_NZ") */
1726   {
1727    switch(dwFlags)
1728     {
1729       case DATE_SHORTDATE:
1730         if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1731         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1732         if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1733         return TRUE;
1734       case DATE_LONGDATE:
1735         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1736         if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
1737         return TRUE;
1738       default:
1739         FIXME("Unknown date format (%ld)\n", dwFlags);
1740         SetLastError(ERROR_INVALID_PARAMETER);
1741         return FALSE;
1742     }
1743   }
1744
1745    case 0x00001809:  /* (Loc,"en_IE") */
1746   {
1747    switch(dwFlags)
1748     {
1749       case DATE_SHORTDATE:
1750         if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1751         if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1752         if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1753         return TRUE;
1754       case DATE_LONGDATE:
1755         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1756         if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1757         return TRUE;
1758       default:
1759         FIXME("Unknown date format (%ld)\n", dwFlags);
1760         SetLastError(ERROR_INVALID_PARAMETER);
1761         return FALSE;
1762     }
1763   }
1764
1765    case 0x00001c09:  /* (Loc,"en_ZA") */
1766   {
1767    switch(dwFlags)
1768     {
1769       case DATE_SHORTDATE:
1770         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
1771         return TRUE;
1772       case DATE_LONGDATE:
1773         if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1774         return TRUE;
1775       default:
1776         FIXME("Unknown date format (%ld)\n", dwFlags);
1777         SetLastError(ERROR_INVALID_PARAMETER);
1778         return FALSE;
1779     }
1780   }
1781
1782    case 0x00002009:  /* (Loc,"en_JM") */
1783   {
1784    switch(dwFlags)
1785     {
1786       case DATE_SHORTDATE:
1787         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1788         return TRUE;
1789       case DATE_LONGDATE:
1790         if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
1791         if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
1792         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
1793         if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
1794         return TRUE;
1795       default:
1796         FIXME("Unknown date format (%ld)\n", dwFlags);
1797         SetLastError(ERROR_INVALID_PARAMETER);
1798         return FALSE;
1799     }
1800   }
1801
1802    case 0x00002809:  /* (Loc,"en_BZ") */
1803    case 0x00002c09:  /* (Loc,"en_TT") */
1804   {
1805    switch(dwFlags)
1806     {
1807       case DATE_SHORTDATE:
1808         if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1809         return TRUE;
1810       case DATE_LONGDATE:
1811         if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
1812         return TRUE;
1813       default:
1814         FIXME("Unknown date format (%ld)\n", dwFlags);
1815         SetLastError(ERROR_INVALID_PARAMETER);
1816         return FALSE;
1817     }
1818   }
1819
1820    default:  /* default to US English "en_US" */
1821   {
1822    switch(dwFlags)
1823     {
1824       case DATE_SHORTDATE:
1825         if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
1826         if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
1827         if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
1828         if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
1829         if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
1830         if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
1831         return TRUE;
1832       case DATE_LONGDATE:
1833         if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
1834         if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
1835         if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
1836         if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
1837         return TRUE;
1838       default:
1839         FIXME("Unknown date format (%ld)\n", dwFlags);
1840         SetLastError(ERROR_INVALID_PARAMETER);
1841         return FALSE;
1842     }
1843   }
1844  }
1845 }
1846
1847 /**************************************************************************
1848  *              EnumDateFormatsW        (KERNEL32.@)
1849  */
1850 BOOL WINAPI EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
1851 {
1852   FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
1853   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1854   return FALSE;
1855 }
1856
1857 /**************************************************************************
1858  *              EnumTimeFormatsA        (KERNEL32.@)
1859  */
1860 BOOL WINAPI EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
1861 {
1862   LCID Loc = GetUserDefaultLCID();
1863   if(!lpTimeFmtEnumProc)
1864     {
1865       SetLastError(ERROR_INVALID_PARAMETER);
1866       return FALSE;
1867     }
1868   if(dwFlags)
1869     {
1870       FIXME("Unknown time format (%ld)\n", dwFlags);
1871     }
1872
1873   switch( Loc )
1874  {
1875    case 0x00000407:  /* (Loc,"de_DE") */
1876    {
1877     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
1878     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1879     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1880     if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
1881     if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
1882     return TRUE;
1883    }
1884
1885    case 0x0000040c:  /* (Loc,"fr_FR") */
1886    case 0x00000c0c:  /* (Loc,"fr_CA") */
1887    {
1888     if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
1889     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1890     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1891     if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
1892     if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
1893     return TRUE;
1894    }
1895
1896    case 0x00000809:  /* (Loc,"en_UK") */
1897    case 0x00000c09:  /* (Loc,"en_AU") */
1898    case 0x00001409:  /* (Loc,"en_NZ") */
1899    case 0x00001809:  /* (Loc,"en_IE") */
1900    {
1901     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1902     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1903     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1904     return TRUE;
1905    }
1906
1907    case 0x00001c09:  /* (Loc,"en_ZA") */
1908    case 0x00002809:  /* (Loc,"en_BZ") */
1909    case 0x00002c09:  /* (Loc,"en_TT") */
1910    {
1911     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1912     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
1913     return TRUE;
1914    }
1915
1916    default:  /* default to US style "en_US" */
1917    {
1918     if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1919     if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
1920     if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1921     if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1922     return TRUE;
1923    }
1924  }
1925 }
1926
1927 /**************************************************************************
1928  *              EnumTimeFormatsW        (KERNEL32.@)
1929  */
1930 BOOL WINAPI EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
1931 {
1932   FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
1933   SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1934   return FALSE;
1935 }
1936
1937 /******************************************************************************
1938  *              EnumCalendarInfoA       [KERNEL32.@]
1939  */
1940 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
1941                                CALID calendar,CALTYPE caltype )
1942 {
1943     FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc,locale,calendar,caltype);
1944     return FALSE;
1945 }