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