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 #ifdef __REACTOS__
26 
27 #include <k32.h>
28 #include "japanese.h"   /* Japanese eras */
29 
30 #define NDEBUG
31 #include <debug.h>
32 DEBUG_CHANNEL(nls);
33 
34 #undef _WIN32_WINNT
35 #undef WINVER
36 #define _WIN32_WINNT DLL_EXPORT_VERSION
37 #define WINVER DLL_EXPORT_VERSION
38 
39 LCID WINAPI LocaleNameToLCID(_In_ LPCWSTR, _In_ DWORD);
40 #define LOCALE_SSHORTESTDAYNAME1 96
41 #define LOCALE_SSHORTESTDAYNAME2 97
42 #define LOCALE_SSHORTESTDAYNAME3 98
43 #define LOCALE_SSHORTESTDAYNAME4 99
44 #define LOCALE_SSHORTESTDAYNAME5 100
45 #define LOCALE_SSHORTESTDAYNAME6 101
46 #define LOCALE_SSHORTESTDAYNAME7 102
47 
48 #define CRITICAL_SECTION RTL_CRITICAL_SECTION
49 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG
50 #define CALINFO_MAX_YEAR 2029
51 
52 #define IS_LCID_JAPANESE(lcid) PRIMARYLANGID(LANGIDFROMLCID(lcid)) == LANG_JAPANESE
53 
54 #ifndef CAL_SABBREVERASTRING
55     #define CAL_SABBREVERASTRING 0x00000039
56 #endif
57 
58 #else /* __REACTOS__ */
59 
60 #include "config.h"
61 #include "wine/port.h"
62 
63 #include <string.h>
64 #include <stdarg.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 
68 #include "windef.h"
69 #include "winbase.h"
70 #include "wine/unicode.h"
71 #include "wine/debug.h"
72 #include "winternl.h"
73 
74 #include "kernel_private.h"
75 
76 WINE_DEFAULT_DEBUG_CHANNEL(nls);
77 
78 #endif /* __REACTOS__ */
79 
80 #define DATE_DATEVARSONLY 0x0100  /* only date stuff: yMdg */
81 #define TIME_TIMEVARSONLY 0x0200  /* only time stuff: hHmst */
82 
83 /* Since calculating the formatting data for each locale is time-consuming,
84  * we get the format data for each locale only once and cache it in memory.
85  * We cache both the system default and user overridden data, after converting
86  * them into the formats that the functions here expect. Since these functions
87  * will typically be called with only a small number of the total locales
88  * installed, the memory overhead is minimal while the speedup is significant.
89  *
90  * Our cache takes the form of a singly linked list, whose node is below:
91  */
92 #define NLS_NUM_CACHED_STRINGS 57
93 
94 typedef struct _NLS_FORMAT_NODE
95 {
96   LCID  lcid;         /* Locale Id */
97   DWORD dwFlags;      /* 0 or LOCALE_NOUSEROVERRIDE */
98   DWORD dwCodePage;   /* Default code page (if LOCALE_USE_ANSI_CP not given) */
99   NUMBERFMTW   fmt;   /* Default format for numbers */
100   CURRENCYFMTW cyfmt; /* Default format for currencies */
101   LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
102   WCHAR szShortAM[2]; /* Short 'AM' marker */
103   WCHAR szShortPM[2]; /* Short 'PM' marker */
104   struct _NLS_FORMAT_NODE *next;
105 } NLS_FORMAT_NODE;
106 
107 /* Macros to get particular data strings from a format node */
108 #define GetNegative(fmt)  fmt->lppszStrings[0]
109 #define GetLongDate(fmt)  fmt->lppszStrings[1]
110 #define GetShortDate(fmt) fmt->lppszStrings[2]
111 #define GetTime(fmt)      fmt->lppszStrings[3]
112 #define GetAM(fmt)        fmt->lppszStrings[54]
113 #define GetPM(fmt)        fmt->lppszStrings[55]
114 #define GetYearMonth(fmt) fmt->lppszStrings[56]
115 
116 #define GetLongDay(fmt,day)       fmt->lppszStrings[4 + day]
117 #define GetShortDay(fmt,day)      fmt->lppszStrings[11 + day]
118 #define GetLongMonth(fmt,mth)     fmt->lppszStrings[18 + mth]
119 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
120 #define GetShortMonth(fmt,mth)    fmt->lppszStrings[42 + mth]
121 
122 /* Write access to the cache is protected by this critical section */
123 static CRITICAL_SECTION NLS_FormatsCS;
124 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
125 {
126     0, 0, &NLS_FormatsCS,
127     { &NLS_FormatsCS_debug.ProcessLocksList,
128       &NLS_FormatsCS_debug.ProcessLocksList },
129 #ifdef __REACTOS__
130       0, 0, 0
131 #else
132       0, 0, { (DWORD_PTR)(__FILE__ ": NLS_Formats") }
133 #endif
134 };
135 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
136 
137 /**************************************************************************
138  * NLS_GetLocaleNumber <internal>
139  *
140  * Get a numeric locale format value.
141  */
142 static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
143 {
144   WCHAR szBuff[80];
145   DWORD dwVal = 0;
146 
147   szBuff[0] = '\0';
148   GetLocaleInfoW(lcid, dwFlags, szBuff, ARRAY_SIZE(szBuff));
149 
150   if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
151     dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
152   else
153   {
154     const WCHAR* iter = szBuff;
155     dwVal = 0;
156     while(*iter >= '0' && *iter <= '9')
157       dwVal = dwVal * 10 + (*iter++ - '0');
158   }
159   return dwVal;
160 }
161 
162 /**************************************************************************
163  * NLS_GetLocaleString <internal>
164  *
165  * Get a string locale format value.
166  */
167 static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
168 {
169   WCHAR szBuff[80], *str;
170   DWORD dwLen;
171 
172   szBuff[0] = '\0';
173   GetLocaleInfoW(lcid, dwFlags, szBuff, ARRAY_SIZE(szBuff));
174   dwLen = strlenW(szBuff) + 1;
175   str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
176   if (str)
177     memcpy(str, szBuff, dwLen * sizeof(WCHAR));
178   return str;
179 }
180 
181 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
182   TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
183 
184 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
185   TRACE( #type ": %s\n", debugstr_w(str))
186 
187 /**************************************************************************
188  * NLS_GetFormats <internal>
189  *
190  * Calculate (and cache) the number formats for a locale.
191  */
192 static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
193 {
194   /* GetLocaleInfo() identifiers for cached formatting strings */
195   static const LCTYPE NLS_LocaleIndices[] = {
196     LOCALE_SNEGATIVESIGN,
197     LOCALE_SLONGDATE,   LOCALE_SSHORTDATE,
198     LOCALE_STIMEFORMAT,
199     LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
200     LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
201     LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
202     LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
203     LOCALE_SABBREVDAYNAME7,
204     LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
205     LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
206     LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
207     LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
208     LOCALE_SMONTHNAME1  | LOCALE_RETURN_GENITIVE_NAMES,
209     LOCALE_SMONTHNAME2  | LOCALE_RETURN_GENITIVE_NAMES,
210     LOCALE_SMONTHNAME3  | LOCALE_RETURN_GENITIVE_NAMES,
211     LOCALE_SMONTHNAME4  | LOCALE_RETURN_GENITIVE_NAMES,
212     LOCALE_SMONTHNAME5  | LOCALE_RETURN_GENITIVE_NAMES,
213     LOCALE_SMONTHNAME6  | LOCALE_RETURN_GENITIVE_NAMES,
214     LOCALE_SMONTHNAME7  | LOCALE_RETURN_GENITIVE_NAMES,
215     LOCALE_SMONTHNAME8  | LOCALE_RETURN_GENITIVE_NAMES,
216     LOCALE_SMONTHNAME9  | LOCALE_RETURN_GENITIVE_NAMES,
217     LOCALE_SMONTHNAME10 | LOCALE_RETURN_GENITIVE_NAMES,
218     LOCALE_SMONTHNAME11 | LOCALE_RETURN_GENITIVE_NAMES,
219     LOCALE_SMONTHNAME12 | LOCALE_RETURN_GENITIVE_NAMES,
220     LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
221     LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
222     LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
223     LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
224     LOCALE_S1159, LOCALE_S2359,
225     LOCALE_SYEARMONTH
226   };
227   static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
228   NLS_FORMAT_NODE *node = NLS_CachedFormats;
229 
230   dwFlags &= LOCALE_NOUSEROVERRIDE;
231 
232   TRACE("(0x%04x,0x%08x)\n", lcid, dwFlags);
233 
234   /* See if we have already cached the locales number format */
235   while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
236     node = node->next;
237 
238   if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
239   {
240     NLS_FORMAT_NODE *new_node;
241     DWORD i;
242 
243     TRACE("Creating new cache entry\n");
244 
245     if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
246       return NULL;
247 
248     GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
249 
250     /* Number Format */
251     new_node->lcid = lcid;
252     new_node->dwFlags = dwFlags;
253     new_node->next = NULL;
254 
255     GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
256     GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
257     GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
258 
259     GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
260     if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
261     {
262       WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
263            new_node->fmt.Grouping);
264       new_node->fmt.Grouping = 0;
265     }
266 
267     GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
268     GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
269 
270     /* Currency Format */
271     new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
272     new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
273 
274     GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
275 
276     if (new_node->cyfmt.Grouping > 9)
277     {
278       WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
279            new_node->cyfmt.Grouping);
280       new_node->cyfmt.Grouping = 0;
281     }
282 
283     GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
284     if (new_node->cyfmt.NegativeOrder > 15)
285     {
286       WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
287            new_node->cyfmt.NegativeOrder);
288       new_node->cyfmt.NegativeOrder = 0;
289     }
290     GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
291     if (new_node->cyfmt.PositiveOrder > 3)
292     {
293       WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
294            new_node->cyfmt.PositiveOrder);
295       new_node->cyfmt.PositiveOrder = 0;
296     }
297     GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
298     GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
299     GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
300 
301     /* Date/Time Format info, negative character, etc */
302     for (i = 0; i < ARRAY_SIZE(NLS_LocaleIndices); i++)
303     {
304       GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
305     }
306     /* Save some memory if month genitive name is the same or not present */
307     for (i = 0; i < 12; i++)
308     {
309       if (strcmpW(GetLongMonth(new_node, i), GetGenitiveMonth(new_node, i)) == 0)
310       {
311         HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node, i));
312         GetGenitiveMonth(new_node, i) = NULL;
313       }
314     }
315 
316     new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
317     new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
318 
319     /* Now add the computed format to the cache */
320     RtlEnterCriticalSection(&NLS_FormatsCS);
321 
322     /* Search again: We may have raced to add the node */
323     node = NLS_CachedFormats;
324     while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
325       node = node->next;
326 
327     if (!node)
328     {
329       node = NLS_CachedFormats = new_node; /* Empty list */
330       new_node = NULL;
331     }
332     else if (node->lcid != lcid || node->dwFlags != dwFlags)
333     {
334       node->next = new_node; /* Not in the list, add to end */
335       node = new_node;
336       new_node = NULL;
337     }
338 
339     RtlLeaveCriticalSection(&NLS_FormatsCS);
340 
341     if (new_node)
342     {
343       /* We raced and lost: The node was already added by another thread.
344        * node points to the currently cached node, so free new_node.
345        */
346       for (i = 0; i < ARRAY_SIZE(NLS_LocaleIndices); i++)
347         HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
348       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
349       HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
350       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
351       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
352       HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
353       HeapFree(GetProcessHeap(), 0, new_node);
354     }
355   }
356   return node;
357 }
358 
359 /**************************************************************************
360  * NLS_IsUnicodeOnlyLcid <internal>
361  *
362  * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
363  */
364 BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
365 {
366   lcid = ConvertDefaultLocale(lcid);
367 
368   switch (PRIMARYLANGID(lcid))
369   {
370   case LANG_ARMENIAN:
371   case LANG_DIVEHI:
372   case LANG_GEORGIAN:
373   case LANG_GUJARATI:
374   case LANG_HINDI:
375   case LANG_KANNADA:
376   case LANG_KONKANI:
377   case LANG_MARATHI:
378   case LANG_PUNJABI:
379   case LANG_SANSKRIT:
380     TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
381     return TRUE;
382   default:
383     return FALSE;
384   }
385 }
386 
387 /*
388  * Formatting of dates, times, numbers and currencies.
389  */
390 
391 #define IsLiteralMarker(p) (p == '\'')
392 #define IsDateFmtChar(p)   (p == 'd'||p == 'M'||p == 'y'||p == 'g')
393 #define IsTimeFmtChar(p)   (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
394 
395 /* Only the following flags can be given if a date/time format is specified */
396 #ifdef __REACTOS__
397 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY | DATE_USE_ALT_CALENDAR)
398 #else
399 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
400 #endif
401 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
402                            TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
403                            TIME_NOTIMEMARKER)
404 
405 /******************************************************************************
406  * NLS_GetDateTimeFormatW <internal>
407  *
408  * Performs the formatting for GetDateFormatW/GetTimeFormatW.
409  *
410  * FIXME
411  * DATE_USE_ALT_CALENDAR           - Requires GetCalendarInfo to work first.
412  * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
413  */
414 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
415                                   const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
416                                   LPWSTR lpStr, INT cchOut)
417 {
418   const NLS_FORMAT_NODE *node;
419   SYSTEMTIME st;
420   INT cchWritten = 0;
421   INT lastFormatPos = 0;
422   BOOL bSkipping = FALSE; /* Skipping text around marker? */
423   BOOL d_dd_formatted = FALSE; /* previous formatted part was for d or dd */
424 
425   /* Verify our arguments */
426   if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
427     goto invalid_parameter;
428 
429   if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
430   {
431     if (lpFormat &&
432         ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
433          (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
434     {
435       goto invalid_flags;
436     }
437 
438     if (dwFlags & DATE_DATEVARSONLY)
439     {
440       if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
441         goto invalid_flags;
442       else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
443         FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
444 
445       switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
446       {
447       case 0:
448         break;
449       case DATE_SHORTDATE:
450       case DATE_LONGDATE:
451       case DATE_YEARMONTH:
452         if (lpFormat)
453           goto invalid_flags;
454         break;
455       default:
456         goto invalid_flags;
457       }
458     }
459   }
460 
461   if (!lpFormat)
462   {
463     /* Use the appropriate default format */
464     if (dwFlags & DATE_DATEVARSONLY)
465     {
466       if (dwFlags & DATE_YEARMONTH)
467         lpFormat = GetYearMonth(node);
468       else if (dwFlags & DATE_LONGDATE)
469         lpFormat = GetLongDate(node);
470       else
471         lpFormat = GetShortDate(node);
472     }
473     else
474       lpFormat = GetTime(node);
475   }
476 
477   if (!lpTime)
478   {
479     GetLocalTime(&st); /* Default to current time */
480     lpTime = &st;
481   }
482   else
483   {
484     if (dwFlags & DATE_DATEVARSONLY)
485     {
486       FILETIME ftTmp;
487 
488       /* Verify the date and correct the D.O.W. if needed */
489       memset(&st, 0, sizeof(st));
490       st.wYear = lpTime->wYear;
491       st.wMonth = lpTime->wMonth;
492       st.wDay = lpTime->wDay;
493 
494       if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
495         goto invalid_parameter;
496 
497       FileTimeToSystemTime(&ftTmp, &st);
498       lpTime = &st;
499     }
500 
501     if (dwFlags & TIME_TIMEVARSONLY)
502     {
503       /* Verify the time */
504       if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
505         goto invalid_parameter;
506     }
507   }
508 
509   /* Format the output */
510   while (*lpFormat)
511   {
512     if (IsLiteralMarker(*lpFormat))
513     {
514       /* Start of a literal string */
515       lpFormat++;
516 
517       /* Loop until the end of the literal marker or end of the string */
518       while (*lpFormat)
519       {
520         if (IsLiteralMarker(*lpFormat))
521         {
522           lpFormat++;
523           if (!IsLiteralMarker(*lpFormat))
524             break; /* Terminating literal marker */
525         }
526 
527         if (!cchOut)
528           cchWritten++;   /* Count size only */
529         else if (cchWritten >= cchOut)
530           goto overrun;
531         else if (!bSkipping)
532         {
533           lpStr[cchWritten] = *lpFormat;
534           cchWritten++;
535         }
536         lpFormat++;
537       }
538     }
539     else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
540              (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
541     {
542       WCHAR buff[32], fmtChar;
543       LPCWSTR szAdd = NULL;
544       DWORD dwVal = 0;
545       int   count = 0, dwLen;
546 
547       bSkipping = FALSE;
548 
549       fmtChar = *lpFormat;
550       while (*lpFormat == fmtChar)
551       {
552         count++;
553         lpFormat++;
554       }
555       buff[0] = '\0';
556 
557       if (fmtChar != 'M') d_dd_formatted = FALSE;
558       switch(fmtChar)
559       {
560       case 'd':
561         if (count >= 4)
562           szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
563         else if (count == 3)
564           szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
565         else
566         {
567           dwVal = lpTime->wDay;
568           szAdd = buff;
569           d_dd_formatted = TRUE;
570         }
571         break;
572 
573       case 'M':
574         if (count >= 4)
575         {
576           LPCWSTR genitive = GetGenitiveMonth(node, lpTime->wMonth - 1);
577           if (genitive)
578           {
579             if (d_dd_formatted)
580             {
581               szAdd = genitive;
582               break;
583             }
584             else
585             {
586               LPCWSTR format = lpFormat;
587               /* Look forward now, if next format pattern is for day genitive
588                  name should be used */
589               while (*format)
590               {
591                 /* Skip parts within markers */
592                 if (IsLiteralMarker(*format))
593                 {
594                   ++format;
595                   while (*format)
596                   {
597                     if (IsLiteralMarker(*format))
598                     {
599                       ++format;
600                       if (!IsLiteralMarker(*format)) break;
601                     }
602                   }
603                 }
604                 if (*format != ' ') break;
605                 ++format;
606               }
607               /* Only numeric day form matters */
608               if (*format == 'd')
609               {
610                 INT dcount = 1;
611                 while (*++format == 'd') dcount++;
612                 if (dcount < 3)
613                 {
614                   szAdd = genitive;
615                   break;
616                 }
617               }
618             }
619           }
620           szAdd = GetLongMonth(node, lpTime->wMonth - 1);
621         }
622         else if (count == 3)
623           szAdd = GetShortMonth(node, lpTime->wMonth - 1);
624         else
625         {
626           dwVal = lpTime->wMonth;
627           szAdd = buff;
628         }
629         break;
630 
631       case 'y':
632 #ifdef __REACTOS__
633         if (IS_LCID_JAPANESE(lcid) && (dwFlags & DATE_USE_ALT_CALENDAR))
634         {
635             PCJAPANESE_ERA pEra = JapaneseEra_Find(lpTime);
636             if (pEra)
637             {
638                 if (count >= 2)
639                 {
640                     count = 2;
641                 }
642 
643                 dwVal = lpTime->wYear - pEra->wYear + 1;
644 
645                 if (dwVal == 1 && JapaneseEra_IsFirstYearGannen())
646                 {
647                     // Gan of 'Gannen'
648                     buff[0] = 0x5143;
649                     buff[1] = 0;
650                 }
651                 szAdd = buff;
652                 break;
653             }
654             SetLastError(ERROR_INVALID_PARAMETER);
655             return 0;
656         }
657 #endif
658         if (count >= 4)
659         {
660           count = 4;
661           dwVal = lpTime->wYear;
662         }
663         else
664         {
665           count = count > 2 ? 2 : count;
666           dwVal = lpTime->wYear % 100;
667         }
668         szAdd = buff;
669         break;
670 
671       case 'g':
672 #ifdef __REACTOS__
673         if (IS_LCID_JAPANESE(lcid))
674         {
675             if (dwFlags & DATE_USE_ALT_CALENDAR)
676             {
677                 PCJAPANESE_ERA pEra = JapaneseEra_Find(lpTime);
678                 if (pEra)
679                 {
680                     RtlStringCbCopyW(buff, sizeof(buff), pEra->szEraName);
681                     szAdd = buff;
682                     break;
683                 }
684                 SetLastError(ERROR_INVALID_PARAMETER);
685                 return 0;
686             }
687             else
688             {
689                 /* Seireki */
690                 buff[0] = 0x897F;
691                 buff[1] = 0x66A6;
692                 buff[2] = 0;
693                 szAdd = buff;
694                 break;
695             }
696         }
697 #endif
698         if (count == 2)
699         {
700           /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
701            *        When it is fixed, this string should be cached in 'node'.
702            */
703           FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
704           buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
705         }
706         else
707         {
708           buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
709         }
710         szAdd = buff;
711         break;
712 
713       case 'h':
714         if (!(dwFlags & TIME_FORCE24HOURFORMAT))
715         {
716           count = count > 2 ? 2 : count;
717           dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
718           szAdd = buff;
719           break;
720         }
721         /* .. fall through if we are forced to output in 24 hour format */
722 
723       case 'H':
724         count = count > 2 ? 2 : count;
725         dwVal = lpTime->wHour;
726         szAdd = buff;
727         break;
728 
729       case 'm':
730         if (dwFlags & TIME_NOMINUTESORSECONDS)
731         {
732           cchWritten = lastFormatPos; /* Skip */
733           bSkipping = TRUE;
734         }
735         else
736         {
737           count = count > 2 ? 2 : count;
738           dwVal = lpTime->wMinute;
739           szAdd = buff;
740         }
741         break;
742 
743       case 's':
744         if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
745         {
746           cchWritten = lastFormatPos; /* Skip */
747           bSkipping = TRUE;
748         }
749         else
750         {
751           count = count > 2 ? 2 : count;
752           dwVal = lpTime->wSecond;
753           szAdd = buff;
754         }
755         break;
756 
757       case 't':
758         if (dwFlags & TIME_NOTIMEMARKER)
759         {
760           cchWritten = lastFormatPos; /* Skip */
761           bSkipping = TRUE;
762         }
763         else
764         {
765           if (count == 1)
766             szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
767           else
768             szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
769         }
770         break;
771       }
772 
773       if (szAdd == buff && buff[0] == '\0')
774       {
775         static const WCHAR fmtW[] = {'%','.','*','d',0};
776         /* We have a numeric value to add */
777         snprintfW(buff, ARRAY_SIZE(buff), fmtW, count, dwVal);
778       }
779 
780       dwLen = szAdd ? strlenW(szAdd) : 0;
781 
782       if (cchOut && dwLen)
783       {
784         if (cchWritten + dwLen < cchOut)
785           memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
786         else
787         {
788           memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
789           goto overrun;
790         }
791       }
792       cchWritten += dwLen;
793       lastFormatPos = cchWritten; /* Save position of last output format text */
794     }
795     else
796     {
797       /* Literal character */
798       if (!cchOut)
799         cchWritten++;   /* Count size only */
800       else if (cchWritten >= cchOut)
801         goto overrun;
802       else if (!bSkipping || *lpFormat == ' ')
803       {
804         lpStr[cchWritten] = *lpFormat;
805         cchWritten++;
806       }
807       lpFormat++;
808     }
809   }
810 
811   /* Final string terminator and sanity check */
812   if (cchOut)
813   {
814    if (cchWritten >= cchOut)
815      goto overrun;
816    else
817      lpStr[cchWritten] = '\0';
818   }
819   cchWritten++; /* Include terminating NUL */
820 
821   TRACE("returning length=%d, output=%s\n", cchWritten, debugstr_w(lpStr));
822   return cchWritten;
823 
824 overrun:
825   TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
826   SetLastError(ERROR_INSUFFICIENT_BUFFER);
827   return 0;
828 
829 invalid_parameter:
830   SetLastError(ERROR_INVALID_PARAMETER);
831   return 0;
832 
833 invalid_flags:
834   SetLastError(ERROR_INVALID_FLAGS);
835   return 0;
836 }
837 
838 /******************************************************************************
839  * NLS_GetDateTimeFormatA <internal>
840  *
841  * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
842  */
843 static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
844                                   const SYSTEMTIME* lpTime,
845                                   LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
846 {
847   DWORD cp = CP_ACP;
848   WCHAR szFormat[128], szOut[128];
849   INT iRet;
850 
851   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
852         debugstr_a(lpFormat), lpStr, cchOut);
853 
854   if (NLS_IsUnicodeOnlyLcid(lcid))
855   {
856     SetLastError(ERROR_INVALID_PARAMETER);
857     return 0;
858   }
859 
860   if (!(dwFlags & LOCALE_USE_CP_ACP))
861   {
862     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
863     if (!node)
864     {
865       SetLastError(ERROR_INVALID_PARAMETER);
866       return 0;
867     }
868 
869     cp = node->dwCodePage;
870   }
871 
872   if (lpFormat)
873     MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, ARRAY_SIZE(szFormat));
874 
875   if (cchOut > (int) ARRAY_SIZE(szOut))
876     cchOut = ARRAY_SIZE(szOut);
877 
878   szOut[0] = '\0';
879 
880   iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
881                                 lpStr ? szOut : NULL, cchOut);
882 
883   if (lpStr)
884   {
885     if (szOut[0])
886       WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0);
887     else if (cchOut && iRet)
888       *lpStr = '\0';
889   }
890   return iRet;
891 }
892 
893 /******************************************************************************
894  * GetDateFormatA [KERNEL32.@]
895  *
896  * Format a date for a given locale.
897  *
898  * PARAMS
899  *  lcid      [I] Locale to format for
900  *  dwFlags   [I] LOCALE_ and DATE_ flags from "winnls.h"
901  *  lpTime    [I] Date to format
902  *  lpFormat  [I] Format string, or NULL to use the system defaults
903  *  lpDateStr [O] Destination for formatted string
904  *  cchOut    [I] Size of lpDateStr, or 0 to calculate the resulting size
905  *
906  * NOTES
907  *  - If lpFormat is NULL, lpDateStr will be formatted according to the format
908  *    details returned by GetLocaleInfoA() and modified by dwFlags.
909  *  - lpFormat is a string of characters and formatting tokens. Any characters
910  *    in the string are copied verbatim to lpDateStr, with tokens being replaced
911  *    by the date values they represent.
912  *  - The following tokens have special meanings in a date format string:
913  *|  Token  Meaning
914  *|  -----  -------
915  *|  d      Single digit day of the month (no leading 0)
916  *|  dd     Double digit day of the month
917  *|  ddd    Short name for the day of the week
918  *|  dddd   Long name for the day of the week
919  *|  M      Single digit month of the year (no leading 0)
920  *|  MM     Double digit month of the year
921  *|  MMM    Short name for the month of the year
922  *|  MMMM   Long name for the month of the year
923  *|  y      Double digit year number (no leading 0)
924  *|  yy     Double digit year number
925  *|  yyyy   Four digit year number
926  *|  gg     Era string, for example 'AD'.
927  *  - To output any literal character that could be misidentified as a token,
928  *    enclose it in single quotes.
929  *  - The Ascii version of this function fails if lcid is Unicode only.
930  *
931  * RETURNS
932  *  Success: The number of character written to lpDateStr, or that would
933  *           have been written, if cchOut is 0.
934  *  Failure: 0. Use GetLastError() to determine the cause.
935  */
936 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
937                            LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
938 {
939   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
940         debugstr_a(lpFormat), lpDateStr, cchOut);
941 
942   return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
943                                 lpFormat, lpDateStr, cchOut);
944 }
945 
946 #if _WIN32_WINNT >= 0x600
947 /******************************************************************************
948  * GetDateFormatEx [KERNEL32.@]
949  *
950  * Format a date for a given locale.
951  *
952  * PARAMS
953  *  localename [I] Locale to format for
954  *  flags      [I] LOCALE_ and DATE_ flags from "winnls.h"
955  *  date       [I] Date to format
956  *  format     [I] Format string, or NULL to use the locale defaults
957  *  outbuf     [O] Destination for formatted string
958  *  bufsize    [I] Size of outbuf, or 0 to calculate the resulting size
959  *  calendar   [I] Reserved, must be NULL
960  *
961  * See GetDateFormatA for notes.
962  *
963  * RETURNS
964  *  Success: The number of characters written to outbuf, or that would have
965  *           been written if bufsize is 0.
966  *  Failure: 0. Use GetLastError() to determine the cause.
967  */
968 INT WINAPI GetDateFormatEx(LPCWSTR localename, DWORD flags,
969                            const SYSTEMTIME* date, LPCWSTR format,
970                            LPWSTR outbuf, INT bufsize, LPCWSTR calendar)
971 {
972   TRACE("(%s,0x%08x,%p,%s,%p,%d,%s)\n", debugstr_w(localename), flags,
973         date, debugstr_w(format), outbuf, bufsize, debugstr_w(calendar));
974 
975   /* Parameter is currently reserved and Windows errors if set */
976   if (calendar != NULL)
977   {
978     SetLastError(ERROR_INVALID_PARAMETER);
979     return 0;
980   }
981 
982   return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename, 0),
983                                 flags | DATE_DATEVARSONLY, date, format,
984                                 outbuf, bufsize);
985 }
986 #endif /* _WIN32_WINNT >= 0x600 */
987 
988 /******************************************************************************
989  * GetDateFormatW	[KERNEL32.@]
990  *
991  * See GetDateFormatA.
992  */
993 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
994                           LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
995 {
996   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
997         debugstr_w(lpFormat), lpDateStr, cchOut);
998 
999   return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
1000                                 lpFormat, lpDateStr, cchOut);
1001 }
1002 
1003 /******************************************************************************
1004  *		GetTimeFormatA	[KERNEL32.@]
1005  *
1006  * Format a time for a given locale.
1007  *
1008  * PARAMS
1009  *  lcid      [I] Locale to format for
1010  *  dwFlags   [I] LOCALE_ and TIME_ flags from "winnls.h"
1011  *  lpTime    [I] Time to format
1012  *  lpFormat  [I] Formatting overrides
1013  *  lpTimeStr [O] Destination for formatted string
1014  *  cchOut    [I] Size of lpTimeStr, or 0 to calculate the resulting size
1015  *
1016  * NOTES
1017  *  - If lpFormat is NULL, lpszValue will be formatted according to the format
1018  *    details returned by GetLocaleInfoA() and modified by dwFlags.
1019  *  - lpFormat is a string of characters and formatting tokens. Any characters
1020  *    in the string are copied verbatim to lpTimeStr, with tokens being replaced
1021  *    by the time values they represent.
1022  *  - The following tokens have special meanings in a time format string:
1023  *|  Token  Meaning
1024  *|  -----  -------
1025  *|  h      Hours with no leading zero (12-hour clock)
1026  *|  hh     Hours with full two digits (12-hour clock)
1027  *|  H      Hours with no leading zero (24-hour clock)
1028  *|  HH     Hours with full two digits (24-hour clock)
1029  *|  m      Minutes with no leading zero
1030  *|  mm     Minutes with full two digits
1031  *|  s      Seconds with no leading zero
1032  *|  ss     Seconds with full two digits
1033  *|  t      Short time marker (e.g. "A" or "P")
1034  *|  tt     Long time marker (e.g. "AM", "PM")
1035  *  - To output any literal character that could be misidentified as a token,
1036  *    enclose it in single quotes.
1037  *  - The Ascii version of this function fails if lcid is Unicode only.
1038  *
1039  * RETURNS
1040  *  Success: The number of character written to lpTimeStr, or that would
1041  *           have been written, if cchOut is 0.
1042  *  Failure: 0. Use GetLastError() to determine the cause.
1043  */
1044 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
1045                           LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
1046 {
1047   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
1048         debugstr_a(lpFormat), lpTimeStr, cchOut);
1049 
1050   return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
1051                                 lpFormat, lpTimeStr, cchOut);
1052 }
1053 
1054 #if _WIN32_WINNT >= 0x600
1055 /******************************************************************************
1056  * GetTimeFormatEx [KERNEL32.@]
1057  *
1058  * Format a date for a given locale.
1059  *
1060  * PARAMS
1061  *  localename [I] Locale to format for
1062  *  flags      [I] LOCALE_ and TIME_ flags from "winnls.h"
1063  *  time       [I] Time to format
1064  *  format     [I] Formatting overrides
1065  *  outbuf     [O] Destination for formatted string
1066  *  bufsize    [I] Size of outbuf, or 0 to calculate the resulting size
1067  *
1068  * See GetTimeFormatA for notes.
1069  *
1070  * RETURNS
1071  *  Success: The number of characters written to outbuf, or that would have
1072  *           have been written if bufsize is 0.
1073  *  Failure: 0. Use GetLastError() to determine the cause.
1074  */
1075 INT WINAPI GetTimeFormatEx(LPCWSTR localename, DWORD flags,
1076                            const SYSTEMTIME* time, LPCWSTR format,
1077                            LPWSTR outbuf, INT bufsize)
1078 {
1079   TRACE("(%s,0x%08x,%p,%s,%p,%d)\n", debugstr_w(localename), flags, time,
1080         debugstr_w(format), outbuf, bufsize);
1081 
1082   return NLS_GetDateTimeFormatW(LocaleNameToLCID(localename, 0),
1083                                 flags | TIME_TIMEVARSONLY, time, format,
1084                                 outbuf, bufsize);
1085 }
1086 #endif /* _WIN32_WINNT >= 0x600 */
1087 
1088 /******************************************************************************
1089  *		GetTimeFormatW	[KERNEL32.@]
1090  *
1091  * See GetTimeFormatA.
1092  */
1093 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
1094                           LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
1095 {
1096   TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
1097         debugstr_w(lpFormat), lpTimeStr, cchOut);
1098 
1099   return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
1100                                 lpFormat, lpTimeStr, cchOut);
1101 }
1102 
1103 /**************************************************************************
1104  *              GetNumberFormatA	(KERNEL32.@)
1105  *
1106  * Format a number string for a given locale.
1107  *
1108  * PARAMS
1109  *  lcid        [I] Locale to format for
1110  *  dwFlags     [I] LOCALE_ flags from "winnls.h"
1111  *  lpszValue   [I] String to format
1112  *  lpFormat    [I] Formatting overrides
1113  *  lpNumberStr [O] Destination for formatted string
1114  *  cchOut      [I] Size of lpNumberStr, or 0 to calculate the resulting size
1115  *
1116  * NOTES
1117  *  - lpszValue can contain only '0' - '9', '-' and '.'.
1118  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1119  *    be formatted according to the format details returned by GetLocaleInfoA().
1120  *  - This function rounds the number string if the number of decimals exceeds the
1121  *    locales normal number of decimal places.
1122  *  - If cchOut is 0, this function does not write to lpNumberStr.
1123  *  - The Ascii version of this function fails if lcid is Unicode only.
1124  *
1125  * RETURNS
1126  *  Success: The number of character written to lpNumberStr, or that would
1127  *           have been written, if cchOut is 0.
1128  *  Failure: 0. Use GetLastError() to determine the cause.
1129  */
1130 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
1131                             LPCSTR lpszValue,  const NUMBERFMTA *lpFormat,
1132                             LPSTR lpNumberStr, int cchOut)
1133 {
1134   DWORD cp = CP_ACP;
1135   WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
1136   NUMBERFMTW fmt;
1137   const NUMBERFMTW *pfmt = NULL;
1138   INT iRet;
1139 
1140   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
1141         lpFormat, lpNumberStr, cchOut);
1142 
1143   if (NLS_IsUnicodeOnlyLcid(lcid))
1144   {
1145     SetLastError(ERROR_INVALID_PARAMETER);
1146     return 0;
1147   }
1148 
1149   if (!(dwFlags & LOCALE_USE_CP_ACP))
1150   {
1151     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1152     if (!node)
1153     {
1154       SetLastError(ERROR_INVALID_PARAMETER);
1155       return 0;
1156     }
1157 
1158     cp = node->dwCodePage;
1159   }
1160 
1161   if (lpFormat)
1162   {
1163     memcpy(&fmt, lpFormat, sizeof(fmt));
1164     pfmt = &fmt;
1165     if (lpFormat->lpDecimalSep)
1166     {
1167       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, ARRAY_SIZE(szDec));
1168       fmt.lpDecimalSep = szDec;
1169     }
1170     if (lpFormat->lpThousandSep)
1171     {
1172       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, ARRAY_SIZE(szGrp));
1173       fmt.lpThousandSep = szGrp;
1174     }
1175   }
1176 
1177   if (lpszValue)
1178     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, ARRAY_SIZE(szIn));
1179 
1180   if (cchOut > (int) ARRAY_SIZE(szOut))
1181     cchOut = ARRAY_SIZE(szOut);
1182 
1183   szOut[0] = '\0';
1184 
1185   iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1186                           lpNumberStr ? szOut : NULL, cchOut);
1187 
1188   if (szOut[0] && lpNumberStr)
1189     WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
1190   return iRet;
1191 }
1192 
1193 /* Number parsing state flags */
1194 #define NF_ISNEGATIVE 0x1  /* '-' found */
1195 #define NF_ISREAL     0x2  /* '.' found */
1196 #define NF_DIGITS     0x4  /* '0'-'9' found */
1197 #define NF_DIGITS_OUT 0x8  /* Digits before the '.' found */
1198 #define NF_ROUND      0x10 /* Number needs to be rounded */
1199 
1200 /* Formatting options for Numbers */
1201 #define NLS_NEG_PARENS      0 /* "(1.1)" */
1202 #define NLS_NEG_LEFT        1 /* "-1.1"  */
1203 #define NLS_NEG_LEFT_SPACE  2 /* "- 1.1" */
1204 #define NLS_NEG_RIGHT       3 /* "1.1-"  */
1205 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1206 
1207 /**************************************************************************
1208  *              GetNumberFormatW	(KERNEL32.@)
1209  *
1210  * See GetNumberFormatA.
1211  */
1212 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
1213                             LPCWSTR lpszValue,  const NUMBERFMTW *lpFormat,
1214                             LPWSTR lpNumberStr, int cchOut)
1215 {
1216   WCHAR szBuff[128], *szOut = szBuff + ARRAY_SIZE(szBuff) - 1;
1217   WCHAR szNegBuff[8];
1218   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
1219   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
1220   INT iRet;
1221 
1222   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1223         lpFormat, lpNumberStr, cchOut);
1224 
1225   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
1226       !IsValidLocale(lcid, 0) ||
1227       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
1228   {
1229     goto error;
1230   }
1231 
1232   if (!lpFormat)
1233   {
1234     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1235 
1236     if (!node)
1237       goto error;
1238     lpFormat = &node->fmt;
1239     lpszNegStart = lpszNeg = GetNegative(node);
1240   }
1241   else
1242   {
1243     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1244                    szNegBuff, ARRAY_SIZE(szNegBuff));
1245     lpszNegStart = lpszNeg = szNegBuff;
1246   }
1247   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1248 
1249   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1250 
1251   /* Format the number backwards into a temporary buffer */
1252 
1253   szSrc = lpszValue;
1254   *szOut-- = '\0';
1255 
1256   /* Check the number for validity */
1257   while (*szSrc)
1258   {
1259     if (*szSrc >= '0' && *szSrc <= '9')
1260     {
1261       dwState |= NF_DIGITS;
1262       if (dwState & NF_ISREAL)
1263         dwDecimals++;
1264     }
1265     else if (*szSrc == '-')
1266     {
1267       if (dwState)
1268         goto error; /* '-' not first character */
1269       dwState |= NF_ISNEGATIVE;
1270     }
1271     else if (*szSrc == '.')
1272     {
1273       if (dwState & NF_ISREAL)
1274         goto error; /* More than one '.' */
1275       dwState |= NF_ISREAL;
1276     }
1277     else
1278       goto error; /* Invalid char */
1279     szSrc++;
1280   }
1281   szSrc--; /* Point to last character */
1282 
1283   if (!(dwState & NF_DIGITS))
1284     goto error; /* No digits */
1285 
1286   /* Add any trailing negative sign */
1287   if (dwState & NF_ISNEGATIVE)
1288   {
1289     switch (lpFormat->NegativeOrder)
1290     {
1291     case NLS_NEG_PARENS:
1292       *szOut-- = ')';
1293       break;
1294     case NLS_NEG_RIGHT:
1295     case NLS_NEG_RIGHT_SPACE:
1296       while (lpszNeg >= lpszNegStart)
1297         *szOut-- = *lpszNeg--;
1298      if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
1299        *szOut-- = ' ';
1300       break;
1301     }
1302   }
1303 
1304   /* Copy all digits up to the decimal point */
1305   if (!lpFormat->NumDigits)
1306   {
1307     if (dwState & NF_ISREAL)
1308     {
1309       while (*szSrc != '.') /* Don't write any decimals or a separator */
1310       {
1311         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1312           dwState |= NF_ROUND;
1313         else
1314           dwState &= ~NF_ROUND;
1315         szSrc--;
1316       }
1317       szSrc--;
1318     }
1319   }
1320   else
1321   {
1322     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1323 
1324     if (dwDecimals <= lpFormat->NumDigits)
1325     {
1326       dwDecimals = lpFormat->NumDigits - dwDecimals;
1327       while (dwDecimals--)
1328         *szOut-- = '0'; /* Pad to correct number of dp */
1329     }
1330     else
1331     {
1332       dwDecimals -= lpFormat->NumDigits;
1333       /* Skip excess decimals, and determine if we have to round the number */
1334       while (dwDecimals--)
1335       {
1336         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1337           dwState |= NF_ROUND;
1338         else
1339           dwState &= ~NF_ROUND;
1340         szSrc--;
1341       }
1342     }
1343 
1344     if (dwState & NF_ISREAL)
1345     {
1346       while (*szSrc != '.')
1347       {
1348         if (dwState & NF_ROUND)
1349         {
1350           if (*szSrc == '9')
1351             *szOut-- = '0'; /* continue rounding */
1352           else
1353           {
1354             dwState &= ~NF_ROUND;
1355             *szOut-- = (*szSrc)+1;
1356           }
1357           szSrc--;
1358         }
1359         else
1360           *szOut-- = *szSrc--; /* Write existing decimals */
1361       }
1362       szSrc--; /* Skip '.' */
1363     }
1364 
1365     while (lpszDec >= lpFormat->lpDecimalSep)
1366       *szOut-- = *lpszDec--; /* Write decimal separator */
1367   }
1368 
1369   dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1370 
1371   /* Write the remaining whole number digits, including grouping chars */
1372   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1373   {
1374     if (dwState & NF_ROUND)
1375     {
1376       if (*szSrc == '9')
1377         *szOut-- = '0'; /* continue rounding */
1378       else
1379       {
1380         dwState &= ~NF_ROUND;
1381         *szOut-- = (*szSrc)+1;
1382       }
1383       szSrc--;
1384     }
1385     else
1386       *szOut-- = *szSrc--;
1387 
1388     dwState |= NF_DIGITS_OUT;
1389     dwCurrentGroupCount++;
1390     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
1391     {
1392       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1393 
1394       while (lpszGrp >= lpFormat->lpThousandSep)
1395         *szOut-- = *lpszGrp--; /* Write grouping char */
1396 
1397       dwCurrentGroupCount = 0;
1398       if (lpFormat->Grouping == 32)
1399         dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1400     }
1401   }
1402   if (dwState & NF_ROUND)
1403   {
1404     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1405   }
1406   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1407     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1408 
1409   /* Add any leading negative sign */
1410   if (dwState & NF_ISNEGATIVE)
1411   {
1412     switch (lpFormat->NegativeOrder)
1413     {
1414     case NLS_NEG_PARENS:
1415       *szOut-- = '(';
1416       break;
1417     case NLS_NEG_LEFT_SPACE:
1418       *szOut-- = ' ';
1419       /* Fall through */
1420     case NLS_NEG_LEFT:
1421       while (lpszNeg >= lpszNegStart)
1422         *szOut-- = *lpszNeg--;
1423       break;
1424     }
1425   }
1426   szOut++;
1427 
1428   iRet = strlenW(szOut) + 1;
1429   if (cchOut)
1430   {
1431     if (iRet <= cchOut)
1432       memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
1433     else
1434     {
1435       memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
1436       lpNumberStr[cchOut - 1] = '\0';
1437       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1438       iRet = 0;
1439     }
1440   }
1441   return iRet;
1442 
1443 error:
1444   SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1445   return 0;
1446 }
1447 
1448 #if _WIN32_WINNT >= 0x600
1449 /**************************************************************************
1450  *              GetNumberFormatEx	(KERNEL32.@)
1451  */
1452 INT WINAPI GetNumberFormatEx(LPCWSTR name, DWORD flags,
1453                              LPCWSTR value, const NUMBERFMTW *format,
1454                              LPWSTR number, int numout)
1455 {
1456   LCID lcid;
1457 
1458   TRACE("(%s,0x%08x,%s,%p,%p,%d)\n", debugstr_w(name), flags,
1459         debugstr_w(value), format, number, numout);
1460 
1461   lcid = LocaleNameToLCID(name, 0);
1462   if (!lcid)
1463     return 0;
1464 
1465   return GetNumberFormatW(lcid, flags, value, format, number, numout);
1466 }
1467 #endif /* _WIN32_WINNT >= 0x600 */
1468 
1469 /**************************************************************************
1470  *              GetCurrencyFormatA	(KERNEL32.@)
1471  *
1472  * Format a currency string for a given locale.
1473  *
1474  * PARAMS
1475  *  lcid          [I] Locale to format for
1476  *  dwFlags       [I] LOCALE_ flags from "winnls.h"
1477  *  lpszValue     [I] String to format
1478  *  lpFormat      [I] Formatting overrides
1479  *  lpCurrencyStr [O] Destination for formatted string
1480  *  cchOut        [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1481  *
1482  * NOTES
1483  *  - lpszValue can contain only '0' - '9', '-' and '.'.
1484  *  - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1485  *    be formatted according to the format details returned by GetLocaleInfoA().
1486  *  - This function rounds the currency if the number of decimals exceeds the
1487  *    locales number of currency decimal places.
1488  *  - If cchOut is 0, this function does not write to lpCurrencyStr.
1489  *  - The Ascii version of this function fails if lcid is Unicode only.
1490  *
1491  * RETURNS
1492  *  Success: The number of character written to lpNumberStr, or that would
1493  *           have been written, if cchOut is 0.
1494  *  Failure: 0. Use GetLastError() to determine the cause.
1495  */
1496 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
1497                               LPCSTR lpszValue,  const CURRENCYFMTA *lpFormat,
1498                               LPSTR lpCurrencyStr, int cchOut)
1499 {
1500   DWORD cp = CP_ACP;
1501   WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
1502   CURRENCYFMTW fmt;
1503   const CURRENCYFMTW *pfmt = NULL;
1504   INT iRet;
1505 
1506   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
1507         lpFormat, lpCurrencyStr, cchOut);
1508 
1509   if (NLS_IsUnicodeOnlyLcid(lcid))
1510   {
1511     SetLastError(ERROR_INVALID_PARAMETER);
1512     return 0;
1513   }
1514 
1515   if (!(dwFlags & LOCALE_USE_CP_ACP))
1516   {
1517     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1518     if (!node)
1519     {
1520       SetLastError(ERROR_INVALID_PARAMETER);
1521       return 0;
1522     }
1523 
1524     cp = node->dwCodePage;
1525   }
1526 
1527   if (lpFormat)
1528   {
1529     memcpy(&fmt, lpFormat, sizeof(fmt));
1530     pfmt = &fmt;
1531     if (lpFormat->lpDecimalSep)
1532     {
1533       MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, ARRAY_SIZE(szDec));
1534       fmt.lpDecimalSep = szDec;
1535     }
1536     if (lpFormat->lpThousandSep)
1537     {
1538       MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, ARRAY_SIZE(szGrp));
1539       fmt.lpThousandSep = szGrp;
1540     }
1541     if (lpFormat->lpCurrencySymbol)
1542     {
1543       MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, ARRAY_SIZE(szCy));
1544       fmt.lpCurrencySymbol = szCy;
1545     }
1546   }
1547 
1548   if (lpszValue)
1549     MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, ARRAY_SIZE(szIn));
1550 
1551   if (cchOut > (int) ARRAY_SIZE(szOut))
1552     cchOut = ARRAY_SIZE(szOut);
1553 
1554   szOut[0] = '\0';
1555 
1556   iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1557                             lpCurrencyStr ? szOut : NULL, cchOut);
1558 
1559   if (szOut[0] && lpCurrencyStr)
1560     WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
1561   return iRet;
1562 }
1563 
1564 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1565 #define CF_PARENS       0x1  /* Parentheses      */
1566 #define CF_MINUS_LEFT   0x2  /* '-' to the left  */
1567 #define CF_MINUS_RIGHT  0x4  /* '-' to the right */
1568 #define CF_MINUS_BEFORE 0x8  /* '-' before '$'   */
1569 #define CF_CY_LEFT      0x10 /* '$' to the left  */
1570 #define CF_CY_RIGHT     0x20 /* '$' to the right */
1571 #define CF_CY_SPACE     0x40 /* ' ' by '$'       */
1572 
1573 /**************************************************************************
1574  *              GetCurrencyFormatW	(KERNEL32.@)
1575  *
1576  * See GetCurrencyFormatA.
1577  */
1578 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
1579                               LPCWSTR lpszValue,  const CURRENCYFMTW *lpFormat,
1580                               LPWSTR lpCurrencyStr, int cchOut)
1581 {
1582   static const BYTE NLS_NegCyFormats[16] =
1583   {
1584     CF_PARENS|CF_CY_LEFT,                       /* ($1.1) */
1585     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT,   /* -$1.1  */
1586     CF_MINUS_LEFT|CF_CY_LEFT,                   /* $-1.1  */
1587     CF_MINUS_RIGHT|CF_CY_LEFT,                  /* $1.1-  */
1588     CF_PARENS|CF_CY_RIGHT,                      /* (1.1$) */
1589     CF_MINUS_LEFT|CF_CY_RIGHT,                  /* -1.1$  */
1590     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$  */
1591     CF_MINUS_RIGHT|CF_CY_RIGHT,                 /* 1.1$-  */
1592     CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE,      /* -1.1 $ */
1593     CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE,   /* -$ 1.1 */
1594     CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE,     /* 1.1 $-  */
1595     CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE,      /* $ 1.1-  */
1596     CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE,       /* $ -1.1  */
1597     CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
1598     CF_PARENS|CF_CY_LEFT|CF_CY_SPACE,           /* ($ 1.1) */
1599     CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE,          /* (1.1 $) */
1600   };
1601   static const BYTE NLS_PosCyFormats[4] =
1602   {
1603     CF_CY_LEFT,              /* $1.1  */
1604     CF_CY_RIGHT,             /* 1.1$  */
1605     CF_CY_LEFT|CF_CY_SPACE,  /* $ 1.1 */
1606     CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
1607   };
1608   WCHAR szBuff[128], *szOut = szBuff + ARRAY_SIZE(szBuff) - 1;
1609   WCHAR szNegBuff[8];
1610   const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
1611   DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
1612   INT iRet;
1613 
1614   TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1615         lpFormat, lpCurrencyStr, cchOut);
1616 
1617   if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
1618       !IsValidLocale(lcid, 0) ||
1619       (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
1620       !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
1621       lpFormat->PositiveOrder > 3)))
1622   {
1623     goto error;
1624   }
1625 
1626   if (!lpFormat)
1627   {
1628     const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1629 
1630     if (!node)
1631       goto error;
1632 
1633     lpFormat = &node->cyfmt;
1634     lpszNegStart = lpszNeg = GetNegative(node);
1635   }
1636   else
1637   {
1638     GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1639                    szNegBuff, ARRAY_SIZE(szNegBuff));
1640     lpszNegStart = lpszNeg = szNegBuff;
1641   }
1642   dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1643 
1644   lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1645   lpszCyStart = lpFormat->lpCurrencySymbol;
1646   lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
1647 
1648   /* Format the currency backwards into a temporary buffer */
1649 
1650   szSrc = lpszValue;
1651   *szOut-- = '\0';
1652 
1653   /* Check the number for validity */
1654   while (*szSrc)
1655   {
1656     if (*szSrc >= '0' && *szSrc <= '9')
1657     {
1658       dwState |= NF_DIGITS;
1659       if (dwState & NF_ISREAL)
1660         dwDecimals++;
1661     }
1662     else if (*szSrc == '-')
1663     {
1664       if (dwState)
1665         goto error; /* '-' not first character */
1666       dwState |= NF_ISNEGATIVE;
1667     }
1668     else if (*szSrc == '.')
1669     {
1670       if (dwState & NF_ISREAL)
1671         goto error; /* More than one '.' */
1672       dwState |= NF_ISREAL;
1673     }
1674     else
1675       goto error; /* Invalid char */
1676     szSrc++;
1677   }
1678   szSrc--; /* Point to last character */
1679 
1680   if (!(dwState & NF_DIGITS))
1681     goto error; /* No digits */
1682 
1683   if (dwState & NF_ISNEGATIVE)
1684     dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
1685   else
1686     dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
1687 
1688   /* Add any trailing negative or currency signs */
1689   if (dwFmt & CF_PARENS)
1690     *szOut-- = ')';
1691 
1692   while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
1693   {
1694     switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
1695     {
1696     case CF_MINUS_RIGHT:
1697     case CF_MINUS_RIGHT|CF_CY_RIGHT:
1698       while (lpszNeg >= lpszNegStart)
1699         *szOut-- = *lpszNeg--;
1700       dwFmt &= ~CF_MINUS_RIGHT;
1701       break;
1702 
1703     case CF_CY_RIGHT:
1704     case CF_MINUS_BEFORE|CF_CY_RIGHT:
1705     case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
1706       while (lpszCy >= lpszCyStart)
1707         *szOut-- = *lpszCy--;
1708       if (dwFmt & CF_CY_SPACE)
1709         *szOut-- = ' ';
1710       dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
1711       break;
1712     }
1713   }
1714 
1715   /* Copy all digits up to the decimal point */
1716   if (!lpFormat->NumDigits)
1717   {
1718     if (dwState & NF_ISREAL)
1719     {
1720       while (*szSrc != '.') /* Don't write any decimals or a separator */
1721       {
1722         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1723           dwState |= NF_ROUND;
1724         else
1725           dwState &= ~NF_ROUND;
1726         szSrc--;
1727       }
1728       szSrc--;
1729     }
1730   }
1731   else
1732   {
1733     LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1734 
1735     if (dwDecimals <= lpFormat->NumDigits)
1736     {
1737       dwDecimals = lpFormat->NumDigits - dwDecimals;
1738       while (dwDecimals--)
1739         *szOut-- = '0'; /* Pad to correct number of dp */
1740     }
1741     else
1742     {
1743       dwDecimals -= lpFormat->NumDigits;
1744       /* Skip excess decimals, and determine if we have to round the number */
1745       while (dwDecimals--)
1746       {
1747         if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1748           dwState |= NF_ROUND;
1749         else
1750           dwState &= ~NF_ROUND;
1751         szSrc--;
1752       }
1753     }
1754 
1755     if (dwState & NF_ISREAL)
1756     {
1757       while (*szSrc != '.')
1758       {
1759         if (dwState & NF_ROUND)
1760         {
1761           if (*szSrc == '9')
1762             *szOut-- = '0'; /* continue rounding */
1763           else
1764           {
1765             dwState &= ~NF_ROUND;
1766             *szOut-- = (*szSrc)+1;
1767           }
1768           szSrc--;
1769         }
1770         else
1771           *szOut-- = *szSrc--; /* Write existing decimals */
1772       }
1773       szSrc--; /* Skip '.' */
1774     }
1775     while (lpszDec >= lpFormat->lpDecimalSep)
1776       *szOut-- = *lpszDec--; /* Write decimal separator */
1777   }
1778 
1779   dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1780 
1781   /* Write the remaining whole number digits, including grouping chars */
1782   while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1783   {
1784     if (dwState & NF_ROUND)
1785     {
1786       if (*szSrc == '9')
1787         *szOut-- = '0'; /* continue rounding */
1788       else
1789       {
1790         dwState &= ~NF_ROUND;
1791         *szOut-- = (*szSrc)+1;
1792       }
1793       szSrc--;
1794     }
1795     else
1796       *szOut-- = *szSrc--;
1797 
1798     dwState |= NF_DIGITS_OUT;
1799     dwCurrentGroupCount++;
1800     if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
1801     {
1802       LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1803 
1804       while (lpszGrp >= lpFormat->lpThousandSep)
1805         *szOut-- = *lpszGrp--; /* Write grouping char */
1806 
1807       dwCurrentGroupCount = 0;
1808       if (lpFormat->Grouping == 32)
1809         dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1810     }
1811   }
1812   if (dwState & NF_ROUND)
1813     *szOut-- = '1'; /* e.g. .6 > 1.0 */
1814   else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1815     *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1816 
1817   /* Add any leading negative or currency sign */
1818   while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
1819   {
1820     switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
1821     {
1822     case CF_MINUS_LEFT:
1823     case CF_MINUS_LEFT|CF_CY_LEFT:
1824       while (lpszNeg >= lpszNegStart)
1825         *szOut-- = *lpszNeg--;
1826       dwFmt &= ~CF_MINUS_LEFT;
1827       break;
1828 
1829     case CF_CY_LEFT:
1830     case CF_CY_LEFT|CF_MINUS_BEFORE:
1831     case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
1832       if (dwFmt & CF_CY_SPACE)
1833         *szOut-- = ' ';
1834       while (lpszCy >= lpszCyStart)
1835         *szOut-- = *lpszCy--;
1836       dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
1837       break;
1838     }
1839   }
1840   if (dwFmt & CF_PARENS)
1841     *szOut-- = '(';
1842   szOut++;
1843 
1844   iRet = strlenW(szOut) + 1;
1845   if (cchOut)
1846   {
1847     if (iRet <= cchOut)
1848       memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
1849     else
1850     {
1851       memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
1852       lpCurrencyStr[cchOut - 1] = '\0';
1853       SetLastError(ERROR_INSUFFICIENT_BUFFER);
1854       iRet = 0;
1855     }
1856   }
1857   return iRet;
1858 
1859 error:
1860   SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1861   return 0;
1862 }
1863 
1864 #if _WIN32_WINNT >= 0x600
1865 /***********************************************************************
1866  *            GetCurrencyFormatEx (KERNEL32.@)
1867  */
1868 int WINAPI GetCurrencyFormatEx(LPCWSTR localename, DWORD flags, LPCWSTR value,
1869                                 const CURRENCYFMTW *format, LPWSTR str, int len)
1870 {
1871     TRACE("(%s,0x%08x,%s,%p,%p,%d)\n", debugstr_w(localename), flags,
1872             debugstr_w(value), format, str, len);
1873 
1874     return GetCurrencyFormatW( LocaleNameToLCID(localename, 0), flags, value, format, str, len);
1875 }
1876 #endif
1877 
1878 
1879 /* FIXME: Everything below here needs to move somewhere else along with the
1880  *        other EnumXXX functions, when a method for storing resources for
1881  *        alternate calendars is determined.
1882  */
1883 
1884 enum enum_callback_type {
1885     CALLBACK_ENUMPROC,
1886     CALLBACK_ENUMPROCEX,
1887     CALLBACK_ENUMPROCEXEX
1888 };
1889 
1890 struct enumdateformats_context {
1891     enum enum_callback_type type;  /* callback kind */
1892     union {
1893         DATEFMT_ENUMPROCW    callback;     /* user callback pointer */
1894         DATEFMT_ENUMPROCEXW  callbackex;
1895         DATEFMT_ENUMPROCEXEX callbackexex;
1896     } u;
1897     LCID   lcid;    /* locale of interest */
1898     DWORD  flags;
1899     LPARAM lParam;
1900     BOOL   unicode; /* A vs W callback type, only for regular and Ex callbacks */
1901 };
1902 
1903 /******************************************************************************
1904  * NLS_EnumDateFormats <internal>
1905  * Enumerates date formats for a specified locale.
1906  *
1907  * PARAMS
1908  *    ctxt [I] enumeration context, see 'struct enumdateformats_context'
1909  *
1910  * RETURNS
1911  *    Success: TRUE.
1912  *    Failure: FALSE. Use GetLastError() to determine the cause.
1913  */
1914 static BOOL NLS_EnumDateFormats(const struct enumdateformats_context *ctxt)
1915 {
1916     WCHAR bufW[256];
1917     char bufA[256];
1918     LCTYPE lctype;
1919     CALID cal_id;
1920     INT ret;
1921 
1922     if (!ctxt->u.callback)
1923     {
1924         SetLastError(ERROR_INVALID_PARAMETER);
1925         return FALSE;
1926     }
1927 
1928     if (!GetLocaleInfoW(ctxt->lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
1929         return FALSE;
1930 
1931     switch (ctxt->flags & ~LOCALE_USE_CP_ACP)
1932     {
1933     case 0:
1934     case DATE_SHORTDATE:
1935         lctype = LOCALE_SSHORTDATE;
1936         break;
1937     case DATE_LONGDATE:
1938         lctype = LOCALE_SLONGDATE;
1939         break;
1940     case DATE_YEARMONTH:
1941         lctype = LOCALE_SYEARMONTH;
1942         break;
1943     default:
1944         FIXME("Unknown date format (0x%08x)\n", ctxt->flags);
1945         SetLastError(ERROR_INVALID_PARAMETER);
1946         return FALSE;
1947     }
1948 
1949     lctype |= ctxt->flags & LOCALE_USE_CP_ACP;
1950     if (ctxt->unicode)
1951         ret = GetLocaleInfoW(ctxt->lcid, lctype, bufW, ARRAY_SIZE(bufW));
1952     else
1953         ret = GetLocaleInfoA(ctxt->lcid, lctype, bufA, ARRAY_SIZE(bufA));
1954 
1955     if (ret)
1956     {
1957         switch (ctxt->type)
1958         {
1959         case CALLBACK_ENUMPROC:
1960             ctxt->u.callback(ctxt->unicode ? bufW : (WCHAR*)bufA);
1961             break;
1962         case CALLBACK_ENUMPROCEX:
1963             ctxt->u.callbackex(ctxt->unicode ? bufW : (WCHAR*)bufA, cal_id);
1964             break;
1965         case CALLBACK_ENUMPROCEXEX:
1966             ctxt->u.callbackexex(bufW, cal_id, ctxt->lParam);
1967             break;
1968         default:
1969             ;
1970         }
1971     }
1972 
1973     return TRUE;
1974 }
1975 
1976 /**************************************************************************
1977  *              EnumDateFormatsExA    (KERNEL32.@)
1978  *
1979  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1980  * LOCALE_NOUSEROVERRIDE here as well?
1981  */
1982 BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags)
1983 {
1984     struct enumdateformats_context ctxt;
1985 
1986     ctxt.type = CALLBACK_ENUMPROCEX;
1987     ctxt.u.callbackex = (DATEFMT_ENUMPROCEXW)proc;
1988     ctxt.lcid = lcid;
1989     ctxt.flags = flags;
1990     ctxt.unicode = FALSE;
1991 
1992     return NLS_EnumDateFormats(&ctxt);
1993 }
1994 
1995 /**************************************************************************
1996  *              EnumDateFormatsExW    (KERNEL32.@)
1997  */
1998 BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags)
1999 {
2000     struct enumdateformats_context ctxt;
2001 
2002     ctxt.type = CALLBACK_ENUMPROCEX;
2003     ctxt.u.callbackex = proc;
2004     ctxt.lcid = lcid;
2005     ctxt.flags = flags;
2006     ctxt.unicode = TRUE;
2007 
2008     return NLS_EnumDateFormats(&ctxt);
2009 }
2010 
2011 /**************************************************************************
2012  *              EnumDateFormatsA	(KERNEL32.@)
2013  *
2014  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
2015  * LOCALE_NOUSEROVERRIDE here as well?
2016  */
2017 BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
2018 {
2019     struct enumdateformats_context ctxt;
2020 
2021     ctxt.type = CALLBACK_ENUMPROC;
2022     ctxt.u.callback = (DATEFMT_ENUMPROCW)proc;
2023     ctxt.lcid = lcid;
2024     ctxt.flags = flags;
2025     ctxt.unicode = FALSE;
2026 
2027     return NLS_EnumDateFormats(&ctxt);
2028 }
2029 
2030 /**************************************************************************
2031  *              EnumDateFormatsW	(KERNEL32.@)
2032  */
2033 BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
2034 {
2035     struct enumdateformats_context ctxt;
2036 
2037     ctxt.type = CALLBACK_ENUMPROC;
2038     ctxt.u.callback = proc;
2039     ctxt.lcid = lcid;
2040     ctxt.flags = flags;
2041     ctxt.unicode = TRUE;
2042 
2043     return NLS_EnumDateFormats(&ctxt);
2044 }
2045 
2046 #if _WIN32_WINNT >= 0x600
2047 /**************************************************************************
2048  *              EnumDateFormatsExEx	(KERNEL32.@)
2049  */
2050 BOOL WINAPI EnumDateFormatsExEx(DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale, DWORD flags, LPARAM lParam)
2051 {
2052     struct enumdateformats_context ctxt;
2053 
2054     ctxt.type = CALLBACK_ENUMPROCEXEX;
2055     ctxt.u.callbackexex = proc;
2056     ctxt.lcid = LocaleNameToLCID(locale, 0);
2057     ctxt.flags = flags;
2058     ctxt.lParam = lParam;
2059     ctxt.unicode = TRUE;
2060 
2061     return NLS_EnumDateFormats(&ctxt);
2062 }
2063 #endif /* _WIN32_WINNT >= 0x600 */
2064 
2065 struct enumtimeformats_context {
2066     enum enum_callback_type type;  /* callback kind */
2067     union {
2068         TIMEFMT_ENUMPROCW  callback;     /* user callback pointer */
2069         TIMEFMT_ENUMPROCEX callbackex;
2070     } u;
2071     LCID   lcid;    /* locale of interest */
2072     DWORD  flags;
2073     LPARAM lParam;
2074     BOOL   unicode; /* A vs W callback type, only for regular and Ex callbacks */
2075 };
2076 
2077 static BOOL NLS_EnumTimeFormats(struct enumtimeformats_context *ctxt)
2078 {
2079     WCHAR bufW[256];
2080     char bufA[256];
2081     LCTYPE lctype;
2082     INT ret;
2083 
2084     if (!ctxt->u.callback)
2085     {
2086         SetLastError(ERROR_INVALID_PARAMETER);
2087         return FALSE;
2088     }
2089 
2090     switch (ctxt->flags & ~LOCALE_USE_CP_ACP)
2091     {
2092     case 0:
2093         lctype = LOCALE_STIMEFORMAT;
2094         break;
2095     case TIME_NOSECONDS:
2096         lctype = LOCALE_SSHORTTIME;
2097         break;
2098     default:
2099         FIXME("Unknown time format (%d)\n", ctxt->flags);
2100         SetLastError(ERROR_INVALID_PARAMETER);
2101         return FALSE;
2102     }
2103 
2104     lctype |= ctxt->flags & LOCALE_USE_CP_ACP;
2105     if (ctxt->unicode)
2106         ret = GetLocaleInfoW(ctxt->lcid, lctype, bufW, ARRAY_SIZE(bufW));
2107     else
2108         ret = GetLocaleInfoA(ctxt->lcid, lctype, bufA, ARRAY_SIZE(bufA));
2109 
2110     if (ret)
2111     {
2112         switch (ctxt->type)
2113         {
2114         case CALLBACK_ENUMPROC:
2115             ctxt->u.callback(ctxt->unicode ? bufW : (WCHAR*)bufA);
2116             break;
2117         case CALLBACK_ENUMPROCEX:
2118             ctxt->u.callbackex(bufW, ctxt->lParam);
2119             break;
2120         default:
2121             ;
2122         }
2123     }
2124 
2125     return TRUE;
2126 }
2127 
2128 /**************************************************************************
2129  *              EnumTimeFormatsA	(KERNEL32.@)
2130  *
2131  * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
2132  * LOCALE_NOUSEROVERRIDE here as well?
2133  */
2134 BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
2135 {
2136     struct enumtimeformats_context ctxt;
2137 
2138     /* EnumTimeFormatsA doesn't support flags, EnumTimeFormatsW does. */
2139     if (flags & ~LOCALE_USE_CP_ACP)
2140     {
2141         SetLastError(ERROR_INVALID_FLAGS);
2142         return FALSE;
2143     }
2144 
2145     ctxt.type = CALLBACK_ENUMPROC;
2146     ctxt.u.callback = (TIMEFMT_ENUMPROCW)proc;
2147     ctxt.lcid = lcid;
2148     ctxt.flags = flags;
2149     ctxt.unicode = FALSE;
2150 
2151     return NLS_EnumTimeFormats(&ctxt);
2152 }
2153 
2154 /**************************************************************************
2155  *              EnumTimeFormatsW	(KERNEL32.@)
2156  */
2157 BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
2158 {
2159     struct enumtimeformats_context ctxt;
2160 
2161     ctxt.type = CALLBACK_ENUMPROC;
2162     ctxt.u.callback = proc;
2163     ctxt.lcid = lcid;
2164     ctxt.flags = flags;
2165     ctxt.unicode = TRUE;
2166 
2167     return NLS_EnumTimeFormats(&ctxt);
2168 }
2169 
2170 #if _WIN32_WINNT >= 0x600
2171 /**************************************************************************
2172  *              EnumTimeFormatsEx	(KERNEL32.@)
2173  */
2174 BOOL WINAPI EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX proc, const WCHAR *locale, DWORD flags, LPARAM lParam)
2175 {
2176     struct enumtimeformats_context ctxt;
2177 
2178     ctxt.type = CALLBACK_ENUMPROCEX;
2179     ctxt.u.callbackex = proc;
2180     ctxt.lcid = LocaleNameToLCID(locale, 0);
2181     ctxt.flags = flags;
2182     ctxt.lParam = lParam;
2183     ctxt.unicode = TRUE;
2184 
2185     return NLS_EnumTimeFormats(&ctxt);
2186 }
2187 #endif /* _WIN32_WINNT >= 0x600 */
2188 
2189 struct enumcalendar_context {
2190     enum enum_callback_type type;  /* callback kind */
2191     union {
2192         CALINFO_ENUMPROCW    callback;     /* user callback pointer */
2193         CALINFO_ENUMPROCEXW  callbackex;
2194         CALINFO_ENUMPROCEXEX callbackexex;
2195     } u;
2196     LCID    lcid;     /* locale of interest */
2197     CALID   calendar; /* specific calendar or ENUM_ALL_CALENDARS */
2198     CALTYPE caltype;  /* calendar information type */
2199     LPARAM  lParam;   /* user input parameter passed to callback, for ExEx case only */
2200     BOOL    unicode;  /* A vs W callback type, only for regular and Ex callbacks */
2201 };
2202 
2203 /******************************************************************************
2204  * NLS_EnumCalendarInfo <internal>
2205  * Enumerates calendar information for a specified locale.
2206  *
2207  * PARAMS
2208  *    ctxt [I] enumeration context, see 'struct enumcalendar_context'
2209  *
2210  * RETURNS
2211  *    Success: TRUE.
2212  *    Failure: FALSE. Use GetLastError() to determine the cause.
2213  *
2214  * NOTES
2215  *    When the ANSI version of this function is used with a Unicode-only LCID,
2216  *    the call can succeed because the system uses the system code page.
2217  *    However, characters that are undefined in the system code page appear
2218  *    in the string as a question mark (?).
2219  *
2220  * TODO
2221  *    The above note should be respected by GetCalendarInfoA.
2222  */
2223 static BOOL NLS_EnumCalendarInfo(const struct enumcalendar_context *ctxt)
2224 {
2225   WCHAR *buf, *opt = NULL, *iter = NULL;
2226   CALID calendar = ctxt->calendar;
2227   BOOL ret = FALSE;
2228   int bufSz = 200;		/* the size of the buffer */
2229 
2230   if (ctxt->u.callback == NULL)
2231   {
2232     SetLastError(ERROR_INVALID_PARAMETER);
2233     return FALSE;
2234   }
2235 
2236   buf = HeapAlloc(GetProcessHeap(), 0, bufSz);
2237   if (buf == NULL)
2238   {
2239     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2240     return FALSE;
2241   }
2242 
2243   if (calendar == ENUM_ALL_CALENDARS)
2244   {
2245     int optSz = GetLocaleInfoW(ctxt->lcid, LOCALE_IOPTIONALCALENDAR, NULL, 0);
2246     if (optSz > 1)
2247     {
2248       opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR));
2249       if (opt == NULL)
2250       {
2251         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2252         goto cleanup;
2253       }
2254       if (GetLocaleInfoW(ctxt->lcid, LOCALE_IOPTIONALCALENDAR, opt, optSz))
2255         iter = opt;
2256     }
2257     calendar = NLS_GetLocaleNumber(ctxt->lcid, LOCALE_ICALENDARTYPE);
2258   }
2259 
2260   while (TRUE)			/* loop through calendars */
2261   {
2262     do				/* loop until there's no error */
2263     {
2264       if (ctxt->caltype & CAL_RETURN_NUMBER)
2265         ret = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, NULL, bufSz / sizeof(WCHAR), (LPDWORD)buf);
2266       else if (ctxt->unicode)
2267         ret = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, buf, bufSz / sizeof(WCHAR), NULL);
2268       else ret = GetCalendarInfoA(ctxt->lcid, calendar, ctxt->caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL);
2269 
2270       if (!ret)
2271       {
2272         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2273         {				/* so resize it */
2274           int newSz;
2275           if (ctxt->unicode)
2276             newSz = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, NULL, 0, NULL) * sizeof(WCHAR);
2277           else newSz = GetCalendarInfoA(ctxt->lcid, calendar, ctxt->caltype, NULL, 0, NULL) * sizeof(CHAR);
2278           if (bufSz >= newSz)
2279           {
2280             ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz);
2281             goto cleanup;
2282           }
2283           bufSz = newSz;
2284           WARN("Buffer too small; resizing to %d bytes.\n", bufSz);
2285           buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz);
2286           if (buf == NULL)
2287             goto cleanup;
2288         } else goto cleanup;
2289       }
2290     } while (!ret);
2291 
2292     /* Here we are. We pass the buffer to the correct version of
2293      * the callback. Because it's not the same number of params,
2294      * we must check for Ex, but we don't care about Unicode
2295      * because the buffer is already in the correct format.
2296      */
2297     switch (ctxt->type)
2298     {
2299     case CALLBACK_ENUMPROC:
2300       ret = ctxt->u.callback(buf);
2301       break;
2302     case CALLBACK_ENUMPROCEX:
2303       ret = ctxt->u.callbackex(buf, calendar);
2304       break;
2305     case CALLBACK_ENUMPROCEXEX:
2306       ret = ctxt->u.callbackexex(buf, calendar, NULL, ctxt->lParam);
2307       break;
2308     default:
2309       ;
2310     }
2311 
2312     if (!ret) {			/* the callback told to stop */
2313       ret = TRUE;
2314       break;
2315     }
2316 
2317     if ((iter == NULL) || (*iter == 0))	/* no more calendars */
2318       break;
2319 
2320     calendar = 0;
2321     while ((*iter >= '0') && (*iter <= '9'))
2322       calendar = calendar * 10 + *iter++ - '0';
2323 
2324     if (*iter++ != 0)
2325     {
2326       SetLastError(ERROR_BADDB);
2327       ret = FALSE;
2328       break;
2329     }
2330   }
2331 
2332 cleanup:
2333   HeapFree(GetProcessHeap(), 0, opt);
2334   HeapFree(GetProcessHeap(), 0, buf);
2335   return ret;
2336 }
2337 
2338 /******************************************************************************
2339  *		EnumCalendarInfoA	[KERNEL32.@]
2340  */
2341 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
2342                                CALID calendar,CALTYPE caltype )
2343 {
2344   struct enumcalendar_context ctxt;
2345 
2346   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2347 
2348   ctxt.type = CALLBACK_ENUMPROC;
2349   ctxt.u.callback = (CALINFO_ENUMPROCW)calinfoproc;
2350   ctxt.lcid = locale;
2351   ctxt.calendar = calendar;
2352   ctxt.caltype = caltype;
2353   ctxt.lParam = 0;
2354   ctxt.unicode = FALSE;
2355   return NLS_EnumCalendarInfo(&ctxt);
2356 }
2357 
2358 /******************************************************************************
2359  *		EnumCalendarInfoW	[KERNEL32.@]
2360  */
2361 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale,
2362                                CALID calendar,CALTYPE caltype )
2363 {
2364   struct enumcalendar_context ctxt;
2365 
2366   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2367 
2368   ctxt.type = CALLBACK_ENUMPROC;
2369   ctxt.u.callback = calinfoproc;
2370   ctxt.lcid = locale;
2371   ctxt.calendar = calendar;
2372   ctxt.caltype = caltype;
2373   ctxt.lParam = 0;
2374   ctxt.unicode = TRUE;
2375   return NLS_EnumCalendarInfo(&ctxt);
2376 }
2377 
2378 /******************************************************************************
2379  *		EnumCalendarInfoExA	[KERNEL32.@]
2380  */
2381 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale,
2382                                  CALID calendar,CALTYPE caltype )
2383 {
2384   struct enumcalendar_context ctxt;
2385 
2386   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2387 
2388   ctxt.type = CALLBACK_ENUMPROCEX;
2389   ctxt.u.callbackex = (CALINFO_ENUMPROCEXW)calinfoproc;
2390   ctxt.lcid = locale;
2391   ctxt.calendar = calendar;
2392   ctxt.caltype = caltype;
2393   ctxt.lParam = 0;
2394   ctxt.unicode = FALSE;
2395   return NLS_EnumCalendarInfo(&ctxt);
2396 }
2397 
2398 /******************************************************************************
2399  *		EnumCalendarInfoExW	[KERNEL32.@]
2400  */
2401 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale,
2402                                  CALID calendar,CALTYPE caltype )
2403 {
2404   struct enumcalendar_context ctxt;
2405 
2406   TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
2407 
2408   ctxt.type = CALLBACK_ENUMPROCEX;
2409   ctxt.u.callbackex = calinfoproc;
2410   ctxt.lcid = locale;
2411   ctxt.calendar = calendar;
2412   ctxt.caltype = caltype;
2413   ctxt.lParam = 0;
2414   ctxt.unicode = TRUE;
2415   return NLS_EnumCalendarInfo(&ctxt);
2416 }
2417 
2418 #if _WIN32_WINNT >= 0x600
2419 /******************************************************************************
2420  *		EnumCalendarInfoExEx	[KERNEL32.@]
2421  */
2422 BOOL WINAPI EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX calinfoproc, LPCWSTR locale, CALID calendar,
2423     LPCWSTR reserved, CALTYPE caltype, LPARAM lParam)
2424 {
2425   struct enumcalendar_context ctxt;
2426 
2427   TRACE("(%p,%s,0x%08x,%p,0x%08x,0x%ld)\n", calinfoproc, debugstr_w(locale), calendar, reserved, caltype, lParam);
2428 
2429   ctxt.type = CALLBACK_ENUMPROCEXEX;
2430   ctxt.u.callbackexex = calinfoproc;
2431   ctxt.lcid = LocaleNameToLCID(locale, 0);
2432   ctxt.calendar = calendar;
2433   ctxt.caltype = caltype;
2434   ctxt.lParam = lParam;
2435   ctxt.unicode = TRUE;
2436   return NLS_EnumCalendarInfo(&ctxt);
2437 }
2438 #endif /* _WIN32_WINNT >= 0x600 */
2439 
2440 /*********************************************************************
2441  *	GetCalendarInfoA				(KERNEL32.@)
2442  *
2443  */
2444 int WINAPI GetCalendarInfoA(LCID lcid, CALID Calendar, CALTYPE CalType,
2445 			    LPSTR lpCalData, int cchData, LPDWORD lpValue)
2446 {
2447     int ret, cchDataW = cchData;
2448     LPWSTR lpCalDataW = NULL;
2449 #ifdef __REACTOS__
2450     DWORD cp = CP_ACP;
2451     if (!(CalType & CAL_USE_CP_ACP))
2452     {
2453         DWORD dwFlags = ((CalType & CAL_NOUSEROVERRIDE) ? LOCALE_NOUSEROVERRIDE : 0);
2454         const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
2455         if (!node)
2456         {
2457             SetLastError(ERROR_INVALID_PARAMETER);
2458             return 0;
2459         }
2460         cp = node->dwCodePage;
2461     }
2462     if ((CalType & 0xFFFF) == CAL_SABBREVERASTRING)
2463     {
2464         /* NOTE: CAL_SABBREVERASTRING is not supported in GetCalendarInfoA */
2465         SetLastError(ERROR_INVALID_PARAMETER);
2466         return 0;
2467     }
2468 #endif
2469 
2470     if (NLS_IsUnicodeOnlyLcid(lcid))
2471     {
2472       SetLastError(ERROR_INVALID_PARAMETER);
2473       return 0;
2474     }
2475 
2476     if (!cchData && !(CalType & CAL_RETURN_NUMBER))
2477         cchDataW = GetCalendarInfoW(lcid, Calendar, CalType, NULL, 0, NULL);
2478     if (!(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchDataW*sizeof(WCHAR))))
2479         return 0;
2480 
2481     ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchDataW, lpValue);
2482     if(ret && lpCalDataW && lpCalData)
2483 #ifdef __REACTOS__
2484         ret = WideCharToMultiByte(cp, 0, lpCalDataW, -1, lpCalData, cchData, NULL, NULL);
2485 #else
2486         ret = WideCharToMultiByte(CP_ACP, 0, lpCalDataW, -1, lpCalData, cchData, NULL, NULL);
2487 #endif
2488     else if (CalType & CAL_RETURN_NUMBER)
2489         ret *= sizeof(WCHAR);
2490     HeapFree(GetProcessHeap(), 0, lpCalDataW);
2491 
2492     return ret;
2493 }
2494 
2495 /*********************************************************************
2496  *	GetCalendarInfoW				(KERNEL32.@)
2497  *
2498  */
2499 int WINAPI GetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType,
2500 			    LPWSTR lpCalData, int cchData, LPDWORD lpValue)
2501 {
2502     static const LCTYPE caltype_lctype_map[] = {
2503         0, /* not used */
2504         0, /* CAL_ICALINTVALUE */
2505         0, /* CAL_SCALNAME */
2506         0, /* CAL_IYEAROFFSETRANGE */
2507         0, /* CAL_SERASTRING */
2508         LOCALE_SSHORTDATE,
2509         LOCALE_SLONGDATE,
2510         LOCALE_SDAYNAME1,
2511         LOCALE_SDAYNAME2,
2512         LOCALE_SDAYNAME3,
2513         LOCALE_SDAYNAME4,
2514         LOCALE_SDAYNAME5,
2515         LOCALE_SDAYNAME6,
2516         LOCALE_SDAYNAME7,
2517         LOCALE_SABBREVDAYNAME1,
2518         LOCALE_SABBREVDAYNAME2,
2519         LOCALE_SABBREVDAYNAME3,
2520         LOCALE_SABBREVDAYNAME4,
2521         LOCALE_SABBREVDAYNAME5,
2522         LOCALE_SABBREVDAYNAME6,
2523         LOCALE_SABBREVDAYNAME7,
2524         LOCALE_SMONTHNAME1,
2525         LOCALE_SMONTHNAME2,
2526         LOCALE_SMONTHNAME3,
2527         LOCALE_SMONTHNAME4,
2528         LOCALE_SMONTHNAME5,
2529         LOCALE_SMONTHNAME6,
2530         LOCALE_SMONTHNAME7,
2531         LOCALE_SMONTHNAME8,
2532         LOCALE_SMONTHNAME9,
2533         LOCALE_SMONTHNAME10,
2534         LOCALE_SMONTHNAME11,
2535         LOCALE_SMONTHNAME12,
2536         LOCALE_SMONTHNAME13,
2537         LOCALE_SABBREVMONTHNAME1,
2538         LOCALE_SABBREVMONTHNAME2,
2539         LOCALE_SABBREVMONTHNAME3,
2540         LOCALE_SABBREVMONTHNAME4,
2541         LOCALE_SABBREVMONTHNAME5,
2542         LOCALE_SABBREVMONTHNAME6,
2543         LOCALE_SABBREVMONTHNAME7,
2544         LOCALE_SABBREVMONTHNAME8,
2545         LOCALE_SABBREVMONTHNAME9,
2546         LOCALE_SABBREVMONTHNAME10,
2547         LOCALE_SABBREVMONTHNAME11,
2548         LOCALE_SABBREVMONTHNAME12,
2549         LOCALE_SABBREVMONTHNAME13,
2550         LOCALE_SYEARMONTH,
2551         0, /* CAL_ITWODIGITYEARMAX */
2552 #if (WINVER >= 0x0600) /* ReactOS */
2553         LOCALE_SSHORTESTDAYNAME1,
2554         LOCALE_SSHORTESTDAYNAME2,
2555         LOCALE_SSHORTESTDAYNAME3,
2556         LOCALE_SSHORTESTDAYNAME4,
2557         LOCALE_SSHORTESTDAYNAME5,
2558         LOCALE_SSHORTESTDAYNAME6,
2559         LOCALE_SSHORTESTDAYNAME7,
2560 #endif
2561         LOCALE_SMONTHDAY,
2562         0, /* CAL_SABBREVERASTRING */
2563     };
2564     DWORD localeflags = 0;
2565     CALTYPE calinfo;
2566 
2567     if (CalType & CAL_NOUSEROVERRIDE)
2568 	FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
2569     if (CalType & CAL_USE_CP_ACP)
2570 	FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
2571 
2572     if (CalType & CAL_RETURN_NUMBER) {
2573         if (!lpValue)
2574         {
2575             SetLastError( ERROR_INVALID_PARAMETER );
2576             return 0;
2577         }
2578 	if (lpCalData != NULL)
2579 	    WARN("lpCalData not NULL (%p) when it should!\n", lpCalData);
2580 	if (cchData != 0)
2581 	    WARN("cchData not 0 (%d) when it should!\n", cchData);
2582     } else {
2583 	if (lpValue != NULL)
2584 	    WARN("lpValue not NULL (%p) when it should!\n", lpValue);
2585     }
2586 
2587     /* FIXME: No verification is made yet wrt Locale
2588      * for the CALTYPES not requiring GetLocaleInfoA */
2589 
2590     calinfo = CalType & 0xffff;
2591 
2592 #ifdef __REACTOS__
2593     if (CalType & LOCALE_RETURN_GENITIVE_NAMES)
2594 #else
2595     if (CalType & CAL_RETURN_GENITIVE_NAMES)
2596 #endif
2597         localeflags |= LOCALE_RETURN_GENITIVE_NAMES;
2598 
2599     switch (calinfo) {
2600 	case CAL_ICALINTVALUE:
2601 #ifdef __REACTOS__
2602         if (IS_LCID_JAPANESE(Locale))
2603         {
2604             if (CalType & CAL_RETURN_NUMBER)
2605             {
2606                 *lpValue = CAL_JAPAN;
2607                 return sizeof(DWORD) / sizeof(WCHAR);
2608             }
2609             else
2610             {
2611                 static const WCHAR fmtW[] = {'%','u',0};
2612                 WCHAR buffer[10];
2613                 int ret = snprintfW( buffer, 10, fmtW, CAL_JAPAN ) + 1;
2614                 if (!lpCalData) return ret;
2615                 if (ret <= cchData)
2616                 {
2617                     strcpyW( lpCalData, buffer );
2618                     return ret;
2619                 }
2620                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2621                 return 0;
2622             }
2623         }
2624 #endif
2625             if (CalType & CAL_RETURN_NUMBER)
2626                 return GetLocaleInfoW(Locale, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE,
2627                         (LPWSTR)lpValue, 2);
2628             return GetLocaleInfoW(Locale, LOCALE_ICALENDARTYPE, lpCalData, cchData);
2629 	case CAL_SCALNAME:
2630 #ifdef __REACTOS__
2631         if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN)
2632         {
2633             // Wareki
2634             lpCalData[0] = 0x548C;
2635             lpCalData[1] = 0x66A6;
2636             lpCalData[2] = 0;
2637             return 3;
2638         }
2639 #endif
2640             FIXME("Unimplemented caltype %d\n", calinfo);
2641             if (lpCalData) *lpCalData = 0;
2642 	    return 1;
2643 	case CAL_IYEAROFFSETRANGE:
2644 #ifdef __REACTOS__
2645         if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN)
2646         {
2647             PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL);
2648             if (pEra)
2649             {
2650                 if (CalType & CAL_RETURN_NUMBER)
2651                 {
2652                     *lpValue = pEra->wYear;
2653                     return sizeof(DWORD) / sizeof(WCHAR);
2654                 }
2655                 else
2656                 {
2657                     static const WCHAR fmtW[] = {'%','u',0};
2658                     WCHAR buffer[10];
2659                     int ret = snprintfW( buffer, 10, fmtW, pEra->wYear ) + 1;
2660                     if (!lpCalData) return ret;
2661                     if (ret <= cchData)
2662                     {
2663                         strcpyW( lpCalData, buffer );
2664                         return ret;
2665                     }
2666                     SetLastError( ERROR_INSUFFICIENT_BUFFER );
2667                     return 0;
2668                 }
2669             }
2670             else
2671             {
2672                 SetLastError(ERROR_INVALID_PARAMETER);
2673                 return 0;
2674             }
2675         }
2676 #endif
2677             FIXME("Unimplemented caltype %d\n", calinfo);
2678 	    return 0;
2679 	case CAL_SERASTRING:
2680 #ifdef __REACTOS__
2681         if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN)
2682         {
2683             PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL);
2684             if (pEra)
2685             {
2686                 RtlStringCchCopyW(lpCalData, cchData, pEra->szEraName);
2687                 return strlenW(lpCalData) + 1;
2688             }
2689             else
2690             {
2691                 SetLastError(ERROR_INVALID_PARAMETER);
2692                 return 0;
2693             }
2694         }
2695 #endif
2696             FIXME("Unimplemented caltype %d\n", calinfo);
2697 	    return 0;
2698 	case CAL_SSHORTDATE:
2699 	case CAL_SLONGDATE:
2700 	case CAL_SDAYNAME1:
2701 	case CAL_SDAYNAME2:
2702 	case CAL_SDAYNAME3:
2703 	case CAL_SDAYNAME4:
2704 	case CAL_SDAYNAME5:
2705 	case CAL_SDAYNAME6:
2706 	case CAL_SDAYNAME7:
2707 	case CAL_SABBREVDAYNAME1:
2708 	case CAL_SABBREVDAYNAME2:
2709 	case CAL_SABBREVDAYNAME3:
2710 	case CAL_SABBREVDAYNAME4:
2711 	case CAL_SABBREVDAYNAME5:
2712 	case CAL_SABBREVDAYNAME6:
2713 	case CAL_SABBREVDAYNAME7:
2714 	case CAL_SMONTHNAME1:
2715 	case CAL_SMONTHNAME2:
2716 	case CAL_SMONTHNAME3:
2717 	case CAL_SMONTHNAME4:
2718 	case CAL_SMONTHNAME5:
2719 	case CAL_SMONTHNAME6:
2720 	case CAL_SMONTHNAME7:
2721 	case CAL_SMONTHNAME8:
2722 	case CAL_SMONTHNAME9:
2723 	case CAL_SMONTHNAME10:
2724 	case CAL_SMONTHNAME11:
2725 	case CAL_SMONTHNAME12:
2726 	case CAL_SMONTHNAME13:
2727 	case CAL_SABBREVMONTHNAME1:
2728 	case CAL_SABBREVMONTHNAME2:
2729 	case CAL_SABBREVMONTHNAME3:
2730 	case CAL_SABBREVMONTHNAME4:
2731 	case CAL_SABBREVMONTHNAME5:
2732 	case CAL_SABBREVMONTHNAME6:
2733 	case CAL_SABBREVMONTHNAME7:
2734 	case CAL_SABBREVMONTHNAME8:
2735 	case CAL_SABBREVMONTHNAME9:
2736 	case CAL_SABBREVMONTHNAME10:
2737 	case CAL_SABBREVMONTHNAME11:
2738 	case CAL_SABBREVMONTHNAME12:
2739 	case CAL_SABBREVMONTHNAME13:
2740 	case CAL_SYEARMONTH:
2741             return GetLocaleInfoW(Locale, caltype_lctype_map[calinfo] | localeflags, lpCalData, cchData);
2742 	case CAL_ITWODIGITYEARMAX:
2743 #ifdef __REACTOS__
2744         if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN)
2745         {
2746             if (CalType & CAL_RETURN_NUMBER)
2747             {
2748                 *lpValue = JAPANESE_MAX_TWODIGITYEAR;
2749                 return sizeof(DWORD) / sizeof(WCHAR);
2750             }
2751             else
2752             {
2753                 static const WCHAR fmtW[] = {'%','u',0};
2754                 WCHAR buffer[10];
2755                 int ret = snprintfW( buffer, 10, fmtW, JAPANESE_MAX_TWODIGITYEAR ) + 1;
2756                 if (!lpCalData) return ret;
2757                 if (ret <= cchData)
2758                 {
2759                     strcpyW( lpCalData, buffer );
2760                     return ret;
2761                 }
2762                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2763                 return 0;
2764             }
2765         }
2766 #endif
2767             if (CalType & CAL_RETURN_NUMBER)
2768             {
2769                 *lpValue = CALINFO_MAX_YEAR;
2770                 return sizeof(DWORD) / sizeof(WCHAR);
2771             }
2772             else
2773             {
2774                 static const WCHAR fmtW[] = {'%','u',0};
2775                 WCHAR buffer[10];
2776                 int ret = snprintfW( buffer, 10, fmtW, CALINFO_MAX_YEAR  ) + 1;
2777                 if (!lpCalData) return ret;
2778                 if (ret <= cchData)
2779                 {
2780                     strcpyW( lpCalData, buffer );
2781                     return ret;
2782                 }
2783                 SetLastError( ERROR_INSUFFICIENT_BUFFER );
2784                 return 0;
2785             }
2786 	    break;
2787 #ifdef __REACTOS__
2788     case CAL_SABBREVERASTRING:
2789         if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN)
2790         {
2791             PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL);
2792             if (pEra)
2793             {
2794                 RtlStringCchCopyW(lpCalData, cchData, pEra->szEraAbbrev);
2795                 return strlenW(lpCalData) + 1;
2796             }
2797         }
2798         SetLastError(ERROR_INVALID_PARAMETER);
2799         return 0;
2800 #endif
2801 	default:
2802             FIXME("Unknown caltype %d\n", calinfo);
2803             SetLastError(ERROR_INVALID_FLAGS);
2804             return 0;
2805     }
2806     return 0;
2807 }
2808 
2809 #if _WIN32_WINNT >= 0x600
2810 /*********************************************************************
2811  *	GetCalendarInfoEx				(KERNEL32.@)
2812  */
2813 int WINAPI GetCalendarInfoEx(LPCWSTR locale, CALID calendar, LPCWSTR lpReserved, CALTYPE caltype,
2814     LPWSTR data, int len, DWORD *value)
2815 {
2816     static int once;
2817 
2818     LCID lcid = LocaleNameToLCID(locale, 0);
2819     if (!once++)
2820         FIXME("(%s, %d, %p, 0x%08x, %p, %d, %p): semi-stub\n", debugstr_w(locale), calendar, lpReserved, caltype,
2821         data, len, value);
2822     return GetCalendarInfoW(lcid, calendar, caltype, data, len, value);
2823 }
2824 #endif
2825 
2826 /*********************************************************************
2827  *	SetCalendarInfoA				(KERNEL32.@)
2828  *
2829  */
2830 int WINAPI	SetCalendarInfoA(LCID Locale, CALID Calendar, CALTYPE CalType, LPCSTR lpCalData)
2831 {
2832     FIXME("(%08x,%08x,%08x,%s): stub\n",
2833 	  Locale, Calendar, CalType, debugstr_a(lpCalData));
2834     return 0;
2835 }
2836 
2837 /*********************************************************************
2838  *	SetCalendarInfoW				(KERNEL32.@)
2839  *
2840  *
2841  */
2842 int WINAPI	SetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, LPCWSTR lpCalData)
2843 {
2844     FIXME("(%08x,%08x,%08x,%s): stub\n",
2845 	  Locale, Calendar, CalType, debugstr_w(lpCalData));
2846     return 0;
2847 }
2848