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