user.exe: The default dialog button id is 0 on Win16.
[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 GetDateTimeFormatA_InvalidParameter:
760     SetLastError(ERROR_INVALID_PARAMETER);
761     return 0;
762   }
763
764   if (!(dwFlags & LOCALE_USE_CP_ACP))
765   {
766     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
767     if (!node)
768       goto GetDateTimeFormatA_InvalidParameter;
769     cp = node->dwCodePage;
770   }
771
772   if (lpFormat)
773     MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
774
775   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
776     cchOut = sizeof(szOut)/sizeof(WCHAR);
777
778   szOut[0] = '\0';
779
780   iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
781                                 lpStr ? szOut : NULL, cchOut);
782
783   if (lpStr)
784   {
785     if (szOut[0])
786       WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0);
787     else if (cchOut && iRet)
788       *lpStr = '\0';
789   }
790   return iRet;
791 }
792
793 /******************************************************************************
794  * GetDateFormatA [KERNEL32.@]
795  *
796  * Format a date for a given locale.
797  *
798  * PARAMS
799  *  lcid      [I] Locale to format for
800  *  dwFlags   [I] LOCALE_ and DATE_ flags from "winnls.h"
801  *  lpTime    [I] Date to format
802  *  lpFormat  [I] Format string, or NULL to use the system defaults
803  *  lpDateStr [O] Destination for formatted string
804  *  cchOut    [I] Size of lpDateStr, or 0 to calculate the resulting size
805  *
806  * NOTES
807  *  - If lpFormat is NULL, lpDateStr will be formatted according to the format
808  *    details returned by GetLocaleInfoA() and modified by dwFlags.
809  *  - lpFormat is a string of characters and formatting tokens. Any characters
810  *    in the string are copied verbatim to lpDateStr, with tokens being replaced
811  *    by the date values they represent.
812  *  - The following tokens have special meanings in a date format string:
813  *|  Token  Meaning
814  *|  -----  -------
815  *|  d      Single digit day of the month (no leading 0)
816  *|  dd     Double digit day of the month
817  *|  ddd    Short name for the day of the week
818  *|  dddd   Long name for the day of the week
819  *|  M      Single digit month of the year (no leading 0)
820  *|  MM     Double digit month of the year
821  *|  MMM    Short name for the month of the year
822  *|  MMMM   Long name for the month of the year
823  *|  y      Double digit year number (no leading 0)
824  *|  yy     Double digit year number
825  *|  yyyy   Four digit year number
826  *|  gg     Era string, for example 'AD'.
827  *  - To output any literal character that could be misidentified as a token,
828  *    enclose it in single quotes.
829  *  - The Ascii version of this function fails if lcid is Unicode only.
830  *
831  * RETURNS
832  *  Success: The number of character written to lpDateStr, or that would
833  *           have been written, if cchOut is 0.
834  *  Failure: 0. Use GetLastError() to determine the cause.
835  */
836 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
837                            LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
838 {
839   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
840         debugstr_a(lpFormat), lpDateStr, cchOut);
841
842   return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
843                                 lpFormat, lpDateStr, cchOut);
844 }
845
846
847 /******************************************************************************
848  * GetDateFormatW       [KERNEL32.@]
849  *
850  * See GetDateFormatA.
851  */
852 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
853                           LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
854 {
855   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
856         debugstr_w(lpFormat), lpDateStr, cchOut);
857
858   return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
859                                 lpFormat, lpDateStr, cchOut);
860 }
861
862 /******************************************************************************
863  *              GetTimeFormatA  [KERNEL32.@]
864  *
865  * Format a time for a given locale.
866  *
867  * PARAMS
868  *  lcid      [I] Locale to format for
869  *  dwFlags   [I] LOCALE_ and TIME_ flags from "winnls.h"
870  *  lpTime    [I] Time to format
871  *  lpFormat  [I] Formatting overrides
872  *  lpTimeStr [O] Destination for formatted string
873  *  cchOut    [I] Size of lpTimeStr, or 0 to calculate the resulting size
874  *
875  * NOTES
876  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
877  *    details returned by GetLocaleInfoA() and modified by dwFlags.
878  *  - lpFormat is a string of characters and formatting tokens. Any characters
879  *    in the string are copied verbatim to lpTimeStr, with tokens being replaced
880  *    by the time values they represent.
881  *  - The following tokens have special meanings in a time format string:
882  *|  Token  Meaning
883  *|  -----  -------
884  *|  h      Hours with no leading zero (12-hour clock)
885  *|  hh     Hours with full two digits (12-hour clock)
886  *|  H      Hours with no leading zero (24-hour clock)
887  *|  HH     Hours with full two digits (24-hour clock)
888  *|  m      Minutes with no leading zero
889  *|  mm     Minutes with full two digits
890  *|  s      Seconds with no leading zero
891  *|  ss     Seconds with full two digits
892  *|  t      Short time marker (e.g. "A" or "P")
893  *|  tt     Long time marker (e.g. "AM", "PM")
894  *  - To output any literal character that could be misidentified as a token,
895  *    enclose it in single quotes.
896  *  - The Ascii version of this function fails if lcid is Unicode only.
897  *
898  * RETURNS
899  *  Success: The number of character written to lpTimeStr, or that would
900  *           have been written, if cchOut is 0.
901  *  Failure: 0. Use GetLastError() to determine the cause.
902  */
903 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
904                           LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
905 {
906   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
907         debugstr_a(lpFormat), lpTimeStr, cchOut);
908
909   return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
910                                 lpFormat, lpTimeStr, cchOut);
911 }
912
913 /******************************************************************************
914  *              GetTimeFormatW  [KERNEL32.@]
915  *
916  * See GetTimeFormatA.
917  */
918 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
919                           LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
920 {
921   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
922         debugstr_w(lpFormat), lpTimeStr, cchOut);
923
924   return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
925                                 lpFormat, lpTimeStr, cchOut);
926 }
927
928 /**************************************************************************
929  *              GetNumberFormatA        (KERNEL32.@)
930  *
931  * Format a number string for a given locale.
932  *
933  * PARAMS
934  *  lcid        [I] Locale to format for
935  *  dwFlags     [I] LOCALE_ flags from "winnls.h"
936  *  lpszValue   [I] String to format
937  *  lpFormat    [I] Formatting overrides
938  *  lpNumberStr [O] Destination for formatted string
939  *  cchOut      [I] Size of lpNumberStr, or 0 to calculate the resulting size
940  *
941  * NOTES
942  *  - lpszValue can contain only '0' - '9', '-' and '.'.
943  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
944  *    be formatted according to the format details returned by GetLocaleInfoA().
945  *  - This function rounds the number string if the number of decimals exceeds the
946  *    locales normal number of decimal places.
947  *  - If cchOut is 0, this function does not write to lpNumberStr.
948  *  - The Ascii version of this function fails if lcid is Unicode only.
949  *
950  * RETURNS
951  *  Success: The number of character written to lpNumberStr, or that would
952  *           have been written, if cchOut is 0.
953  *  Failure: 0. Use GetLastError() to determine the cause.
954  */
955 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
956                             LPCSTR lpszValue,  const NUMBERFMTA *lpFormat,
957                             LPSTR lpNumberStr, int cchOut)
958 {
959   DWORD cp = CP_ACP;
960   WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
961   NUMBERFMTW fmt;
962   const NUMBERFMTW *pfmt = NULL;
963   INT iRet;
964
965   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
966         lpFormat, lpNumberStr, cchOut);
967
968   if (NLS_IsUnicodeOnlyLcid(lcid))
969   {
970 GetNumberFormatA_InvalidParameter:
971     SetLastError(ERROR_INVALID_PARAMETER);
972     return 0;
973   }
974
975   if (!(dwFlags & LOCALE_USE_CP_ACP))
976   {
977     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
978     if (!node)
979       goto GetNumberFormatA_InvalidParameter;
980     cp = node->dwCodePage;
981   }
982
983   if (lpFormat)
984   {
985     memcpy(&fmt, lpFormat, sizeof(fmt));
986     pfmt = &fmt;
987     if (lpFormat->lpDecimalSep)
988     {
989       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
990       fmt.lpDecimalSep = szDec;
991     }
992     if (lpFormat->lpThousandSep)
993     {
994       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
995       fmt.lpThousandSep = szGrp;
996     }
997   }
998
999   if (lpszValue)
1000     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
1001
1002   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1003     cchOut = sizeof(szOut)/sizeof(WCHAR);
1004
1005   szOut[0] = '\0';
1006
1007   iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1008                           lpNumberStr ? szOut : NULL, cchOut);
1009
1010   if (szOut[0] && lpNumberStr)
1011     WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
1012   return iRet;
1013 }
1014
1015 /* Number parsing state flags */
1016 #define NF_ISNEGATIVE 0x1  /* '-' found */
1017 #define NF_ISREAL     0x2  /* '.' found */
1018 #define NF_DIGITS     0x4  /* '0'-'9' found */
1019 #define NF_DIGITS_OUT 0x8  /* Digits before the '.' found */
1020 #define NF_ROUND      0x10 /* Number needs to be rounded */
1021
1022 /* Formatting options for Numbers */
1023 #define NLS_NEG_PARENS      0 /* "(1.1)" */
1024 #define NLS_NEG_LEFT        1 /* "-1.1"  */
1025 #define NLS_NEG_LEFT_SPACE  2 /* "- 1.1" */
1026 #define NLS_NEG_RIGHT       3 /* "1.1-"  */
1027 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1028
1029 /**************************************************************************
1030  *              GetNumberFormatW        (KERNEL32.@)
1031  *
1032  * See GetNumberFormatA.
1033  */
1034 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
1035                             LPCWSTR lpszValue,  const NUMBERFMTW *lpFormat,
1036                             LPWSTR lpNumberStr, int cchOut)
1037 {
1038   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1039   WCHAR szNegBuff[8];
1040   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
1041   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
1042   INT iRet;
1043
1044   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1045         lpFormat, lpNumberStr, cchOut);
1046
1047   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
1048       !IsValidLocale(lcid, 0) ||
1049       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
1050   {
1051 GetNumberFormatW_Error:
1052     SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1053     return 0;
1054   }
1055
1056   if (!lpFormat)
1057   {
1058     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1059
1060     if (!node)
1061       goto GetNumberFormatW_Error;
1062     lpFormat = &node->fmt;
1063     lpszNegStart = lpszNeg = GetNegative(node);
1064   }
1065   else
1066   {
1067     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1068                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1069     lpszNegStart = lpszNeg = szNegBuff;
1070   }
1071   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1072
1073   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1074
1075   /* Format the number backwards into a temporary buffer */
1076
1077   szSrc = lpszValue;
1078   *szOut-- = '\0';
1079
1080   /* Check the number for validity */
1081   while (*szSrc)
1082   {
1083     if (*szSrc >= '0' && *szSrc <= '9')
1084     {
1085       dwState |= NF_DIGITS;
1086       if (dwState & NF_ISREAL)
1087         dwDecimals++;
1088     }
1089     else if (*szSrc == '-')
1090     {
1091       if (dwState)
1092         goto GetNumberFormatW_Error; /* '-' not first character */
1093       dwState |= NF_ISNEGATIVE;
1094     }
1095     else if (*szSrc == '.')
1096     {
1097       if (dwState & NF_ISREAL)
1098         goto GetNumberFormatW_Error; /* More than one '.' */
1099       dwState |= NF_ISREAL;
1100     }
1101     else
1102       goto GetNumberFormatW_Error; /* Invalid char */
1103     szSrc++;
1104   }
1105   szSrc--; /* Point to last character */
1106
1107   if (!(dwState & NF_DIGITS))
1108     goto GetNumberFormatW_Error; /* No digits */
1109
1110   /* Add any trailing negative sign */
1111   if (dwState & NF_ISNEGATIVE)
1112   {
1113     switch (lpFormat->NegativeOrder)
1114     {
1115     case NLS_NEG_PARENS:
1116       *szOut-- = ')';
1117       break;
1118     case NLS_NEG_RIGHT:
1119     case NLS_NEG_RIGHT_SPACE:
1120       while (lpszNeg >= lpszNegStart)
1121         *szOut-- = *lpszNeg--;
1122      if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
1123        *szOut-- = ' ';
1124       break;
1125     }
1126   }
1127
1128   /* Copy all digits up to the decimal point */
1129   if (!lpFormat->NumDigits)
1130   {
1131     if (dwState & NF_ISREAL)
1132     {
1133       while (*szSrc != '.') /* Don't write any decimals or a separator */
1134       {
1135         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1136           dwState |= NF_ROUND;
1137         else
1138           dwState &= ~NF_ROUND;
1139         szSrc--;
1140       }
1141       szSrc--;
1142     }
1143   }
1144   else
1145   {
1146     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1147
1148     if (dwDecimals <= lpFormat->NumDigits)
1149     {
1150       dwDecimals = lpFormat->NumDigits - dwDecimals;
1151       while (dwDecimals--)
1152         *szOut-- = '0'; /* Pad to correct number of dp */
1153     }
1154     else
1155     {
1156       dwDecimals -= lpFormat->NumDigits;
1157       /* Skip excess decimals, and determine if we have to round the number */
1158       while (dwDecimals--)
1159       {
1160         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1161           dwState |= NF_ROUND;
1162         else
1163           dwState &= ~NF_ROUND;
1164         szSrc--;
1165       }
1166     }
1167
1168     if (dwState & NF_ISREAL)
1169     {
1170       while (*szSrc != '.')
1171       {
1172         if (dwState & NF_ROUND)
1173         {
1174           if (*szSrc == '9')
1175             *szOut-- = '0'; /* continue rounding */
1176           else
1177           {
1178             dwState &= ~NF_ROUND;
1179             *szOut-- = (*szSrc)+1;
1180           }
1181           szSrc--;
1182         }
1183         else
1184           *szOut-- = *szSrc--; /* Write existing decimals */
1185       }
1186       szSrc--; /* Skip '.' */
1187     }
1188
1189     while (lpszDec >= lpFormat->lpDecimalSep)
1190       *szOut-- = *lpszDec--; /* Write decimal separator */
1191   }
1192
1193   dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1194
1195   /* Write the remaining whole number digits, including grouping chars */
1196   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1197   {
1198     if (dwState & NF_ROUND)
1199     {
1200       if (*szSrc == '9')
1201         *szOut-- = '0'; /* continue rounding */
1202       else
1203       {
1204         dwState &= ~NF_ROUND;
1205         *szOut-- = (*szSrc)+1;
1206       }
1207       szSrc--;
1208     }
1209     else
1210       *szOut-- = *szSrc--;
1211
1212     dwState |= NF_DIGITS_OUT;
1213     dwCurrentGroupCount++;
1214     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
1215     {
1216       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1217
1218       while (lpszGrp >= lpFormat->lpThousandSep)
1219         *szOut-- = *lpszGrp--; /* Write grouping char */
1220
1221       dwCurrentGroupCount = 0;
1222       if (lpFormat->Grouping == 32)
1223         dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1224     }
1225   }
1226   if (dwState & NF_ROUND)
1227   {
1228     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1229   }
1230   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1231     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1232
1233   /* Add any leading negative sign */
1234   if (dwState & NF_ISNEGATIVE)
1235   {
1236     switch (lpFormat->NegativeOrder)
1237     {
1238     case NLS_NEG_PARENS:
1239       *szOut-- = '(';
1240       break;
1241     case NLS_NEG_LEFT_SPACE:
1242       *szOut-- = ' ';
1243       /* Fall through */
1244     case NLS_NEG_LEFT:
1245       while (lpszNeg >= lpszNegStart)
1246         *szOut-- = *lpszNeg--;
1247       break;
1248     }
1249   }
1250   szOut++;
1251
1252   iRet = strlenW(szOut) + 1;
1253   if (cchOut)
1254   {
1255     if (iRet <= cchOut)
1256       memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
1257     else
1258     {
1259       memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
1260       lpNumberStr[cchOut - 1] = '\0';
1261       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1262       iRet = 0;
1263     }
1264   }
1265   return iRet;
1266 }
1267
1268 /**************************************************************************
1269  *              GetCurrencyFormatA      (KERNEL32.@)
1270  *
1271  * Format a currency string for a given locale.
1272  *
1273  * PARAMS
1274  *  lcid          [I] Locale to format for
1275  *  dwFlags       [I] LOCALE_ flags from "winnls.h"
1276  *  lpszValue     [I] String to format
1277  *  lpFormat      [I] Formatting overrides
1278  *  lpCurrencyStr [O] Destination for formatted string
1279  *  cchOut        [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1280  *
1281  * NOTES
1282  *  - lpszValue can contain only '0' - '9', '-' and '.'.
1283  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1284  *    be formatted according to the format details returned by GetLocaleInfoA().
1285  *  - This function rounds the currency if the number of decimals exceeds the
1286  *    locales number of currency decimal places.
1287  *  - If cchOut is 0, this function does not write to lpCurrencyStr.
1288  *  - The Ascii version of this function fails if lcid is Unicode only.
1289  *
1290  * RETURNS
1291  *  Success: The number of character written to lpNumberStr, or that would
1292  *           have been written, if cchOut is 0.
1293  *  Failure: 0. Use GetLastError() to determine the cause.
1294  */
1295 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
1296                               LPCSTR lpszValue,  const CURRENCYFMTA *lpFormat,
1297                               LPSTR lpCurrencyStr, int cchOut)
1298 {
1299   DWORD cp = CP_ACP;
1300   WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
1301   CURRENCYFMTW fmt;
1302   const CURRENCYFMTW *pfmt = NULL;
1303   INT iRet;
1304
1305   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
1306         lpFormat, lpCurrencyStr, cchOut);
1307
1308   if (NLS_IsUnicodeOnlyLcid(lcid))
1309   {
1310 GetCurrencyFormatA_InvalidParameter:
1311     SetLastError(ERROR_INVALID_PARAMETER);
1312     return 0;
1313   }
1314
1315   if (!(dwFlags & LOCALE_USE_CP_ACP))
1316   {
1317     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1318     if (!node)
1319       goto GetCurrencyFormatA_InvalidParameter;
1320     cp = node->dwCodePage;
1321   }
1322
1323   if (lpFormat)
1324   {
1325     memcpy(&fmt, lpFormat, sizeof(fmt));
1326     pfmt = &fmt;
1327     if (lpFormat->lpDecimalSep)
1328     {
1329       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
1330       fmt.lpDecimalSep = szDec;
1331     }
1332     if (lpFormat->lpThousandSep)
1333     {
1334       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
1335       fmt.lpThousandSep = szGrp;
1336     }
1337     if (lpFormat->lpCurrencySymbol)
1338     {
1339       MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR));
1340       fmt.lpCurrencySymbol = szCy;
1341     }
1342   }
1343
1344   if (lpszValue)
1345     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
1346
1347   if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1348     cchOut = sizeof(szOut)/sizeof(WCHAR);
1349
1350   szOut[0] = '\0';
1351
1352   iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1353                             lpCurrencyStr ? szOut : NULL, cchOut);
1354
1355   if (szOut[0] && lpCurrencyStr)
1356     WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
1357   return iRet;
1358 }
1359
1360 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1361 #define CF_PARENS       0x1  /* Parentheses      */
1362 #define CF_MINUS_LEFT   0x2  /* '-' to the left  */
1363 #define CF_MINUS_RIGHT  0x4  /* '-' to the right */
1364 #define CF_MINUS_BEFORE 0x8  /* '-' before '$'   */
1365 #define CF_CY_LEFT      0x10 /* '$' to the left  */
1366 #define CF_CY_RIGHT     0x20 /* '$' to the right */
1367 #define CF_CY_SPACE     0x40 /* ' ' by '$'       */
1368
1369 /**************************************************************************
1370  *              GetCurrencyFormatW      (KERNEL32.@)
1371  *
1372  * See GetCurrencyFormatA.
1373  */
1374 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
1375                               LPCWSTR lpszValue,  const CURRENCYFMTW *lpFormat,
1376                               LPWSTR lpCurrencyStr, int cchOut)
1377 {
1378   static const BYTE NLS_NegCyFormats[16] =
1379   {
1380     CF_PARENS|CF_CY_LEFT,                       /* ($1.1) */
1381     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT,   /* -$1.1  */
1382     CF_MINUS_LEFT|CF_CY_LEFT,                   /* $-1.1  */
1383     CF_MINUS_RIGHT|CF_CY_LEFT,                  /* $1.1-  */
1384     CF_PARENS|CF_CY_RIGHT,                      /* (1.1$) */
1385     CF_MINUS_LEFT|CF_CY_RIGHT,                  /* -1.1$  */
1386     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$  */
1387     CF_MINUS_RIGHT|CF_CY_RIGHT,                 /* 1.1$-  */
1388     CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE,      /* -1.1 $ */
1389     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE,   /* -$ 1.1 */
1390     CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE,     /* 1.1 $-  */
1391     CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE,      /* $ 1.1-  */
1392     CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE,       /* $ -1.1  */
1393     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
1394     CF_PARENS|CF_CY_LEFT|CF_CY_SPACE,           /* ($ 1.1) */
1395     CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE,          /* (1.1 $) */
1396   };
1397   static const BYTE NLS_PosCyFormats[4] =
1398   {
1399     CF_CY_LEFT,              /* $1.1  */
1400     CF_CY_RIGHT,             /* 1.1$  */
1401     CF_CY_LEFT|CF_CY_SPACE,  /* $ 1.1 */
1402     CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
1403   };
1404   WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1405   WCHAR szNegBuff[8];
1406   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
1407   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
1408   INT iRet;
1409
1410   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1411         lpFormat, lpCurrencyStr, cchOut);
1412
1413   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
1414       !IsValidLocale(lcid, 0) ||
1415       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
1416       !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
1417       lpFormat->PositiveOrder > 3)))
1418   {
1419 GetCurrencyFormatW_Error:
1420     SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1421     return 0;
1422   }
1423
1424   if (!lpFormat)
1425   {
1426     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1427
1428     if (!node)
1429       goto GetCurrencyFormatW_Error;
1430     lpFormat = &node->cyfmt;
1431     lpszNegStart = lpszNeg = GetNegative(node);
1432   }
1433   else
1434   {
1435     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1436                    szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1437     lpszNegStart = lpszNeg = szNegBuff;
1438   }
1439   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1440
1441   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1442   lpszCyStart = lpFormat->lpCurrencySymbol;
1443   lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
1444
1445   /* Format the currency backwards into a temporary buffer */
1446
1447   szSrc = lpszValue;
1448   *szOut-- = '\0';
1449
1450   /* Check the number for validity */
1451   while (*szSrc)
1452   {
1453     if (*szSrc >= '0' && *szSrc <= '9')
1454     {
1455       dwState |= NF_DIGITS;
1456       if (dwState & NF_ISREAL)
1457         dwDecimals++;
1458     }
1459     else if (*szSrc == '-')
1460     {
1461       if (dwState)
1462         goto GetCurrencyFormatW_Error; /* '-' not first character */
1463       dwState |= NF_ISNEGATIVE;
1464     }
1465     else if (*szSrc == '.')
1466     {
1467       if (dwState & NF_ISREAL)
1468         goto GetCurrencyFormatW_Error; /* More than one '.' */
1469       dwState |= NF_ISREAL;
1470     }
1471     else
1472       goto GetCurrencyFormatW_Error; /* Invalid char */
1473     szSrc++;
1474   }
1475   szSrc--; /* Point to last character */
1476
1477   if (!(dwState & NF_DIGITS))
1478     goto GetCurrencyFormatW_Error; /* No digits */
1479
1480   if (dwState & NF_ISNEGATIVE)
1481     dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
1482   else
1483     dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
1484
1485   /* Add any trailing negative or currency signs */
1486   if (dwFmt & CF_PARENS)
1487     *szOut-- = ')';
1488
1489   while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
1490   {
1491     switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
1492     {
1493     case CF_MINUS_RIGHT:
1494     case CF_MINUS_RIGHT|CF_CY_RIGHT:
1495       while (lpszNeg >= lpszNegStart)
1496         *szOut-- = *lpszNeg--;
1497       dwFmt &= ~CF_MINUS_RIGHT;
1498       break;
1499
1500     case CF_CY_RIGHT:
1501     case CF_MINUS_BEFORE|CF_CY_RIGHT:
1502     case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
1503       while (lpszCy >= lpszCyStart)
1504         *szOut-- = *lpszCy--;
1505       if (dwFmt & CF_CY_SPACE)
1506         *szOut-- = ' ';
1507       dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
1508       break;
1509     }
1510   }
1511
1512   /* Copy all digits up to the decimal point */
1513   if (!lpFormat->NumDigits)
1514   {
1515     if (dwState & NF_ISREAL)
1516     {
1517       while (*szSrc != '.') /* Don't write any decimals or a separator */
1518       {
1519         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1520           dwState |= NF_ROUND;
1521         else
1522           dwState &= ~NF_ROUND;
1523         szSrc--;
1524       }
1525       szSrc--;
1526     }
1527   }
1528   else
1529   {
1530     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1531
1532     if (dwDecimals <= lpFormat->NumDigits)
1533     {
1534       dwDecimals = lpFormat->NumDigits - dwDecimals;
1535       while (dwDecimals--)
1536         *szOut-- = '0'; /* Pad to correct number of dp */
1537     }
1538     else
1539     {
1540       dwDecimals -= lpFormat->NumDigits;
1541       /* Skip excess decimals, and determine if we have to round the number */
1542       while (dwDecimals--)
1543       {
1544         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1545           dwState |= NF_ROUND;
1546         else
1547           dwState &= ~NF_ROUND;
1548         szSrc--;
1549       }
1550     }
1551
1552     if (dwState & NF_ISREAL)
1553     {
1554       while (*szSrc != '.')
1555       {
1556         if (dwState & NF_ROUND)
1557         {
1558           if (*szSrc == '9')
1559             *szOut-- = '0'; /* continue rounding */
1560           else
1561           {
1562             dwState &= ~NF_ROUND;
1563             *szOut-- = (*szSrc)+1;
1564           }
1565           szSrc--;
1566         }
1567         else
1568           *szOut-- = *szSrc--; /* Write existing decimals */
1569       }
1570       szSrc--; /* Skip '.' */
1571     }
1572     while (lpszDec >= lpFormat->lpDecimalSep)
1573       *szOut-- = *lpszDec--; /* Write decimal separator */
1574   }
1575
1576   dwGroupCount = lpFormat->Grouping;
1577
1578   /* Write the remaining whole number digits, including grouping chars */
1579   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1580   {
1581     if (dwState & NF_ROUND)
1582     {
1583       if (*szSrc == '9')
1584         *szOut-- = '0'; /* continue rounding */
1585       else
1586       {
1587         dwState &= ~NF_ROUND;
1588         *szOut-- = (*szSrc)+1;
1589       }
1590       szSrc--;
1591     }
1592     else
1593       *szOut-- = *szSrc--;
1594
1595     dwState |= NF_DIGITS_OUT;
1596     dwCurrentGroupCount++;
1597     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
1598     {
1599       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1600
1601       while (lpszGrp >= lpFormat->lpThousandSep)
1602         *szOut-- = *lpszGrp--; /* Write grouping char */
1603
1604       dwCurrentGroupCount = 0;
1605     }
1606   }
1607   if (dwState & NF_ROUND)
1608     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1609   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1610     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1611
1612   /* Add any leading negative or currency sign */
1613   while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
1614   {
1615     switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
1616     {
1617     case CF_MINUS_LEFT:
1618     case CF_MINUS_LEFT|CF_CY_LEFT:
1619       while (lpszNeg >= lpszNegStart)
1620         *szOut-- = *lpszNeg--;
1621       dwFmt &= ~CF_MINUS_LEFT;
1622       break;
1623
1624     case CF_CY_LEFT:
1625     case CF_CY_LEFT|CF_MINUS_BEFORE:
1626     case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
1627       if (dwFmt & CF_CY_SPACE)
1628         *szOut-- = ' ';
1629       while (lpszCy >= lpszCyStart)
1630         *szOut-- = *lpszCy--;
1631       dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
1632       break;
1633     }
1634   }
1635   if (dwFmt & CF_PARENS)
1636     *szOut-- = '(';
1637   szOut++;
1638
1639   iRet = strlenW(szOut) + 1;
1640   if (cchOut)
1641   {
1642     if (iRet <= cchOut)
1643       memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
1644     else
1645     {
1646       memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
1647       lpCurrencyStr[cchOut - 1] = '\0';
1648       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1649       iRet = 0;
1650     }
1651   }
1652   return iRet;
1653 }
1654
1655 /* FIXME: Everything below here needs to move somewhere else along with the
1656  *        other EnumXXX functions, when a method for storing resources for
1657  *        alternate calendars is determined.
1658  */
1659
1660 /**************************************************************************
1661  *              EnumDateFormatsExA    (KERNEL32.@)
1662  *
1663  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1664  * LOCALE_NOUSEROVERRIDE here as well?
1665  */
1666 BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags)
1667 {
1668     CALID cal_id;
1669     char buf[256];
1670
1671     if (!proc)
1672     {
1673         SetLastError(ERROR_INVALID_PARAMETER);
1674         return FALSE;
1675     }
1676
1677     if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
1678         return FALSE;
1679
1680     switch (flags & ~LOCALE_USE_CP_ACP)
1681     {
1682     case 0:
1683     case DATE_SHORTDATE:
1684         if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1685             proc(buf, cal_id);
1686         break;
1687
1688     case DATE_LONGDATE:
1689         if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1690             proc(buf, cal_id);
1691         break;
1692
1693     case DATE_YEARMONTH:
1694         if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1695             proc(buf, cal_id);
1696         break;
1697
1698     default:
1699         FIXME("Unknown date format (%d)\n", flags);
1700         SetLastError(ERROR_INVALID_PARAMETER);
1701         return FALSE;
1702     }
1703     return TRUE;
1704 }
1705
1706 /**************************************************************************
1707  *              EnumDateFormatsExW    (KERNEL32.@)
1708  */
1709 BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags)
1710 {
1711     CALID cal_id;
1712     WCHAR buf[256];
1713
1714     if (!proc)
1715     {
1716         SetLastError(ERROR_INVALID_PARAMETER);
1717         return FALSE;
1718     }
1719
1720     if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
1721         return FALSE;
1722
1723     switch (flags & ~LOCALE_USE_CP_ACP)
1724     {
1725     case 0:
1726     case DATE_SHORTDATE:
1727         if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1728             proc(buf, cal_id);
1729         break;
1730
1731     case DATE_LONGDATE:
1732         if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1733             proc(buf, cal_id);
1734         break;
1735
1736     case DATE_YEARMONTH:
1737         if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1738             proc(buf, cal_id);
1739         break;
1740
1741     default:
1742         FIXME("Unknown date format (%d)\n", flags);
1743         SetLastError(ERROR_INVALID_PARAMETER);
1744         return FALSE;
1745     }
1746     return TRUE;
1747 }
1748
1749 /**************************************************************************
1750  *              EnumDateFormatsA        (KERNEL32.@)
1751  *
1752  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1753  * LOCALE_NOUSEROVERRIDE here as well?
1754  */
1755 BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
1756 {
1757     char buf[256];
1758
1759     if (!proc)
1760     {
1761         SetLastError(ERROR_INVALID_PARAMETER);
1762         return FALSE;
1763     }
1764
1765     switch (flags & ~LOCALE_USE_CP_ACP)
1766     {
1767     case 0:
1768     case DATE_SHORTDATE:
1769         if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1770             proc(buf);
1771         break;
1772
1773     case DATE_LONGDATE:
1774         if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1775             proc(buf);
1776         break;
1777
1778     case DATE_YEARMONTH:
1779         if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1780             proc(buf);
1781         break;
1782
1783     default:
1784         FIXME("Unknown date format (%d)\n", flags);
1785         SetLastError(ERROR_INVALID_PARAMETER);
1786         return FALSE;
1787     }
1788     return TRUE;
1789 }
1790
1791 /**************************************************************************
1792  *              EnumDateFormatsW        (KERNEL32.@)
1793  */
1794 BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
1795 {
1796     WCHAR buf[256];
1797
1798     if (!proc)
1799     {
1800         SetLastError(ERROR_INVALID_PARAMETER);
1801         return FALSE;
1802     }
1803
1804     switch (flags & ~LOCALE_USE_CP_ACP)
1805     {
1806     case 0:
1807     case DATE_SHORTDATE:
1808         if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1809             proc(buf);
1810         break;
1811
1812     case DATE_LONGDATE:
1813         if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1814             proc(buf);
1815         break;
1816
1817     case DATE_YEARMONTH:
1818         if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1819             proc(buf);
1820         break;
1821
1822     default:
1823         FIXME("Unknown date format (%d)\n", flags);
1824         SetLastError(ERROR_INVALID_PARAMETER);
1825         return FALSE;
1826     }
1827     return TRUE;
1828 }
1829
1830 /**************************************************************************
1831  *              EnumTimeFormatsA        (KERNEL32.@)
1832  *
1833  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1834  * LOCALE_NOUSEROVERRIDE here as well?
1835  */
1836 BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
1837 {
1838     char buf[256];
1839
1840     if (!proc)
1841     {
1842         SetLastError(ERROR_INVALID_PARAMETER);
1843         return FALSE;
1844     }
1845
1846     switch (flags & ~LOCALE_USE_CP_ACP)
1847     {
1848     case 0:
1849         if (GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
1850             proc(buf);
1851         break;
1852
1853     default:
1854         FIXME("Unknown time format (%d)\n", flags);
1855         SetLastError(ERROR_INVALID_PARAMETER);
1856         return FALSE;
1857     }
1858     return TRUE;
1859 }
1860
1861 /**************************************************************************
1862  *              EnumTimeFormatsW        (KERNEL32.@)
1863  */
1864 BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
1865 {
1866     WCHAR buf[256];
1867
1868     if (!proc)
1869     {
1870         SetLastError(ERROR_INVALID_PARAMETER);
1871         return FALSE;
1872     }
1873
1874     switch (flags & ~LOCALE_USE_CP_ACP)
1875     {
1876     case 0:
1877         if (GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
1878             proc(buf);
1879         break;
1880
1881     default:
1882         FIXME("Unknown time format (%d)\n", flags);
1883         SetLastError(ERROR_INVALID_PARAMETER);
1884         return FALSE;
1885     }
1886     return TRUE;
1887 }
1888
1889 /******************************************************************************
1890  * NLS_EnumCalendarInfoAW <internal>
1891  * Enumerates calendar information for a specified locale.
1892  *
1893  * PARAMS
1894  *    calinfoproc [I] Pointer to the callback
1895  *    locale      [I] The locale for which to retrieve calendar information.
1896  *                    This parameter can be a locale identifier created by the
1897  *                    MAKELCID macro, or one of the following values:
1898  *                        LOCALE_SYSTEM_DEFAULT
1899  *                            Use the default system locale.
1900  *                        LOCALE_USER_DEFAULT
1901  *                            Use the default user locale.
1902  *    calendar    [I] The calendar for which information is requested, or
1903  *                    ENUM_ALL_CALENDARS.
1904  *    caltype     [I] The type of calendar information to be returned. Note
1905  *                    that only one CALTYPE value can be specified per call
1906  *                    of this function, except where noted.
1907  *    unicode     [I] Specifies if the callback expects a unicode string.
1908  *    ex          [I] Specifies if the callback needs the calendar identifier.
1909  *
1910  * RETURNS
1911  *    Success: TRUE.
1912  *    Failure: FALSE. Use GetLastError() to determine the cause.
1913  *
1914  * NOTES
1915  *    When the ANSI version of this function is used with a Unicode-only LCID,
1916  *    the call can succeed because the system uses the system code page.
1917  *    However, characters that are undefined in the system code page appear
1918  *    in the string as a question mark (?).
1919  *
1920  * TODO
1921  *    The above note should be respected by GetCalendarInfoA.
1922  */
1923 static BOOL NLS_EnumCalendarInfoAW(void *calinfoproc, LCID locale,
1924                   CALID calendar, CALTYPE caltype, BOOL unicode, BOOL ex )
1925 {
1926   WCHAR *buf, *opt = NULL, *iter = NULL;
1927   BOOL ret = FALSE;
1928   int bufSz = 200;              /* the size of the buffer */
1929
1930   if (calinfoproc == NULL)
1931   {
1932     SetLastError(ERROR_INVALID_PARAMETER);
1933     return FALSE;
1934   }
1935
1936   buf = HeapAlloc(GetProcessHeap(), 0, bufSz);
1937   if (buf == NULL)
1938   {
1939     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1940     return FALSE;
1941   }
1942
1943   if (calendar == ENUM_ALL_CALENDARS)
1944   {
1945     int optSz = GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, NULL, 0);
1946     if (optSz > 1)
1947     {
1948       opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR));
1949       if (opt == NULL)
1950       {
1951         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1952         goto NLS_EnumCalendarInfoAW_Cleanup;
1953       }
1954       if (GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, opt, optSz))
1955         iter = opt;
1956     }
1957     calendar = NLS_GetLocaleNumber(locale, LOCALE_ICALENDARTYPE);
1958   }
1959
1960   while (TRUE)                  /* loop through calendars */
1961   {
1962     do                          /* loop until there's no error */
1963     {
1964       if (unicode)
1965         ret = GetCalendarInfoW(locale, calendar, caltype, buf, bufSz / sizeof(WCHAR), NULL);
1966       else ret = GetCalendarInfoA(locale, calendar, caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL);
1967
1968       if (!ret)
1969       {
1970         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1971         {                               /* so resize it */
1972           int newSz;
1973           if (unicode)
1974             newSz = GetCalendarInfoW(locale, calendar, caltype, NULL, 0, NULL) * sizeof(WCHAR);
1975           else newSz = GetCalendarInfoA(locale, calendar, caltype, NULL, 0, NULL) * sizeof(CHAR);
1976           if (bufSz >= newSz)
1977           {
1978             ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz);
1979             goto NLS_EnumCalendarInfoAW_Cleanup;
1980           }
1981           bufSz = newSz;
1982           WARN("Buffer too small; resizing to %d bytes.\n", bufSz);
1983           buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz);
1984           if (buf == NULL)
1985             goto NLS_EnumCalendarInfoAW_Cleanup;
1986         } else goto NLS_EnumCalendarInfoAW_Cleanup;
1987       }
1988     } while (!ret);
1989
1990     /* Here we are. We pass the buffer to the correct version of
1991      * the callback. Because it's not the same number of params,
1992      * we must check for Ex, but we don't care about Unicode
1993      * because the buffer is already in the correct format.
1994      */
1995     if (ex) {
1996       ret = ((CALINFO_ENUMPROCEXW)calinfoproc)(buf, calendar);
1997     } else
1998       ret = ((CALINFO_ENUMPROCW)calinfoproc)(buf);
1999
2000     if (!ret) {                 /* the callback told to stop */
2001       ret = TRUE;
2002       break;
2003     }
2004
2005     if ((iter == NULL) || (*iter == 0)) /* no more calendars */
2006       break;
2007
2008     calendar = 0;
2009     while ((*iter >= '0') && (*iter <= '9'))
2010       calendar = calendar * 10 + *iter++ - '0';
2011
2012     if (*iter++ != 0)
2013     {
2014       SetLastError(ERROR_BADDB);
2015       ret = FALSE;
2016       break;
2017     }
2018   }
2019
2020 NLS_EnumCalendarInfoAW_Cleanup:
2021   HeapFree(GetProcessHeap(), 0, opt);
2022   HeapFree(GetProcessHeap(), 0, buf);
2023   return ret;
2024 }
2025
2026 /******************************************************************************
2027  *              EnumCalendarInfoA       [KERNEL32.@]
2028  *
2029  * See EnumCalendarInfoAW.
2030  */
2031 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
2032                                CALID calendar,CALTYPE caltype )
2033 {
2034   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2035   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, FALSE);
2036 }
2037
2038 /******************************************************************************
2039  *              EnumCalendarInfoW       [KERNEL32.@]
2040  *
2041  * See EnumCalendarInfoAW.
2042  */
2043 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale,
2044                                CALID calendar,CALTYPE caltype )
2045 {
2046   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2047   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, FALSE);
2048 }
2049
2050 /******************************************************************************
2051  *              EnumCalendarInfoExA     [KERNEL32.@]
2052  *
2053  * See EnumCalendarInfoAW.
2054  */
2055 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale,
2056                                  CALID calendar,CALTYPE caltype )
2057 {
2058   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2059   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, TRUE);
2060 }
2061
2062 /******************************************************************************
2063  *              EnumCalendarInfoExW     [KERNEL32.@]
2064  *
2065  * See EnumCalendarInfoAW.
2066  */
2067 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale,
2068                                  CALID calendar,CALTYPE caltype )
2069 {
2070   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2071   return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, TRUE);
2072 }