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