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