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