kernel32: Add support for starting binaries in 32/64-bit mode from the other mode.
[wine] / dlls / kernel32 / lcformat.c
CommitLineData
90b68fc7
AJ
1/*
2 * Locale-dependent format handling
3 *
4 * Copyright 1995 Martin von Loewis
5 * Copyright 1998 David Lee Lambert
bcc77a89 6 * Copyright 2000 Julio César Gázquez
31f1a801 7 * Copyright 2003 Jon Griffiths
8873d90f 8 * Copyright 2005 Dmitry Timoshkov
90b68fc7
AJ
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
360a3f91 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
90b68fc7
AJ
23 */
24
25#include "config.h"
26#include "wine/port.h"
27
28#include <string.h>
e37c6e18 29#include <stdarg.h>
90b68fc7
AJ
30#include <stdio.h>
31#include <stdlib.h>
32
33#include "windef.h"
34#include "winbase.h"
35#include "wine/unicode.h"
36#include "wine/debug.h"
31f1a801 37#include "winternl.h"
90b68fc7 38
0f36d124
MM
39#include "kernel_private.h"
40
90b68fc7
AJ
41WINE_DEFAULT_DEBUG_CHANNEL(nls);
42
31f1a801
JG
43#define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
44#define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
45
46/* Since calculating the formatting data for each locale is time-consuming,
47 * we get the format data for each locale only once and cache it in memory.
48 * We cache both the system default and user overridden data, after converting
49 * them into the formats that the functions here expect. Since these functions
50 * will typically be called with only a small number of the total locales
51 * installed, the memory overhead is minimal while the speedup is significant.
52 *
53 * Our cache takes the form of a singly linked list, whose node is below:
54 */
8ea4102a 55#define NLS_NUM_CACHED_STRINGS 57
31f1a801
JG
56
57typedef struct _NLS_FORMAT_NODE
58{
59 LCID lcid; /* Locale Id */
60 DWORD dwFlags; /* 0 or LOCALE_NOUSEROVERRIDE */
61 DWORD dwCodePage; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
62 NUMBERFMTW fmt; /* Default format for numbers */
63 CURRENCYFMTW cyfmt; /* Default format for currencies */
64 LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
65 WCHAR szShortAM[2]; /* Short 'AM' marker */
66 WCHAR szShortPM[2]; /* Short 'PM' marker */
67 struct _NLS_FORMAT_NODE *next;
68} NLS_FORMAT_NODE;
69
70/* Macros to get particular data strings from a format node */
71#define GetNegative(fmt) fmt->lppszStrings[0]
72#define GetLongDate(fmt) fmt->lppszStrings[1]
73#define GetShortDate(fmt) fmt->lppszStrings[2]
74#define GetTime(fmt) fmt->lppszStrings[3]
8ea4102a
NS
75#define GetAM(fmt) fmt->lppszStrings[54]
76#define GetPM(fmt) fmt->lppszStrings[55]
77#define GetYearMonth(fmt) fmt->lppszStrings[56]
31f1a801 78
8ea4102a
NS
79#define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
80#define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
81#define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
82#define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
83#define GetShortMonth(fmt,mth) fmt->lppszStrings[42 + mth]
31f1a801
JG
84
85/* Write access to the cache is protected by this critical section */
86static CRITICAL_SECTION NLS_FormatsCS;
87static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
88{
89 0, 0, &NLS_FormatsCS,
90 { &NLS_FormatsCS_debug.ProcessLocksList,
91 &NLS_FormatsCS_debug.ProcessLocksList },
20a1a203 92 0, 0, { (DWORD_PTR)(__FILE__ ": NLS_Formats") }
31f1a801
JG
93};
94static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
95
96/**************************************************************************
97 * NLS_GetLocaleNumber <internal>
98 *
99 * Get a numeric locale format value.
100 */
d9339f83 101static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
31f1a801
JG
102{
103 WCHAR szBuff[80];
104 DWORD dwVal = 0;
105
106 szBuff[0] = '\0';
107 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
108
109 if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
110 dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
111 else
112 {
113 const WCHAR* iter = szBuff;
114 dwVal = 0;
115 while(*iter >= '0' && *iter <= '9')
116 dwVal = dwVal * 10 + (*iter++ - '0');
117 }
118 return dwVal;
119}
120
121/**************************************************************************
122 * NLS_GetLocaleString <internal>
123 *
124 * Get a string locale format value.
125 */
d9339f83 126static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
31f1a801
JG
127{
128 WCHAR szBuff[80], *str;
129 DWORD dwLen;
130
131 szBuff[0] = '\0';
132 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
133 dwLen = strlenW(szBuff) + 1;
134 str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
135 if (str)
136 memcpy(str, szBuff, dwLen * sizeof(WCHAR));
137 return str;
138}
139
140#define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
8c38b880 141 TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
31f1a801
JG
142
143#define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
aab5e585 144 TRACE( #type ": %s\n", debugstr_w(str))
31f1a801
JG
145
146/**************************************************************************
147 * NLS_GetFormats <internal>
148 *
149 * Calculate (and cache) the number formats for a locale.
150 */
d9339f83 151static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
31f1a801
JG
152{
153 /* GetLocaleInfo() identifiers for cached formatting strings */
8ea4102a 154 static const LCTYPE NLS_LocaleIndices[] = {
31f1a801
JG
155 LOCALE_SNEGATIVESIGN,
156 LOCALE_SLONGDATE, LOCALE_SSHORTDATE,
157 LOCALE_STIMEFORMAT,
158 LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
159 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
160 LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
161 LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
162 LOCALE_SABBREVDAYNAME7,
163 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
164 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
165 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
166 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
8ea4102a
NS
167 LOCALE_SMONTHNAME1 | LOCALE_RETURN_GENITIVE_NAMES,
168 LOCALE_SMONTHNAME2 | LOCALE_RETURN_GENITIVE_NAMES,
169 LOCALE_SMONTHNAME3 | LOCALE_RETURN_GENITIVE_NAMES,
170 LOCALE_SMONTHNAME4 | LOCALE_RETURN_GENITIVE_NAMES,
171 LOCALE_SMONTHNAME5 | LOCALE_RETURN_GENITIVE_NAMES,
172 LOCALE_SMONTHNAME6 | LOCALE_RETURN_GENITIVE_NAMES,
173 LOCALE_SMONTHNAME7 | LOCALE_RETURN_GENITIVE_NAMES,
174 LOCALE_SMONTHNAME8 | LOCALE_RETURN_GENITIVE_NAMES,
175 LOCALE_SMONTHNAME9 | LOCALE_RETURN_GENITIVE_NAMES,
176 LOCALE_SMONTHNAME10 | LOCALE_RETURN_GENITIVE_NAMES,
177 LOCALE_SMONTHNAME11 | LOCALE_RETURN_GENITIVE_NAMES,
178 LOCALE_SMONTHNAME12 | LOCALE_RETURN_GENITIVE_NAMES,
31f1a801
JG
179 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
180 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
181 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
182 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
183 LOCALE_S1159, LOCALE_S2359,
184 LOCALE_SYEARMONTH
185 };
186 static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
187 NLS_FORMAT_NODE *node = NLS_CachedFormats;
188
189 dwFlags &= LOCALE_NOUSEROVERRIDE;
190
8c38b880 191 TRACE("(0x%04x,0x%08x)\n", lcid, dwFlags);
31f1a801
JG
192
193 /* See if we have already cached the locales number format */
194 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
195 node = node->next;
196
197 if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
198 {
199 NLS_FORMAT_NODE *new_node;
200 DWORD i;
201
202 TRACE("Creating new cache entry\n");
203
204 if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
205 return NULL;
206
207 GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
208
209 /* Number Format */
210 new_node->lcid = lcid;
211 new_node->dwFlags = dwFlags;
212 new_node->next = NULL;
213
214 GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
215 GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
216 GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
217
218 GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
219 if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
220 {
221 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
222 new_node->fmt.Grouping);
223 new_node->fmt.Grouping = 0;
224 }
225
226 GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
227 GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
228
229 /* Currency Format */
230 new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
231 new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
232
233 GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
234
235 if (new_node->cyfmt.Grouping > 9)
236 {
237 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
238 new_node->cyfmt.Grouping);
239 new_node->cyfmt.Grouping = 0;
240 }
241
242 GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
243 if (new_node->cyfmt.NegativeOrder > 15)
244 {
245 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
246 new_node->cyfmt.NegativeOrder);
247 new_node->cyfmt.NegativeOrder = 0;
248 }
249 GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
250 if (new_node->cyfmt.PositiveOrder > 3)
251 {
252 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
253 new_node->cyfmt.PositiveOrder);
254 new_node->cyfmt.PositiveOrder = 0;
255 }
256 GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
257 GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
258 GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
259
260 /* Date/Time Format info, negative character, etc */
261 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
262 {
263 GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
264 }
8ea4102a
NS
265 /* Save some memory if month genitive name is the same or not present */
266 for (i = 0; i < 12; i++)
267 {
268 if (strcmpW(GetLongMonth(new_node, i), GetGenitiveMonth(new_node, i)) == 0)
269 {
270 HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node, i));
271 GetGenitiveMonth(new_node, i) = NULL;
272 }
273 }
274
31f1a801
JG
275 new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
276 new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
277
278 /* Now add the computed format to the cache */
279 RtlEnterCriticalSection(&NLS_FormatsCS);
280
281 /* Search again: We may have raced to add the node */
282 node = NLS_CachedFormats;
283 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
284 node = node->next;
285
286 if (!node)
287 {
288 node = NLS_CachedFormats = new_node; /* Empty list */
289 new_node = NULL;
290 }
291 else if (node->lcid != lcid || node->dwFlags != dwFlags)
292 {
293 node->next = new_node; /* Not in the list, add to end */
294 node = new_node;
295 new_node = NULL;
296 }
297
298 RtlLeaveCriticalSection(&NLS_FormatsCS);
299
300 if (new_node)
301 {
302 /* We raced and lost: The node was already added by another thread.
303 * node points to the currently cached node, so free new_node.
304 */
305 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
306 HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
307 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
308 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
309 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
310 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
311 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
312 HeapFree(GetProcessHeap(), 0, new_node);
313 }
314 }
315 return node;
316}
317
318/**************************************************************************
319 * NLS_IsUnicodeOnlyLcid <internal>
320 *
321 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
322 */
d9339f83 323BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
31f1a801 324{
2b01f6e0
AJ
325 lcid = ConvertDefaultLocale(lcid);
326
31f1a801
JG
327 switch (PRIMARYLANGID(lcid))
328 {
329 case LANG_ARMENIAN:
330 case LANG_DIVEHI:
331 case LANG_GEORGIAN:
332 case LANG_GUJARATI:
333 case LANG_HINDI:
334 case LANG_KANNADA:
335 case LANG_KONKANI:
336 case LANG_MARATHI:
337 case LANG_PUNJABI:
338 case LANG_SANSKRIT:
8c38b880 339 TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
31f1a801
JG
340 return TRUE;
341 default:
342 return FALSE;
343 }
344}
345
346/*
347 * Formatting of dates, times, numbers and currencies.
348 */
349
350#define IsLiteralMarker(p) (p == '\'')
351#define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
352#define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
353
354/* Only the following flags can be given if a date/time format is specified */
f70fb8ec 355#define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
31f1a801
JG
356#define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
357 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
f70fb8ec 358 TIME_NOTIMEMARKER)
90b68fc7
AJ
359
360/******************************************************************************
31f1a801 361 * NLS_GetDateTimeFormatW <internal>
90b68fc7 362 *
31f1a801 363 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
90b68fc7 364 *
31f1a801
JG
365 * FIXME
366 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
367 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
368 */
369static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
370 const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
371 LPWSTR lpStr, INT cchOut)
372{
373 const NLS_FORMAT_NODE *node;
374 SYSTEMTIME st;
375 INT cchWritten = 0;
376 INT lastFormatPos = 0;
377 BOOL bSkipping = FALSE; /* Skipping text around marker? */
8ea4102a 378 BOOL d_dd_formatted = FALSE; /* previous formatted part was for d or dd */
31f1a801
JG
379
380 /* Verify our arguments */
381 if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
1a260918 382 goto invalid_parameter;
31f1a801
JG
383
384 if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
385 {
386 if (lpFormat &&
387 ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
388 (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
389 {
1a260918 390 goto invalid_flags;
31f1a801
JG
391 }
392
393 if (dwFlags & DATE_DATEVARSONLY)
394 {
395 if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
1a260918 396 goto invalid_flags;
31f1a801
JG
397 else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
398 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
399
400 switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
401 {
402 case 0:
403 break;
404 case DATE_SHORTDATE:
405 case DATE_LONGDATE:
406 case DATE_YEARMONTH:
407 if (lpFormat)
1a260918 408 goto invalid_flags;
31f1a801
JG
409 break;
410 default:
1a260918 411 goto invalid_flags;
31f1a801
JG
412 }
413 }
414 }
415
416 if (!lpFormat)
417 {
418 /* Use the appropriate default format */
419 if (dwFlags & DATE_DATEVARSONLY)
420 {
421 if (dwFlags & DATE_YEARMONTH)
422 lpFormat = GetYearMonth(node);
423 else if (dwFlags & DATE_LONGDATE)
424 lpFormat = GetLongDate(node);
425 else
426 lpFormat = GetShortDate(node);
427 }
428 else
429 lpFormat = GetTime(node);
430 }
431
432 if (!lpTime)
433 {
53ca036a 434 GetLocalTime(&st); /* Default to current time */
31f1a801
JG
435 lpTime = &st;
436 }
437 else
438 {
439 if (dwFlags & DATE_DATEVARSONLY)
440 {
441 FILETIME ftTmp;
442
443 /* Verify the date and correct the D.O.W. if needed */
444 memset(&st, 0, sizeof(st));
445 st.wYear = lpTime->wYear;
446 st.wMonth = lpTime->wMonth;
447 st.wDay = lpTime->wDay;
448
449 if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
1a260918 450 goto invalid_parameter;
31f1a801
JG
451
452 FileTimeToSystemTime(&ftTmp, &st);
453 lpTime = &st;
454 }
455
456 if (dwFlags & TIME_TIMEVARSONLY)
457 {
458 /* Verify the time */
459 if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
1a260918 460 goto invalid_parameter;
31f1a801
JG
461 }
462 }
463
464 /* Format the output */
465 while (*lpFormat)
466 {
467 if (IsLiteralMarker(*lpFormat))
468 {
469 /* Start of a literal string */
470 lpFormat++;
471
472 /* Loop until the end of the literal marker or end of the string */
473 while (*lpFormat)
474 {
475 if (IsLiteralMarker(*lpFormat))
476 {
477 lpFormat++;
478 if (!IsLiteralMarker(*lpFormat))
479 break; /* Terminating literal marker */
480 }
481
482 if (!cchOut)
483 cchWritten++; /* Count size only */
484 else if (cchWritten >= cchOut)
1a260918 485 goto overrun;
31f1a801
JG
486 else if (!bSkipping)
487 {
488 lpStr[cchWritten] = *lpFormat;
489 cchWritten++;
490 }
491 lpFormat++;
492 }
493 }
494 else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
495 (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
496 {
31f1a801
JG
497 WCHAR buff[32], fmtChar;
498 LPCWSTR szAdd = NULL;
499 DWORD dwVal = 0;
500 int count = 0, dwLen;
501
502 bSkipping = FALSE;
503
504 fmtChar = *lpFormat;
505 while (*lpFormat == fmtChar)
506 {
507 count++;
508 lpFormat++;
509 }
510 buff[0] = '\0';
511
8ea4102a 512 if (fmtChar != 'M') d_dd_formatted = FALSE;
31f1a801
JG
513 switch(fmtChar)
514 {
515 case 'd':
516 if (count >= 4)
517 szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
518 else if (count == 3)
519 szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
520 else
521 {
522 dwVal = lpTime->wDay;
523 szAdd = buff;
8ea4102a 524 d_dd_formatted = TRUE;
31f1a801
JG
525 }
526 break;
527
528 case 'M':
529 if (count >= 4)
8ea4102a
NS
530 {
531 LPCWSTR genitive = GetGenitiveMonth(node, lpTime->wMonth - 1);
532 if (genitive)
533 {
534 if (d_dd_formatted)
535 {
536 szAdd = genitive;
537 break;
538 }
539 else
540 {
541 LPCWSTR format = lpFormat;
542 /* Look forward now, if next format pattern is for day genitive
543 name should be used */
544 while (*format)
545 {
546 /* Skip parts within markers */
547 if (IsLiteralMarker(*format))
548 {
549 ++format;
550 while (*format)
551 {
552 if (IsLiteralMarker(*format))
553 {
554 ++format;
555 if (!IsLiteralMarker(*format)) break;
556 }
557 }
558 }
559 if (*format != ' ') break;
560 ++format;
561 }
562 /* Only numeric day form matters */
563 if (*format && *format == 'd')
564 {
565 INT dcount = 1;
566 while (*++format == 'd') dcount++;
567 if (dcount < 3)
568 {
569 szAdd = genitive;
570 break;
571 }
572 }
573 }
574 }
31f1a801 575 szAdd = GetLongMonth(node, lpTime->wMonth - 1);
8ea4102a 576 }
31f1a801
JG
577 else if (count == 3)
578 szAdd = GetShortMonth(node, lpTime->wMonth - 1);
579 else
580 {
581 dwVal = lpTime->wMonth;
582 szAdd = buff;
583 }
584 break;
585
586 case 'y':
587 if (count >= 4)
588 {
589 count = 4;
590 dwVal = lpTime->wYear;
591 }
592 else
593 {
594 count = count > 2 ? 2 : count;
595 dwVal = lpTime->wYear % 100;
596 }
597 szAdd = buff;
598 break;
599
600 case 'g':
601 if (count == 2)
602 {
603 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
604 * When it is fixed, this string should be cached in 'node'.
605 */
606 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
607 buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
608 }
609 else
610 {
611 buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
612 }
613 szAdd = buff;
614 break;
615
616 case 'h':
617 if (!(dwFlags & TIME_FORCE24HOURFORMAT))
618 {
619 count = count > 2 ? 2 : count;
620 dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
621 szAdd = buff;
622 break;
623 }
624 /* .. fall through if we are forced to output in 24 hour format */
625
626 case 'H':
627 count = count > 2 ? 2 : count;
628 dwVal = lpTime->wHour;
629 szAdd = buff;
630 break;
631
632 case 'm':
633 if (dwFlags & TIME_NOMINUTESORSECONDS)
634 {
635 cchWritten = lastFormatPos; /* Skip */
636 bSkipping = TRUE;
637 }
638 else
639 {
640 count = count > 2 ? 2 : count;
641 dwVal = lpTime->wMinute;
642 szAdd = buff;
643 }
644 break;
645
646 case 's':
647 if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
648 {
649 cchWritten = lastFormatPos; /* Skip */
650 bSkipping = TRUE;
651 }
652 else
653 {
654 count = count > 2 ? 2 : count;
655 dwVal = lpTime->wSecond;
656 szAdd = buff;
657 }
658 break;
659
660 case 't':
661 if (dwFlags & TIME_NOTIMEMARKER)
662 {
663 cchWritten = lastFormatPos; /* Skip */
664 bSkipping = TRUE;
665 }
666 else
667 {
668 if (count == 1)
669 szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
670 else
671 szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
672 }
673 break;
674 }
675
676 if (szAdd == buff && buff[0] == '\0')
677 {
8e4f283e 678 static const WCHAR fmtW[] = {'%','.','*','d',0};
31f1a801 679 /* We have a numeric value to add */
8e4f283e 680 snprintfW(buff, sizeof(buff)/sizeof(WCHAR), fmtW, count, dwVal);
31f1a801
JG
681 }
682
683 dwLen = szAdd ? strlenW(szAdd) : 0;
684
685 if (cchOut && dwLen)
686 {
687 if (cchWritten + dwLen < cchOut)
688 memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
689 else
690 {
691 memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
1a260918 692 goto overrun;
31f1a801
JG
693 }
694 }
695 cchWritten += dwLen;
696 lastFormatPos = cchWritten; /* Save position of last output format text */
697 }
698 else
699 {
700 /* Literal character */
701 if (!cchOut)
702 cchWritten++; /* Count size only */
703 else if (cchWritten >= cchOut)
1a260918 704 goto overrun;
31f1a801
JG
705 else if (!bSkipping || *lpFormat == ' ')
706 {
707 lpStr[cchWritten] = *lpFormat;
708 cchWritten++;
709 }
710 lpFormat++;
711 }
712 }
713
714 /* Final string terminator and sanity check */
715 if (cchOut)
716 {
717 if (cchWritten >= cchOut)
1a260918 718 goto overrun;
31f1a801
JG
719 else
720 lpStr[cchWritten] = '\0';
721 }
722 cchWritten++; /* Include terminating NUL */
723
aab5e585 724 TRACE("returning length=%d, ouput=%s\n", cchWritten, debugstr_w(lpStr));
31f1a801
JG
725 return cchWritten;
726
1a260918 727overrun:
31f1a801
JG
728 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
729 SetLastError(ERROR_INSUFFICIENT_BUFFER);
730 return 0;
1a260918
NS
731
732invalid_parameter:
733 SetLastError(ERROR_INVALID_PARAMETER);
734 return 0;
735
736invalid_flags:
737 SetLastError(ERROR_INVALID_FLAGS);
738 return 0;
31f1a801
JG
739}
740
741/******************************************************************************
742 * NLS_GetDateTimeFormatA <internal>
743 *
744 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
745 */
d9339f83
JG
746static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
747 const SYSTEMTIME* lpTime,
748 LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
31f1a801
JG
749{
750 DWORD cp = CP_ACP;
751 WCHAR szFormat[128], szOut[128];
752 INT iRet;
753
8c38b880 754 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
31f1a801
JG
755 debugstr_a(lpFormat), lpStr, cchOut);
756
757 if (NLS_IsUnicodeOnlyLcid(lcid))
758 {
31f1a801
JG
759 SetLastError(ERROR_INVALID_PARAMETER);
760 return 0;
761 }
762
763 if (!(dwFlags & LOCALE_USE_CP_ACP))
764 {
765 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
766 if (!node)
464ece64
NS
767 {
768 SetLastError(ERROR_INVALID_PARAMETER);
769 return 0;
770 }
771
31f1a801
JG
772 cp = node->dwCodePage;
773 }
774
775 if (lpFormat)
776 MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
777
778 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
779 cchOut = sizeof(szOut)/sizeof(WCHAR);
780
781 szOut[0] = '\0';
782
783 iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
784 lpStr ? szOut : NULL, cchOut);
785
786 if (lpStr)
787 {
788 if (szOut[0])
e0cffe35 789 WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0);
31f1a801
JG
790 else if (cchOut && iRet)
791 *lpStr = '\0';
792 }
793 return iRet;
794}
795
796/******************************************************************************
797 * GetDateFormatA [KERNEL32.@]
798 *
799 * Format a date for a given locale.
800 *
801 * PARAMS
802 * lcid [I] Locale to format for
803 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
804 * lpTime [I] Date to format
805 * lpFormat [I] Format string, or NULL to use the system defaults
806 * lpDateStr [O] Destination for formatted string
807 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
808 *
809 * NOTES
eff671ef 810 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
31f1a801
JG
811 * details returned by GetLocaleInfoA() and modified by dwFlags.
812 * - lpFormat is a string of characters and formatting tokens. Any characters
813 * in the string are copied verbatim to lpDateStr, with tokens being replaced
814 * by the date values they represent.
815 * - The following tokens have special meanings in a date format string:
816 *| Token Meaning
817 *| ----- -------
818 *| d Single digit day of the month (no leading 0)
819 *| dd Double digit day of the month
820 *| ddd Short name for the day of the week
821 *| dddd Long name for the day of the week
822 *| M Single digit month of the year (no leading 0)
823 *| MM Double digit month of the year
824 *| MMM Short name for the month of the year
825 *| MMMM Long name for the month of the year
826 *| y Double digit year number (no leading 0)
827 *| yy Double digit year number
828 *| yyyy Four digit year number
829 *| gg Era string, for example 'AD'.
830 * - To output any literal character that could be misidentified as a token,
831 * enclose it in single quotes.
832 * - The Ascii version of this function fails if lcid is Unicode only.
833 *
834 * RETURNS
835 * Success: The number of character written to lpDateStr, or that would
836 * have been written, if cchOut is 0.
837 * Failure: 0. Use GetLastError() to determine the cause.
838 */
839INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
840 LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
841{
8c38b880 842 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
31f1a801
JG
843 debugstr_a(lpFormat), lpDateStr, cchOut);
844
845 return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
846 lpFormat, lpDateStr, cchOut);
847}
848
849
850/******************************************************************************
851 * GetDateFormatW [KERNEL32.@]
852 *
853 * See GetDateFormatA.
854 */
855INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
856 LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
857{
8c38b880 858 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
31f1a801
JG
859 debugstr_w(lpFormat), lpDateStr, cchOut);
860
861 return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
862 lpFormat, lpDateStr, cchOut);
863}
864
865/******************************************************************************
866 * GetTimeFormatA [KERNEL32.@]
90b68fc7 867 *
31f1a801 868 * Format a time for a given locale.
90b68fc7 869 *
31f1a801
JG
870 * PARAMS
871 * lcid [I] Locale to format for
872 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
873 * lpTime [I] Time to format
874 * lpFormat [I] Formatting overrides
875 * lpTimeStr [O] Destination for formatted string
876 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
90b68fc7 877 *
31f1a801
JG
878 * NOTES
879 * - If lpFormat is NULL, lpszValue will be formatted according to the format
880 * details returned by GetLocaleInfoA() and modified by dwFlags.
881 * - lpFormat is a string of characters and formatting tokens. Any characters
882 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
883 * by the time values they represent.
884 * - The following tokens have special meanings in a time format string:
885 *| Token Meaning
886 *| ----- -------
887 *| h Hours with no leading zero (12-hour clock)
888 *| hh Hours with full two digits (12-hour clock)
889 *| H Hours with no leading zero (24-hour clock)
890 *| HH Hours with full two digits (24-hour clock)
891 *| m Minutes with no leading zero
892 *| mm Minutes with full two digits
893 *| s Seconds with no leading zero
894 *| ss Seconds with full two digits
895 *| t Short time marker (e.g. "A" or "P")
896 *| tt Long time marker (e.g. "AM", "PM")
897 * - To output any literal character that could be misidentified as a token,
898 * enclose it in single quotes.
899 * - The Ascii version of this function fails if lcid is Unicode only.
90b68fc7 900 *
31f1a801
JG
901 * RETURNS
902 * Success: The number of character written to lpTimeStr, or that would
903 * have been written, if cchOut is 0.
904 * Failure: 0. Use GetLastError() to determine the cause.
905 */
906INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
907 LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
908{
8c38b880 909 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
31f1a801
JG
910 debugstr_a(lpFormat), lpTimeStr, cchOut);
911
912 return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
913 lpFormat, lpTimeStr, cchOut);
914}
915
916/******************************************************************************
917 * GetTimeFormatW [KERNEL32.@]
918 *
919 * See GetTimeFormatA.
920 */
921INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
922 LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
923{
8c38b880 924 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
31f1a801
JG
925 debugstr_w(lpFormat), lpTimeStr, cchOut);
926
927 return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
928 lpFormat, lpTimeStr, cchOut);
929}
930
931/**************************************************************************
932 * GetNumberFormatA (KERNEL32.@)
90b68fc7 933 *
31f1a801 934 * Format a number string for a given locale.
90b68fc7 935 *
31f1a801
JG
936 * PARAMS
937 * lcid [I] Locale to format for
938 * dwFlags [I] LOCALE_ flags from "winnls.h"
939 * lpszValue [I] String to format
940 * lpFormat [I] Formatting overrides
941 * lpNumberStr [O] Destination for formatted string
942 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
90b68fc7 943 *
31f1a801
JG
944 * NOTES
945 * - lpszValue can contain only '0' - '9', '-' and '.'.
946 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
947 * be formatted according to the format details returned by GetLocaleInfoA().
948 * - This function rounds the number string if the number of decimals exceeds the
949 * locales normal number of decimal places.
950 * - If cchOut is 0, this function does not write to lpNumberStr.
951 * - The Ascii version of this function fails if lcid is Unicode only.
952 *
953 * RETURNS
954 * Success: The number of character written to lpNumberStr, or that would
955 * have been written, if cchOut is 0.
956 * Failure: 0. Use GetLastError() to determine the cause.
90b68fc7 957 */
31f1a801
JG
958INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
959 LPCSTR lpszValue, const NUMBERFMTA *lpFormat,
960 LPSTR lpNumberStr, int cchOut)
90b68fc7 961{
31f1a801
JG
962 DWORD cp = CP_ACP;
963 WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
964 NUMBERFMTW fmt;
965 const NUMBERFMTW *pfmt = NULL;
966 INT iRet;
967
8c38b880 968 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
31f1a801
JG
969 lpFormat, lpNumberStr, cchOut);
970
971 if (NLS_IsUnicodeOnlyLcid(lcid))
972 {
31f1a801
JG
973 SetLastError(ERROR_INVALID_PARAMETER);
974 return 0;
975 }
976
977 if (!(dwFlags & LOCALE_USE_CP_ACP))
978 {
979 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
980 if (!node)
464ece64
NS
981 {
982 SetLastError(ERROR_INVALID_PARAMETER);
983 return 0;
984 }
985
31f1a801
JG
986 cp = node->dwCodePage;
987 }
988
989 if (lpFormat)
990 {
991 memcpy(&fmt, lpFormat, sizeof(fmt));
992 pfmt = &fmt;
993 if (lpFormat->lpDecimalSep)
994 {
995 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
996 fmt.lpDecimalSep = szDec;
997 }
998 if (lpFormat->lpThousandSep)
999 {
1000 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
1001 fmt.lpThousandSep = szGrp;
1002 }
1003 }
1004
1005 if (lpszValue)
1006 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
1007
1008 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1009 cchOut = sizeof(szOut)/sizeof(WCHAR);
1010
1011 szOut[0] = '\0';
1012
1013 iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1014 lpNumberStr ? szOut : NULL, cchOut);
1015
1016 if (szOut[0] && lpNumberStr)
1017 WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
1018 return iRet;
1019}
1020
1021/* Number parsing state flags */
1022#define NF_ISNEGATIVE 0x1 /* '-' found */
1023#define NF_ISREAL 0x2 /* '.' found */
1024#define NF_DIGITS 0x4 /* '0'-'9' found */
1025#define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
1026#define NF_ROUND 0x10 /* Number needs to be rounded */
1027
1028/* Formatting options for Numbers */
1029#define NLS_NEG_PARENS 0 /* "(1.1)" */
1030#define NLS_NEG_LEFT 1 /* "-1.1" */
1031#define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
1032#define NLS_NEG_RIGHT 3 /* "1.1-" */
1033#define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1034
1035/**************************************************************************
1036 * GetNumberFormatW (KERNEL32.@)
1037 *
1038 * See GetNumberFormatA.
1039 */
1040INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
1041 LPCWSTR lpszValue, const NUMBERFMTW *lpFormat,
1042 LPWSTR lpNumberStr, int cchOut)
1043{
1044 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1045 WCHAR szNegBuff[8];
1046 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
1047 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
1048 INT iRet;
1049
8c38b880 1050 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
31f1a801
JG
1051 lpFormat, lpNumberStr, cchOut);
1052
1053 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
1054 !IsValidLocale(lcid, 0) ||
1055 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
1056 {
464ece64 1057 goto error;
31f1a801
JG
1058 }
1059
1060 if (!lpFormat)
1061 {
1062 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1063
1064 if (!node)
464ece64 1065 goto error;
31f1a801
JG
1066 lpFormat = &node->fmt;
1067 lpszNegStart = lpszNeg = GetNegative(node);
1068 }
1069 else
1070 {
1071 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1072 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1073 lpszNegStart = lpszNeg = szNegBuff;
1074 }
1075 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1076
1077 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1078
1079 /* Format the number backwards into a temporary buffer */
1080
1081 szSrc = lpszValue;
1082 *szOut-- = '\0';
1083
1084 /* Check the number for validity */
1085 while (*szSrc)
1086 {
1087 if (*szSrc >= '0' && *szSrc <= '9')
1088 {
1089 dwState |= NF_DIGITS;
1090 if (dwState & NF_ISREAL)
1091 dwDecimals++;
1092 }
1093 else if (*szSrc == '-')
1094 {
1095 if (dwState)
464ece64 1096 goto error; /* '-' not first character */
31f1a801
JG
1097 dwState |= NF_ISNEGATIVE;
1098 }
1099 else if (*szSrc == '.')
1100 {
1101 if (dwState & NF_ISREAL)
464ece64 1102 goto error; /* More than one '.' */
31f1a801
JG
1103 dwState |= NF_ISREAL;
1104 }
1105 else
464ece64 1106 goto error; /* Invalid char */
31f1a801
JG
1107 szSrc++;
1108 }
1109 szSrc--; /* Point to last character */
1110
1111 if (!(dwState & NF_DIGITS))
464ece64 1112 goto error; /* No digits */
31f1a801
JG
1113
1114 /* Add any trailing negative sign */
1115 if (dwState & NF_ISNEGATIVE)
1116 {
1117 switch (lpFormat->NegativeOrder)
1118 {
1119 case NLS_NEG_PARENS:
1120 *szOut-- = ')';
1121 break;
1122 case NLS_NEG_RIGHT:
1123 case NLS_NEG_RIGHT_SPACE:
1124 while (lpszNeg >= lpszNegStart)
1125 *szOut-- = *lpszNeg--;
1126 if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
1127 *szOut-- = ' ';
1128 break;
1129 }
1130 }
1131
1132 /* Copy all digits up to the decimal point */
1133 if (!lpFormat->NumDigits)
1134 {
1135 if (dwState & NF_ISREAL)
1136 {
224063f0 1137 while (*szSrc != '.') /* Don't write any decimals or a separator */
90b68fc7 1138 {
31f1a801
JG
1139 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1140 dwState |= NF_ROUND;
1141 else
1142 dwState &= ~NF_ROUND;
1143 szSrc--;
1144 }
1145 szSrc--;
1146 }
1147 }
1148 else
1149 {
1150 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1151
1152 if (dwDecimals <= lpFormat->NumDigits)
1153 {
1154 dwDecimals = lpFormat->NumDigits - dwDecimals;
1155 while (dwDecimals--)
1156 *szOut-- = '0'; /* Pad to correct number of dp */
1157 }
1158 else
1159 {
1160 dwDecimals -= lpFormat->NumDigits;
1161 /* Skip excess decimals, and determine if we have to round the number */
1162 while (dwDecimals--)
90b68fc7 1163 {
31f1a801
JG
1164 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1165 dwState |= NF_ROUND;
1166 else
1167 dwState &= ~NF_ROUND;
1168 szSrc--;
90b68fc7 1169 }
31f1a801
JG
1170 }
1171
1172 if (dwState & NF_ISREAL)
1173 {
1174 while (*szSrc != '.')
1175 {
1176 if (dwState & NF_ROUND)
1177 {
1178 if (*szSrc == '9')
1179 *szOut-- = '0'; /* continue rounding */
1180 else
1181 {
1182 dwState &= ~NF_ROUND;
1183 *szOut-- = (*szSrc)+1;
1184 }
1185 szSrc--;
1186 }
1187 else
1188 *szOut-- = *szSrc--; /* Write existing decimals */
1189 }
1190 szSrc--; /* Skip '.' */
1191 }
1192
1193 while (lpszDec >= lpFormat->lpDecimalSep)
224063f0 1194 *szOut-- = *lpszDec--; /* Write decimal separator */
31f1a801
JG
1195 }
1196
1197 dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1198
1199 /* Write the remaining whole number digits, including grouping chars */
1200 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1201 {
1202 if (dwState & NF_ROUND)
1203 {
1204 if (*szSrc == '9')
1205 *szOut-- = '0'; /* continue rounding */
1206 else
1207 {
1208 dwState &= ~NF_ROUND;
1209 *szOut-- = (*szSrc)+1;
1210 }
1211 szSrc--;
1212 }
1213 else
1214 *szOut-- = *szSrc--;
1215
1216 dwState |= NF_DIGITS_OUT;
1217 dwCurrentGroupCount++;
8d94d192 1218 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
31f1a801
JG
1219 {
1220 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1221
1222 while (lpszGrp >= lpFormat->lpThousandSep)
1223 *szOut-- = *lpszGrp--; /* Write grouping char */
1224
1225 dwCurrentGroupCount = 0;
1226 if (lpFormat->Grouping == 32)
1227 dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1228 }
1229 }
1230 if (dwState & NF_ROUND)
1231 {
1232 *szOut-- = '1'; /* e.g. .6 > 1.0 */
1233 }
1234 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1235 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1236
1237 /* Add any leading negative sign */
1238 if (dwState & NF_ISNEGATIVE)
1239 {
1240 switch (lpFormat->NegativeOrder)
1241 {
1242 case NLS_NEG_PARENS:
1243 *szOut-- = '(';
1244 break;
1245 case NLS_NEG_LEFT_SPACE:
1246 *szOut-- = ' ';
1247 /* Fall through */
1248 case NLS_NEG_LEFT:
1249 while (lpszNeg >= lpszNegStart)
1250 *szOut-- = *lpszNeg--;
1251 break;
1252 }
1253 }
1254 szOut++;
1255
1256 iRet = strlenW(szOut) + 1;
1257 if (cchOut)
1258 {
1259 if (iRet <= cchOut)
1260 memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
1261 else
1262 {
1263 memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
1264 lpNumberStr[cchOut - 1] = '\0';
1265 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1266 iRet = 0;
1267 }
1268 }
1269 return iRet;
464ece64
NS
1270
1271error:
1272 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1273 return 0;
31f1a801
JG
1274}
1275
1276/**************************************************************************
1277 * GetCurrencyFormatA (KERNEL32.@)
1278 *
1279 * Format a currency string for a given locale.
1280 *
1281 * PARAMS
1282 * lcid [I] Locale to format for
1283 * dwFlags [I] LOCALE_ flags from "winnls.h"
1284 * lpszValue [I] String to format
1285 * lpFormat [I] Formatting overrides
1286 * lpCurrencyStr [O] Destination for formatted string
1287 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1288 *
1289 * NOTES
1290 * - lpszValue can contain only '0' - '9', '-' and '.'.
1291 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1292 * be formatted according to the format details returned by GetLocaleInfoA().
1293 * - This function rounds the currency if the number of decimals exceeds the
1294 * locales number of currency decimal places.
1295 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1296 * - The Ascii version of this function fails if lcid is Unicode only.
1297 *
1298 * RETURNS
1299 * Success: The number of character written to lpNumberStr, or that would
1300 * have been written, if cchOut is 0.
1301 * Failure: 0. Use GetLastError() to determine the cause.
1302 */
1303INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
1304 LPCSTR lpszValue, const CURRENCYFMTA *lpFormat,
1305 LPSTR lpCurrencyStr, int cchOut)
1306{
1307 DWORD cp = CP_ACP;
1308 WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
1309 CURRENCYFMTW fmt;
1310 const CURRENCYFMTW *pfmt = NULL;
1311 INT iRet;
1312
8c38b880 1313 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
31f1a801
JG
1314 lpFormat, lpCurrencyStr, cchOut);
1315
1316 if (NLS_IsUnicodeOnlyLcid(lcid))
1317 {
31f1a801
JG
1318 SetLastError(ERROR_INVALID_PARAMETER);
1319 return 0;
1320 }
1321
1322 if (!(dwFlags & LOCALE_USE_CP_ACP))
1323 {
1324 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1325 if (!node)
464ece64
NS
1326 {
1327 SetLastError(ERROR_INVALID_PARAMETER);
1328 return 0;
1329 }
1330
31f1a801
JG
1331 cp = node->dwCodePage;
1332 }
1333
1334 if (lpFormat)
1335 {
1336 memcpy(&fmt, lpFormat, sizeof(fmt));
1337 pfmt = &fmt;
1338 if (lpFormat->lpDecimalSep)
1339 {
1340 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
1341 fmt.lpDecimalSep = szDec;
1342 }
1343 if (lpFormat->lpThousandSep)
1344 {
1345 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
1346 fmt.lpThousandSep = szGrp;
1347 }
1348 if (lpFormat->lpCurrencySymbol)
1349 {
1350 MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR));
1351 fmt.lpCurrencySymbol = szCy;
1352 }
1353 }
90b68fc7 1354
31f1a801
JG
1355 if (lpszValue)
1356 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
90b68fc7 1357
31f1a801
JG
1358 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1359 cchOut = sizeof(szOut)/sizeof(WCHAR);
90b68fc7 1360
31f1a801 1361 szOut[0] = '\0';
90b68fc7 1362
31f1a801
JG
1363 iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1364 lpCurrencyStr ? szOut : NULL, cchOut);
1365
1366 if (szOut[0] && lpCurrencyStr)
1367 WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
1368 return iRet;
90b68fc7
AJ
1369}
1370
31f1a801
JG
1371/* Formatting states for Currencies. We use flags to avoid code duplication. */
1372#define CF_PARENS 0x1 /* Parentheses */
1373#define CF_MINUS_LEFT 0x2 /* '-' to the left */
1374#define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1375#define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1376#define CF_CY_LEFT 0x10 /* '$' to the left */
1377#define CF_CY_RIGHT 0x20 /* '$' to the right */
1378#define CF_CY_SPACE 0x40 /* ' ' by '$' */
90b68fc7 1379
31f1a801
JG
1380/**************************************************************************
1381 * GetCurrencyFormatW (KERNEL32.@)
90b68fc7 1382 *
31f1a801 1383 * See GetCurrencyFormatA.
90b68fc7 1384 */
31f1a801
JG
1385INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
1386 LPCWSTR lpszValue, const CURRENCYFMTW *lpFormat,
1387 LPWSTR lpCurrencyStr, int cchOut)
90b68fc7 1388{
31f1a801 1389 static const BYTE NLS_NegCyFormats[16] =
90b68fc7 1390 {
31f1a801
JG
1391 CF_PARENS|CF_CY_LEFT, /* ($1.1) */
1392 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT, /* -$1.1 */
1393 CF_MINUS_LEFT|CF_CY_LEFT, /* $-1.1 */
1394 CF_MINUS_RIGHT|CF_CY_LEFT, /* $1.1- */
1395 CF_PARENS|CF_CY_RIGHT, /* (1.1$) */
1396 CF_MINUS_LEFT|CF_CY_RIGHT, /* -1.1$ */
1397 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$ */
1398 CF_MINUS_RIGHT|CF_CY_RIGHT, /* 1.1$- */
1399 CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE, /* -1.1 $ */
1400 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE, /* -$ 1.1 */
1401 CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $- */
1402 CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1- */
1403 CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE, /* $ -1.1 */
1404 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
1405 CF_PARENS|CF_CY_LEFT|CF_CY_SPACE, /* ($ 1.1) */
1406 CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE, /* (1.1 $) */
1407 };
1408 static const BYTE NLS_PosCyFormats[4] =
90b68fc7 1409 {
31f1a801
JG
1410 CF_CY_LEFT, /* $1.1 */
1411 CF_CY_RIGHT, /* 1.1$ */
1412 CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1 */
1413 CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
1414 };
1415 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1416 WCHAR szNegBuff[8];
1417 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
1418 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
1419 INT iRet;
1420
8c38b880 1421 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
31f1a801
JG
1422 lpFormat, lpCurrencyStr, cchOut);
1423
1424 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
1425 !IsValidLocale(lcid, 0) ||
1426 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
1427 !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
1428 lpFormat->PositiveOrder > 3)))
1429 {
464ece64 1430 goto error;
90b68fc7
AJ
1431 }
1432
31f1a801 1433 if (!lpFormat)
90b68fc7 1434 {
31f1a801 1435 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
90b68fc7 1436
31f1a801 1437 if (!node)
464ece64
NS
1438 goto error;
1439
31f1a801
JG
1440 lpFormat = &node->cyfmt;
1441 lpszNegStart = lpszNeg = GetNegative(node);
1442 }
1443 else
90b68fc7 1444 {
31f1a801
JG
1445 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1446 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1447 lpszNegStart = lpszNeg = szNegBuff;
90b68fc7 1448 }
31f1a801 1449 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
90b68fc7 1450
31f1a801
JG
1451 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1452 lpszCyStart = lpFormat->lpCurrencySymbol;
1453 lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
90b68fc7 1454
31f1a801 1455 /* Format the currency backwards into a temporary buffer */
90b68fc7 1456
31f1a801
JG
1457 szSrc = lpszValue;
1458 *szOut-- = '\0';
1459
1460 /* Check the number for validity */
1461 while (*szSrc)
1462 {
1463 if (*szSrc >= '0' && *szSrc <= '9')
90b68fc7 1464 {
31f1a801
JG
1465 dwState |= NF_DIGITS;
1466 if (dwState & NF_ISREAL)
1467 dwDecimals++;
90b68fc7 1468 }
31f1a801 1469 else if (*szSrc == '-')
90b68fc7 1470 {
31f1a801 1471 if (dwState)
464ece64 1472 goto error; /* '-' not first character */
31f1a801 1473 dwState |= NF_ISNEGATIVE;
90b68fc7 1474 }
31f1a801 1475 else if (*szSrc == '.')
90b68fc7 1476 {
31f1a801 1477 if (dwState & NF_ISREAL)
464ece64 1478 goto error; /* More than one '.' */
31f1a801 1479 dwState |= NF_ISREAL;
90b68fc7 1480 }
31f1a801 1481 else
464ece64 1482 goto error; /* Invalid char */
31f1a801
JG
1483 szSrc++;
1484 }
1485 szSrc--; /* Point to last character */
90b68fc7 1486
31f1a801 1487 if (!(dwState & NF_DIGITS))
464ece64 1488 goto error; /* No digits */
31f1a801
JG
1489
1490 if (dwState & NF_ISNEGATIVE)
1491 dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
1492 else
1493 dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
1494
1495 /* Add any trailing negative or currency signs */
1496 if (dwFmt & CF_PARENS)
1497 *szOut-- = ')';
1498
1499 while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
1500 {
1501 switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
90b68fc7 1502 {
31f1a801
JG
1503 case CF_MINUS_RIGHT:
1504 case CF_MINUS_RIGHT|CF_CY_RIGHT:
1505 while (lpszNeg >= lpszNegStart)
1506 *szOut-- = *lpszNeg--;
1507 dwFmt &= ~CF_MINUS_RIGHT;
1508 break;
1509
1510 case CF_CY_RIGHT:
1511 case CF_MINUS_BEFORE|CF_CY_RIGHT:
1512 case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
1513 while (lpszCy >= lpszCyStart)
1514 *szOut-- = *lpszCy--;
1515 if (dwFmt & CF_CY_SPACE)
1516 *szOut-- = ' ';
1517 dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
1518 break;
90b68fc7 1519 }
31f1a801 1520 }
90b68fc7 1521
31f1a801
JG
1522 /* Copy all digits up to the decimal point */
1523 if (!lpFormat->NumDigits)
1524 {
1525 if (dwState & NF_ISREAL)
90b68fc7 1526 {
224063f0 1527 while (*szSrc != '.') /* Don't write any decimals or a separator */
31f1a801
JG
1528 {
1529 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1530 dwState |= NF_ROUND;
1531 else
1532 dwState &= ~NF_ROUND;
1533 szSrc--;
1534 }
1535 szSrc--;
90b68fc7 1536 }
31f1a801
JG
1537 }
1538 else
1539 {
1540 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
90b68fc7 1541
31f1a801 1542 if (dwDecimals <= lpFormat->NumDigits)
90b68fc7 1543 {
31f1a801
JG
1544 dwDecimals = lpFormat->NumDigits - dwDecimals;
1545 while (dwDecimals--)
1546 *szOut-- = '0'; /* Pad to correct number of dp */
90b68fc7 1547 }
31f1a801 1548 else
90b68fc7 1549 {
31f1a801
JG
1550 dwDecimals -= lpFormat->NumDigits;
1551 /* Skip excess decimals, and determine if we have to round the number */
1552 while (dwDecimals--)
1553 {
1554 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1555 dwState |= NF_ROUND;
1556 else
1557 dwState &= ~NF_ROUND;
1558 szSrc--;
1559 }
1560 }
1561
1562 if (dwState & NF_ISREAL)
90b68fc7 1563 {
31f1a801
JG
1564 while (*szSrc != '.')
1565 {
1566 if (dwState & NF_ROUND)
90b68fc7 1567 {
31f1a801
JG
1568 if (*szSrc == '9')
1569 *szOut-- = '0'; /* continue rounding */
1570 else
1571 {
1572 dwState &= ~NF_ROUND;
1573 *szOut-- = (*szSrc)+1;
1574 }
1575 szSrc--;
90b68fc7 1576 }
31f1a801
JG
1577 else
1578 *szOut-- = *szSrc--; /* Write existing decimals */
1579 }
1580 szSrc--; /* Skip '.' */
90b68fc7 1581 }
31f1a801 1582 while (lpszDec >= lpFormat->lpDecimalSep)
224063f0 1583 *szOut-- = *lpszDec--; /* Write decimal separator */
31f1a801 1584 }
90b68fc7 1585
31f1a801
JG
1586 dwGroupCount = lpFormat->Grouping;
1587
1588 /* Write the remaining whole number digits, including grouping chars */
1589 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1590 {
1591 if (dwState & NF_ROUND)
90b68fc7 1592 {
31f1a801
JG
1593 if (*szSrc == '9')
1594 *szOut-- = '0'; /* continue rounding */
1595 else
1596 {
1597 dwState &= ~NF_ROUND;
1598 *szOut-- = (*szSrc)+1;
1599 }
1600 szSrc--;
1601 }
1602 else
1603 *szOut-- = *szSrc--;
1604
1605 dwState |= NF_DIGITS_OUT;
1606 dwCurrentGroupCount++;
3dc8b28d 1607 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
90b68fc7 1608 {
31f1a801
JG
1609 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1610
1611 while (lpszGrp >= lpFormat->lpThousandSep)
1612 *szOut-- = *lpszGrp--; /* Write grouping char */
1613
1614 dwCurrentGroupCount = 0;
90b68fc7 1615 }
31f1a801
JG
1616 }
1617 if (dwState & NF_ROUND)
1618 *szOut-- = '1'; /* e.g. .6 > 1.0 */
1619 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1620 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
90b68fc7 1621
31f1a801
JG
1622 /* Add any leading negative or currency sign */
1623 while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
1624 {
1625 switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
1626 {
1627 case CF_MINUS_LEFT:
1628 case CF_MINUS_LEFT|CF_CY_LEFT:
1629 while (lpszNeg >= lpszNegStart)
1630 *szOut-- = *lpszNeg--;
1631 dwFmt &= ~CF_MINUS_LEFT;
1632 break;
1633
1634 case CF_CY_LEFT:
1635 case CF_CY_LEFT|CF_MINUS_BEFORE:
1636 case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
1637 if (dwFmt & CF_CY_SPACE)
1638 *szOut-- = ' ';
1639 while (lpszCy >= lpszCyStart)
1640 *szOut-- = *lpszCy--;
1641 dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
1642 break;
1643 }
1644 }
1645 if (dwFmt & CF_PARENS)
1646 *szOut-- = '(';
1647 szOut++;
90b68fc7 1648
31f1a801
JG
1649 iRet = strlenW(szOut) + 1;
1650 if (cchOut)
1651 {
1652 if (iRet <= cchOut)
1653 memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
1654 else
1655 {
1656 memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
1657 lpCurrencyStr[cchOut - 1] = '\0';
1658 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1659 iRet = 0;
1660 }
1661 }
1662 return iRet;
464ece64
NS
1663
1664error:
1665 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1666 return 0;
90b68fc7
AJ
1667}
1668
31f1a801
JG
1669/* FIXME: Everything below here needs to move somewhere else along with the
1670 * other EnumXXX functions, when a method for storing resources for
1671 * alternate calendars is determined.
1672 */
90b68fc7 1673
f4a5ba1f
HL
1674/**************************************************************************
1675 * EnumDateFormatsExA (KERNEL32.@)
5da1c0ad
DT
1676 *
1677 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1678 * LOCALE_NOUSEROVERRIDE here as well?
f4a5ba1f 1679 */
5da1c0ad 1680BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags)
f4a5ba1f 1681{
5da1c0ad
DT
1682 CALID cal_id;
1683 char buf[256];
1684
1685 if (!proc)
1686 {
1687 SetLastError(ERROR_INVALID_PARAMETER);
1688 return FALSE;
1689 }
1690
1691 if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
1692 return FALSE;
1693
1694 switch (flags & ~LOCALE_USE_CP_ACP)
1695 {
1696 case 0:
1697 case DATE_SHORTDATE:
1698 if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1699 proc(buf, cal_id);
1700 break;
1701
1702 case DATE_LONGDATE:
1703 if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1704 proc(buf, cal_id);
1705 break;
1706
1707 case DATE_YEARMONTH:
1708 if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1709 proc(buf, cal_id);
1710 break;
1711
1712 default:
8c38b880 1713 FIXME("Unknown date format (%d)\n", flags);
5da1c0ad
DT
1714 SetLastError(ERROR_INVALID_PARAMETER);
1715 return FALSE;
1716 }
1717 return TRUE;
f4a5ba1f
HL
1718}
1719
1720/**************************************************************************
1721 * EnumDateFormatsExW (KERNEL32.@)
1722 */
5da1c0ad 1723BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags)
f4a5ba1f 1724{
5da1c0ad
DT
1725 CALID cal_id;
1726 WCHAR buf[256];
1727
1728 if (!proc)
1729 {
1730 SetLastError(ERROR_INVALID_PARAMETER);
1731 return FALSE;
1732 }
1733
1734 if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR)))
1735 return FALSE;
1736
1737 switch (flags & ~LOCALE_USE_CP_ACP)
1738 {
1739 case 0:
1740 case DATE_SHORTDATE:
1741 if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1742 proc(buf, cal_id);
1743 break;
1744
1745 case DATE_LONGDATE:
1746 if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1747 proc(buf, cal_id);
1748 break;
1749
1750 case DATE_YEARMONTH:
1751 if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1752 proc(buf, cal_id);
1753 break;
1754
1755 default:
8c38b880 1756 FIXME("Unknown date format (%d)\n", flags);
5da1c0ad
DT
1757 SetLastError(ERROR_INVALID_PARAMETER);
1758 return FALSE;
1759 }
1760 return TRUE;
f4a5ba1f
HL
1761}
1762
90b68fc7
AJ
1763/**************************************************************************
1764 * EnumDateFormatsA (KERNEL32.@)
8873d90f
DT
1765 *
1766 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1767 * LOCALE_NOUSEROVERRIDE here as well?
90b68fc7 1768 */
8873d90f 1769BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
90b68fc7 1770{
8873d90f 1771 char buf[256];
90b68fc7 1772
8873d90f 1773 if (!proc)
90b68fc7 1774 {
8873d90f
DT
1775 SetLastError(ERROR_INVALID_PARAMETER);
1776 return FALSE;
1777 }
90b68fc7 1778
8873d90f 1779 switch (flags & ~LOCALE_USE_CP_ACP)
90b68fc7 1780 {
8873d90f
DT
1781 case 0:
1782 case DATE_SHORTDATE:
1783 if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1784 proc(buf);
1785 break;
90b68fc7 1786
8873d90f
DT
1787 case DATE_LONGDATE:
1788 if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1789 proc(buf);
1790 break;
90b68fc7 1791
8873d90f
DT
1792 case DATE_YEARMONTH:
1793 if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1794 proc(buf);
1795 break;
90b68fc7 1796
8873d90f 1797 default:
8c38b880 1798 FIXME("Unknown date format (%d)\n", flags);
8873d90f
DT
1799 SetLastError(ERROR_INVALID_PARAMETER);
1800 return FALSE;
90b68fc7 1801 }
8873d90f
DT
1802 return TRUE;
1803}
90b68fc7 1804
8873d90f
DT
1805/**************************************************************************
1806 * EnumDateFormatsW (KERNEL32.@)
1807 */
1808BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
1809{
1810 WCHAR buf[256];
90b68fc7 1811
8873d90f 1812 if (!proc)
90b68fc7 1813 {
8873d90f
DT
1814 SetLastError(ERROR_INVALID_PARAMETER);
1815 return FALSE;
90b68fc7 1816 }
90b68fc7 1817
8873d90f 1818 switch (flags & ~LOCALE_USE_CP_ACP)
90b68fc7 1819 {
8873d90f
DT
1820 case 0:
1821 case DATE_SHORTDATE:
1822 if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1823 proc(buf);
1824 break;
90b68fc7 1825
8873d90f
DT
1826 case DATE_LONGDATE:
1827 if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256))
1828 proc(buf);
1829 break;
90b68fc7 1830
8873d90f
DT
1831 case DATE_YEARMONTH:
1832 if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256))
1833 proc(buf);
1834 break;
90b68fc7 1835
8873d90f 1836 default:
8c38b880 1837 FIXME("Unknown date format (%d)\n", flags);
8873d90f
DT
1838 SetLastError(ERROR_INVALID_PARAMETER);
1839 return FALSE;
90b68fc7 1840 }
8873d90f 1841 return TRUE;
90b68fc7
AJ
1842}
1843
1844/**************************************************************************
1845 * EnumTimeFormatsA (KERNEL32.@)
6f6d58b8
DT
1846 *
1847 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1848 * LOCALE_NOUSEROVERRIDE here as well?
90b68fc7 1849 */
6f6d58b8 1850BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags)
90b68fc7 1851{
6f6d58b8
DT
1852 char buf[256];
1853
1854 if (!proc)
90b68fc7 1855 {
6f6d58b8
DT
1856 SetLastError(ERROR_INVALID_PARAMETER);
1857 return FALSE;
90b68fc7 1858 }
6f6d58b8
DT
1859
1860 switch (flags & ~LOCALE_USE_CP_ACP)
90b68fc7 1861 {
6f6d58b8
DT
1862 case 0:
1863 if (GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
1864 proc(buf);
1865 break;
90b68fc7 1866
6f6d58b8 1867 default:
8c38b880 1868 FIXME("Unknown time format (%d)\n", flags);
6f6d58b8
DT
1869 SetLastError(ERROR_INVALID_PARAMETER);
1870 return FALSE;
1871 }
90b68fc7 1872 return TRUE;
90b68fc7
AJ
1873}
1874
1875/**************************************************************************
1876 * EnumTimeFormatsW (KERNEL32.@)
1877 */
6f6d58b8 1878BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags)
90b68fc7 1879{
6f6d58b8
DT
1880 WCHAR buf[256];
1881
1882 if (!proc)
1883 {
1884 SetLastError(ERROR_INVALID_PARAMETER);
1885 return FALSE;
1886 }
1887
1888 switch (flags & ~LOCALE_USE_CP_ACP)
1889 {
1890 case 0:
1891 if (GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256))
1892 proc(buf);
1893 break;
1894
1895 default:
8c38b880 1896 FIXME("Unknown time format (%d)\n", flags);
6f6d58b8
DT
1897 SetLastError(ERROR_INVALID_PARAMETER);
1898 return FALSE;
1899 }
1900 return TRUE;
90b68fc7
AJ
1901}
1902
c7471d11
KB
1903/******************************************************************************
1904 * NLS_EnumCalendarInfoAW <internal>
1905 * Enumerates calendar information for a specified locale.
1906 *
1907 * PARAMS
1908 * calinfoproc [I] Pointer to the callback
1909 * locale [I] The locale for which to retrieve calendar information.
1910 * This parameter can be a locale identifier created by the
1911 * MAKELCID macro, or one of the following values:
1912 * LOCALE_SYSTEM_DEFAULT
1913 * Use the default system locale.
1914 * LOCALE_USER_DEFAULT
1915 * Use the default user locale.
1916 * calendar [I] The calendar for which information is requested, or
1917 * ENUM_ALL_CALENDARS.
1918 * caltype [I] The type of calendar information to be returned. Note
1919 * that only one CALTYPE value can be specified per call
1920 * of this function, except where noted.
1921 * unicode [I] Specifies if the callback expects a unicode string.
1922 * ex [I] Specifies if the callback needs the calendar identifier.
1923 *
1924 * RETURNS
1925 * Success: TRUE.
1926 * Failure: FALSE. Use GetLastError() to determine the cause.
1927 *
1928 * NOTES
1929 * When the ANSI version of this function is used with a Unicode-only LCID,
1930 * the call can succeed because the system uses the system code page.
1931 * However, characters that are undefined in the system code page appear
1932 * in the string as a question mark (?).
1933 *
1934 * TODO
1935 * The above note should be respected by GetCalendarInfoA.
1936 */
0f36d124 1937static BOOL NLS_EnumCalendarInfoAW(void *calinfoproc, LCID locale,
c7471d11
KB
1938 CALID calendar, CALTYPE caltype, BOOL unicode, BOOL ex )
1939{
1940 WCHAR *buf, *opt = NULL, *iter = NULL;
1941 BOOL ret = FALSE;
1942 int bufSz = 200; /* the size of the buffer */
1943
1944 if (calinfoproc == NULL)
1945 {
1946 SetLastError(ERROR_INVALID_PARAMETER);
1947 return FALSE;
1948 }
1949
1950 buf = HeapAlloc(GetProcessHeap(), 0, bufSz);
1951 if (buf == NULL)
1952 {
1953 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1954 return FALSE;
1955 }
1956
1957 if (calendar == ENUM_ALL_CALENDARS)
1958 {
1959 int optSz = GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, NULL, 0);
1960 if (optSz > 1)
1961 {
1962 opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR));
1963 if (opt == NULL)
1964 {
1965 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
464ece64 1966 goto cleanup;
c7471d11
KB
1967 }
1968 if (GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, opt, optSz))
1969 iter = opt;
1970 }
1971 calendar = NLS_GetLocaleNumber(locale, LOCALE_ICALENDARTYPE);
1972 }
1973
1974 while (TRUE) /* loop through calendars */
1975 {
1976 do /* loop until there's no error */
1977 {
1978 if (unicode)
1979 ret = GetCalendarInfoW(locale, calendar, caltype, buf, bufSz / sizeof(WCHAR), NULL);
1980 else ret = GetCalendarInfoA(locale, calendar, caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL);
1981
1982 if (!ret)
1983 {
1984 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1985 { /* so resize it */
1986 int newSz;
1987 if (unicode)
1988 newSz = GetCalendarInfoW(locale, calendar, caltype, NULL, 0, NULL) * sizeof(WCHAR);
1989 else newSz = GetCalendarInfoA(locale, calendar, caltype, NULL, 0, NULL) * sizeof(CHAR);
1990 if (bufSz >= newSz)
1991 {
1992 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz);
464ece64 1993 goto cleanup;
c7471d11
KB
1994 }
1995 bufSz = newSz;
1996 WARN("Buffer too small; resizing to %d bytes.\n", bufSz);
1997 buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz);
1998 if (buf == NULL)
464ece64
NS
1999 goto cleanup;
2000 } else goto cleanup;
c7471d11
KB
2001 }
2002 } while (!ret);
2003
2004 /* Here we are. We pass the buffer to the correct version of
2005 * the callback. Because it's not the same number of params,
2006 * we must check for Ex, but we don't care about Unicode
2007 * because the buffer is already in the correct format.
2008 */
2009 if (ex) {
2010 ret = ((CALINFO_ENUMPROCEXW)calinfoproc)(buf, calendar);
2011 } else
2012 ret = ((CALINFO_ENUMPROCW)calinfoproc)(buf);
2013
2014 if (!ret) { /* the callback told to stop */
2015 ret = TRUE;
2016 break;
2017 }
2018
2019 if ((iter == NULL) || (*iter == 0)) /* no more calendars */
2020 break;
2021
2022 calendar = 0;
2023 while ((*iter >= '0') && (*iter <= '9'))
2024 calendar = calendar * 10 + *iter++ - '0';
2025
2026 if (*iter++ != 0)
2027 {
2028 SetLastError(ERROR_BADDB);
2029 ret = FALSE;
2030 break;
2031 }
2032 }
2033
464ece64 2034cleanup:
5ad7d858
MS
2035 HeapFree(GetProcessHeap(), 0, opt);
2036 HeapFree(GetProcessHeap(), 0, buf);
c7471d11
KB
2037 return ret;
2038}
2039
90b68fc7
AJ
2040/******************************************************************************
2041 * EnumCalendarInfoA [KERNEL32.@]
c7471d11
KB
2042 *
2043 * See EnumCalendarInfoAW.
90b68fc7
AJ
2044 */
2045BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
2046 CALID calendar,CALTYPE caltype )
2047{
8c38b880 2048 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
c7471d11
KB
2049 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, FALSE);
2050}
2051
2052/******************************************************************************
2053 * EnumCalendarInfoW [KERNEL32.@]
2054 *
2055 * See EnumCalendarInfoAW.
2056 */
2057BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale,
2058 CALID calendar,CALTYPE caltype )
2059{
8c38b880 2060 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
c7471d11
KB
2061 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, FALSE);
2062}
2063
2064/******************************************************************************
2065 * EnumCalendarInfoExA [KERNEL32.@]
2066 *
2067 * See EnumCalendarInfoAW.
2068 */
2069BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale,
2070 CALID calendar,CALTYPE caltype )
2071{
8c38b880 2072 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
c7471d11
KB
2073 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, TRUE);
2074}
2075
2076/******************************************************************************
2077 * EnumCalendarInfoExW [KERNEL32.@]
2078 *
2079 * See EnumCalendarInfoAW.
2080 */
2081BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale,
2082 CALID calendar,CALTYPE caltype )
2083{
8c38b880 2084 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype);
c7471d11 2085 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, TRUE);
90b68fc7 2086}