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