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