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