Use aliases for calls to ordinals.
[wine/dcerpc.git] / dlls / kernel / lcformat.c
blob4e89245150aebeb03919505cf346c60aa9f69228
1 /*
2 * Locale-dependent format handling
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
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36 #include "winreg.h"
37 #include "winternl.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(nls);
41 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
42 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
44 /* Since calculating the formatting data for each locale is time-consuming,
45 * we get the format data for each locale only once and cache it in memory.
46 * We cache both the system default and user overridden data, after converting
47 * them into the formats that the functions here expect. Since these functions
48 * will typically be called with only a small number of the total locales
49 * installed, the memory overhead is minimal while the speedup is significant.
51 * Our cache takes the form of a singly linked list, whose node is below:
53 #define NLS_NUM_CACHED_STRINGS 45
55 typedef struct _NLS_FORMAT_NODE
57 LCID lcid; /* Locale Id */
58 DWORD dwFlags; /* 0 or LOCALE_NOUSEROVERRIDE */
59 DWORD dwCodePage; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
60 NUMBERFMTW fmt; /* Default format for numbers */
61 CURRENCYFMTW cyfmt; /* Default format for currencies */
62 LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
63 WCHAR szShortAM[2]; /* Short 'AM' marker */
64 WCHAR szShortPM[2]; /* Short 'PM' marker */
65 struct _NLS_FORMAT_NODE *next;
66 } NLS_FORMAT_NODE;
68 /* Macros to get particular data strings from a format node */
69 #define GetNegative(fmt) fmt->lppszStrings[0]
70 #define GetLongDate(fmt) fmt->lppszStrings[1]
71 #define GetShortDate(fmt) fmt->lppszStrings[2]
72 #define GetTime(fmt) fmt->lppszStrings[3]
73 #define GetAM(fmt) fmt->lppszStrings[42]
74 #define GetPM(fmt) fmt->lppszStrings[43]
75 #define GetYearMonth(fmt) fmt->lppszStrings[44]
77 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
78 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
79 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
80 #define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
82 /* Write access to the cache is protected by this critical section */
83 static CRITICAL_SECTION NLS_FormatsCS;
84 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
86 0, 0, &NLS_FormatsCS,
87 { &NLS_FormatsCS_debug.ProcessLocksList,
88 &NLS_FormatsCS_debug.ProcessLocksList },
89 0, 0, { 0, (DWORD)(__FILE__ ": NLS_Formats") }
91 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
93 /**************************************************************************
94 * NLS_GetLocaleNumber <internal>
96 * Get a numeric locale format value.
98 static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
100 WCHAR szBuff[80];
101 DWORD dwVal = 0;
103 szBuff[0] = '\0';
104 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
106 if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
107 dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
108 else
110 const WCHAR* iter = szBuff;
111 dwVal = 0;
112 while(*iter >= '0' && *iter <= '9')
113 dwVal = dwVal * 10 + (*iter++ - '0');
115 return dwVal;
118 /**************************************************************************
119 * NLS_GetLocaleString <internal>
121 * Get a string locale format value.
123 static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
125 WCHAR szBuff[80], *str;
126 DWORD dwLen;
128 szBuff[0] = '\0';
129 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
130 dwLen = strlenW(szBuff) + 1;
131 str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
132 if (str)
133 memcpy(str, szBuff, dwLen * sizeof(WCHAR));
134 return str;
137 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
138 TRACE( #type ": %ld (%08lx)\n", (DWORD)num, (DWORD)num)
140 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
141 TRACE( #type ": '%s'\n", debugstr_w(str))
143 /**************************************************************************
144 * NLS_GetFormats <internal>
146 * Calculate (and cache) the number formats for a locale.
148 static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
150 /* GetLocaleInfo() identifiers for cached formatting strings */
151 static const USHORT NLS_LocaleIndices[] = {
152 LOCALE_SNEGATIVESIGN,
153 LOCALE_SLONGDATE, LOCALE_SSHORTDATE,
154 LOCALE_STIMEFORMAT,
155 LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
156 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
157 LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
158 LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
159 LOCALE_SABBREVDAYNAME7,
160 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
161 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
162 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
163 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
164 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
165 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
166 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
167 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
168 LOCALE_S1159, LOCALE_S2359,
169 LOCALE_SYEARMONTH
171 static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
172 NLS_FORMAT_NODE *node = NLS_CachedFormats;
174 dwFlags &= LOCALE_NOUSEROVERRIDE;
176 TRACE("(0x%04lx,0x%08lx)\n", lcid, dwFlags);
178 /* See if we have already cached the locales number format */
179 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
180 node = node->next;
182 if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
184 NLS_FORMAT_NODE *new_node;
185 DWORD i;
187 TRACE("Creating new cache entry\n");
189 if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
190 return NULL;
192 GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
194 /* Number Format */
195 new_node->lcid = lcid;
196 new_node->dwFlags = dwFlags;
197 new_node->next = NULL;
199 GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
200 GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
201 GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
203 GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
204 if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
206 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
207 new_node->fmt.Grouping);
208 new_node->fmt.Grouping = 0;
211 GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
212 GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
214 /* Currency Format */
215 new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
216 new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
218 GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
220 if (new_node->cyfmt.Grouping > 9)
222 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
223 new_node->cyfmt.Grouping);
224 new_node->cyfmt.Grouping = 0;
227 GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
228 if (new_node->cyfmt.NegativeOrder > 15)
230 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
231 new_node->cyfmt.NegativeOrder);
232 new_node->cyfmt.NegativeOrder = 0;
234 GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
235 if (new_node->cyfmt.PositiveOrder > 3)
237 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
238 new_node->cyfmt.PositiveOrder);
239 new_node->cyfmt.PositiveOrder = 0;
241 GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
242 GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
243 GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
245 /* Date/Time Format info, negative character, etc */
246 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
248 GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
250 new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
251 new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
253 /* Now add the computed format to the cache */
254 RtlEnterCriticalSection(&NLS_FormatsCS);
256 /* Search again: We may have raced to add the node */
257 node = NLS_CachedFormats;
258 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
259 node = node->next;
261 if (!node)
263 node = NLS_CachedFormats = new_node; /* Empty list */
264 new_node = NULL;
266 else if (node->lcid != lcid || node->dwFlags != dwFlags)
268 node->next = new_node; /* Not in the list, add to end */
269 node = new_node;
270 new_node = NULL;
273 RtlLeaveCriticalSection(&NLS_FormatsCS);
275 if (new_node)
277 /* We raced and lost: The node was already added by another thread.
278 * node points to the currently cached node, so free new_node.
280 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
281 HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
282 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
283 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
284 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
285 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
286 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
287 HeapFree(GetProcessHeap(), 0, new_node);
290 return node;
293 /**************************************************************************
294 * NLS_IsUnicodeOnlyLcid <internal>
296 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
298 BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
300 switch (PRIMARYLANGID(lcid))
302 case LANG_ARMENIAN:
303 case LANG_DIVEHI:
304 case LANG_GEORGIAN:
305 case LANG_GUJARATI:
306 case LANG_HINDI:
307 case LANG_KANNADA:
308 case LANG_KONKANI:
309 case LANG_MARATHI:
310 case LANG_PUNJABI:
311 case LANG_SANSKRIT:
312 TRACE("lcid 0x%08lx: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
313 return TRUE;
314 default:
315 return FALSE;
320 * Formatting of dates, times, numbers and currencies.
323 #define IsLiteralMarker(p) (p == '\'')
324 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
325 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
327 /* Only the following flags can be given if a date/time format is specified */
328 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY|LOCALE_NOUSEROVERRIDE)
329 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
330 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
331 TIME_NOTIMEMARKER|LOCALE_NOUSEROVERRIDE)
333 /******************************************************************************
334 * NLS_GetDateTimeFormatW <internal>
336 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
338 * FIXME
339 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
340 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
342 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
343 const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
344 LPWSTR lpStr, INT cchOut)
346 const NLS_FORMAT_NODE *node;
347 SYSTEMTIME st;
348 INT cchWritten = 0;
349 INT lastFormatPos = 0;
350 BOOL bSkipping = FALSE; /* Skipping text around marker? */
352 /* Verify our arguments */
353 if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
355 NLS_GetDateTimeFormatW_InvalidParameter:
356 SetLastError(ERROR_INVALID_PARAMETER);
357 return 0;
360 if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
362 if (lpFormat &&
363 ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
364 (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
366 NLS_GetDateTimeFormatW_InvalidFlags:
367 SetLastError(ERROR_INVALID_FLAGS);
368 return 0;
371 if (dwFlags & DATE_DATEVARSONLY)
373 if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
374 goto NLS_GetDateTimeFormatW_InvalidFlags;
375 else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
376 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
378 switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
380 case 0:
381 break;
382 case DATE_SHORTDATE:
383 case DATE_LONGDATE:
384 case DATE_YEARMONTH:
385 if (lpFormat)
386 goto NLS_GetDateTimeFormatW_InvalidFlags;
387 break;
388 default:
389 goto NLS_GetDateTimeFormatW_InvalidFlags;
394 if (!lpFormat)
396 /* Use the appropriate default format */
397 if (dwFlags & DATE_DATEVARSONLY)
399 if (dwFlags & DATE_YEARMONTH)
400 lpFormat = GetYearMonth(node);
401 else if (dwFlags & DATE_LONGDATE)
402 lpFormat = GetLongDate(node);
403 else
404 lpFormat = GetShortDate(node);
406 else
407 lpFormat = GetTime(node);
410 if (!lpTime)
412 GetLocalTime(&st); /* Default to current time */
413 lpTime = &st;
415 else
417 if (dwFlags & DATE_DATEVARSONLY)
419 FILETIME ftTmp;
421 /* Verify the date and correct the D.O.W. if needed */
422 memset(&st, 0, sizeof(st));
423 st.wYear = lpTime->wYear;
424 st.wMonth = lpTime->wMonth;
425 st.wDay = lpTime->wDay;
427 if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp))
428 goto NLS_GetDateTimeFormatW_InvalidParameter;
430 FileTimeToSystemTime(&ftTmp, &st);
431 lpTime = &st;
434 if (dwFlags & TIME_TIMEVARSONLY)
436 /* Verify the time */
437 if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59)
438 goto NLS_GetDateTimeFormatW_InvalidParameter;
442 /* Format the output */
443 while (*lpFormat)
445 if (IsLiteralMarker(*lpFormat))
447 /* Start of a literal string */
448 lpFormat++;
450 /* Loop until the end of the literal marker or end of the string */
451 while (*lpFormat)
453 if (IsLiteralMarker(*lpFormat))
455 lpFormat++;
456 if (!IsLiteralMarker(*lpFormat))
457 break; /* Terminating literal marker */
460 if (!cchOut)
461 cchWritten++; /* Count size only */
462 else if (cchWritten >= cchOut)
463 goto NLS_GetDateTimeFormatW_Overrun;
464 else if (!bSkipping)
466 lpStr[cchWritten] = *lpFormat;
467 cchWritten++;
469 lpFormat++;
472 else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) ||
473 (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat)))
475 char buffA[32];
476 WCHAR buff[32], fmtChar;
477 LPCWSTR szAdd = NULL;
478 DWORD dwVal = 0;
479 int count = 0, dwLen;
481 bSkipping = FALSE;
483 fmtChar = *lpFormat;
484 while (*lpFormat == fmtChar)
486 count++;
487 lpFormat++;
489 buff[0] = '\0';
491 switch(fmtChar)
493 case 'd':
494 if (count >= 4)
495 szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7);
496 else if (count == 3)
497 szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7);
498 else
500 dwVal = lpTime->wDay;
501 szAdd = buff;
503 break;
505 case 'M':
506 if (count >= 4)
507 szAdd = GetLongMonth(node, lpTime->wMonth - 1);
508 else if (count == 3)
509 szAdd = GetShortMonth(node, lpTime->wMonth - 1);
510 else
512 dwVal = lpTime->wMonth;
513 szAdd = buff;
515 break;
517 case 'y':
518 if (count >= 4)
520 count = 4;
521 dwVal = lpTime->wYear;
523 else
525 count = count > 2 ? 2 : count;
526 dwVal = lpTime->wYear % 100;
528 szAdd = buff;
529 break;
531 case 'g':
532 if (count == 2)
534 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
535 * When it is fixed, this string should be cached in 'node'.
537 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
538 buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0';
540 else
542 buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */
544 szAdd = buff;
545 break;
547 case 'h':
548 if (!(dwFlags & TIME_FORCE24HOURFORMAT))
550 count = count > 2 ? 2 : count;
551 dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1;
552 szAdd = buff;
553 break;
555 /* .. fall through if we are forced to output in 24 hour format */
557 case 'H':
558 count = count > 2 ? 2 : count;
559 dwVal = lpTime->wHour;
560 szAdd = buff;
561 break;
563 case 'm':
564 if (dwFlags & TIME_NOMINUTESORSECONDS)
566 cchWritten = lastFormatPos; /* Skip */
567 bSkipping = TRUE;
569 else
571 count = count > 2 ? 2 : count;
572 dwVal = lpTime->wMinute;
573 szAdd = buff;
575 break;
577 case 's':
578 if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS))
580 cchWritten = lastFormatPos; /* Skip */
581 bSkipping = TRUE;
583 else
585 count = count > 2 ? 2 : count;
586 dwVal = lpTime->wSecond;
587 szAdd = buff;
589 break;
591 case 't':
592 if (dwFlags & TIME_NOTIMEMARKER)
594 cchWritten = lastFormatPos; /* Skip */
595 bSkipping = TRUE;
597 else
599 if (count == 1)
600 szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM;
601 else
602 szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node);
604 break;
607 if (szAdd == buff && buff[0] == '\0')
609 /* We have a numeric value to add */
610 sprintf(buffA, "%.*ld", count, dwVal);
611 MultiByteToWideChar(CP_ACP, 0, buffA, -1, buff, sizeof(buff)/sizeof(WCHAR));
614 dwLen = szAdd ? strlenW(szAdd) : 0;
616 if (cchOut && dwLen)
618 if (cchWritten + dwLen < cchOut)
619 memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR));
620 else
622 memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR));
623 goto NLS_GetDateTimeFormatW_Overrun;
626 cchWritten += dwLen;
627 lastFormatPos = cchWritten; /* Save position of last output format text */
629 else
631 /* Literal character */
632 if (!cchOut)
633 cchWritten++; /* Count size only */
634 else if (cchWritten >= cchOut)
635 goto NLS_GetDateTimeFormatW_Overrun;
636 else if (!bSkipping || *lpFormat == ' ')
638 lpStr[cchWritten] = *lpFormat;
639 cchWritten++;
641 lpFormat++;
645 /* Final string terminator and sanity check */
646 if (cchOut)
648 if (cchWritten >= cchOut)
649 goto NLS_GetDateTimeFormatW_Overrun;
650 else
651 lpStr[cchWritten] = '\0';
653 cchWritten++; /* Include terminating NUL */
655 TRACE("returning length=%d, ouput='%s'\n", cchWritten, debugstr_w(lpStr));
656 return cchWritten;
658 NLS_GetDateTimeFormatW_Overrun:
659 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
660 SetLastError(ERROR_INSUFFICIENT_BUFFER);
661 return 0;
664 /******************************************************************************
665 * NLS_GetDateTimeFormatA <internal>
667 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
669 static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags,
670 const SYSTEMTIME* lpTime,
671 LPCSTR lpFormat, LPSTR lpStr, INT cchOut)
673 DWORD cp = CP_ACP;
674 WCHAR szFormat[128], szOut[128];
675 INT iRet;
677 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
678 debugstr_a(lpFormat), lpStr, cchOut);
680 if (NLS_IsUnicodeOnlyLcid(lcid))
682 GetDateTimeFormatA_InvalidParameter:
683 SetLastError(ERROR_INVALID_PARAMETER);
684 return 0;
687 if (!(dwFlags & LOCALE_USE_CP_ACP))
689 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
690 if (!node)
691 goto GetDateTimeFormatA_InvalidParameter;
692 cp = node->dwCodePage;
695 if (lpFormat)
696 MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR));
698 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
699 cchOut = sizeof(szOut)/sizeof(WCHAR);
701 szOut[0] = '\0';
703 iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL,
704 lpStr ? szOut : NULL, cchOut);
706 if (lpStr)
708 if (szOut[0])
709 WideCharToMultiByte(cp, 0, szOut, -1, lpStr, cchOut, 0, 0);
710 else if (cchOut && iRet)
711 *lpStr = '\0';
713 return iRet;
716 /******************************************************************************
717 * GetDateFormatA [KERNEL32.@]
719 * Format a date for a given locale.
721 * PARAMS
722 * lcid [I] Locale to format for
723 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
724 * lpTime [I] Date to format
725 * lpFormat [I] Format string, or NULL to use the system defaults
726 * lpDateStr [O] Destination for formatted string
727 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
729 * NOTES
730 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
731 * details returned by GetLocaleInfoA() and modified by dwFlags.
732 * - lpFormat is a string of characters and formatting tokens. Any characters
733 * in the string are copied verbatim to lpDateStr, with tokens being replaced
734 * by the date values they represent.
735 * - The following tokens have special meanings in a date format string:
736 *| Token Meaning
737 *| ----- -------
738 *| d Single digit day of the month (no leading 0)
739 *| dd Double digit day of the month
740 *| ddd Short name for the day of the week
741 *| dddd Long name for the day of the week
742 *| M Single digit month of the year (no leading 0)
743 *| MM Double digit month of the year
744 *| MMM Short name for the month of the year
745 *| MMMM Long name for the month of the year
746 *| y Double digit year number (no leading 0)
747 *| yy Double digit year number
748 *| yyyy Four digit year number
749 *| gg Era string, for example 'AD'.
750 * - To output any literal character that could be misidentified as a token,
751 * enclose it in single quotes.
752 * - The Ascii version of this function fails if lcid is Unicode only.
754 * RETURNS
755 * Success: The number of character written to lpDateStr, or that would
756 * have been written, if cchOut is 0.
757 * Failure: 0. Use GetLastError() to determine the cause.
759 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
760 LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut)
762 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
763 debugstr_a(lpFormat), lpDateStr, cchOut);
765 return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime,
766 lpFormat, lpDateStr, cchOut);
770 /******************************************************************************
771 * GetDateFormatW [KERNEL32.@]
773 * See GetDateFormatA.
775 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
776 LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut)
778 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime,
779 debugstr_w(lpFormat), lpDateStr, cchOut);
781 return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime,
782 lpFormat, lpDateStr, cchOut);
785 /******************************************************************************
786 * GetTimeFormatA [KERNEL32.@]
788 * Format a time for a given locale.
790 * PARAMS
791 * lcid [I] Locale to format for
792 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
793 * lpTime [I] Time to format
794 * lpFormat [I] Formatting overrides
795 * lpTimeStr [O] Destination for formatted string
796 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
798 * NOTES
799 * - If lpFormat is NULL, lpszValue will be formatted according to the format
800 * details returned by GetLocaleInfoA() and modified by dwFlags.
801 * - lpFormat is a string of characters and formatting tokens. Any characters
802 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
803 * by the time values they represent.
804 * - The following tokens have special meanings in a time format string:
805 *| Token Meaning
806 *| ----- -------
807 *| h Hours with no leading zero (12-hour clock)
808 *| hh Hours with full two digits (12-hour clock)
809 *| H Hours with no leading zero (24-hour clock)
810 *| HH Hours with full two digits (24-hour clock)
811 *| m Minutes with no leading zero
812 *| mm Minutes with full two digits
813 *| s Seconds with no leading zero
814 *| ss Seconds with full two digits
815 *| t Short time marker (e.g. "A" or "P")
816 *| tt Long time marker (e.g. "AM", "PM")
817 * - To output any literal character that could be misidentified as a token,
818 * enclose it in single quotes.
819 * - The Ascii version of this function fails if lcid is Unicode only.
821 * RETURNS
822 * Success: The number of character written to lpTimeStr, or that would
823 * have been written, if cchOut is 0.
824 * Failure: 0. Use GetLastError() to determine the cause.
826 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
827 LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut)
829 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
830 debugstr_a(lpFormat), lpTimeStr, cchOut);
832 return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
833 lpFormat, lpTimeStr, cchOut);
836 /******************************************************************************
837 * GetTimeFormatW [KERNEL32.@]
839 * See GetTimeFormatA.
841 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime,
842 LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut)
844 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime,
845 debugstr_w(lpFormat), lpTimeStr, cchOut);
847 return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime,
848 lpFormat, lpTimeStr, cchOut);
851 /**************************************************************************
852 * GetNumberFormatA (KERNEL32.@)
854 * Format a number string for a given locale.
856 * PARAMS
857 * lcid [I] Locale to format for
858 * dwFlags [I] LOCALE_ flags from "winnls.h"
859 * lpszValue [I] String to format
860 * lpFormat [I] Formatting overrides
861 * lpNumberStr [O] Destination for formatted string
862 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
864 * NOTES
865 * - lpszValue can contain only '0' - '9', '-' and '.'.
866 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
867 * be formatted according to the format details returned by GetLocaleInfoA().
868 * - This function rounds the number string if the number of decimals exceeds the
869 * locales normal number of decimal places.
870 * - If cchOut is 0, this function does not write to lpNumberStr.
871 * - The Ascii version of this function fails if lcid is Unicode only.
873 * RETURNS
874 * Success: The number of character written to lpNumberStr, or that would
875 * have been written, if cchOut is 0.
876 * Failure: 0. Use GetLastError() to determine the cause.
878 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags,
879 LPCSTR lpszValue, const NUMBERFMTA *lpFormat,
880 LPSTR lpNumberStr, int cchOut)
882 DWORD cp = CP_ACP;
883 WCHAR szDec[8], szGrp[8], szIn[128], szOut[128];
884 NUMBERFMTW fmt;
885 const NUMBERFMTW *pfmt = NULL;
886 INT iRet;
888 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
889 lpFormat, lpNumberStr, cchOut);
891 if (NLS_IsUnicodeOnlyLcid(lcid))
893 GetNumberFormatA_InvalidParameter:
894 SetLastError(ERROR_INVALID_PARAMETER);
895 return 0;
898 if (!(dwFlags & LOCALE_USE_CP_ACP))
900 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
901 if (!node)
902 goto GetNumberFormatA_InvalidParameter;
903 cp = node->dwCodePage;
906 if (lpFormat)
908 memcpy(&fmt, lpFormat, sizeof(fmt));
909 pfmt = &fmt;
910 if (lpFormat->lpDecimalSep)
912 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
913 fmt.lpDecimalSep = szDec;
915 if (lpFormat->lpThousandSep)
917 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
918 fmt.lpThousandSep = szGrp;
922 if (lpszValue)
923 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
925 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
926 cchOut = sizeof(szOut)/sizeof(WCHAR);
928 szOut[0] = '\0';
930 iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
931 lpNumberStr ? szOut : NULL, cchOut);
933 if (szOut[0] && lpNumberStr)
934 WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0);
935 return iRet;
938 /* Number parsing state flags */
939 #define NF_ISNEGATIVE 0x1 /* '-' found */
940 #define NF_ISREAL 0x2 /* '.' found */
941 #define NF_DIGITS 0x4 /* '0'-'9' found */
942 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
943 #define NF_ROUND 0x10 /* Number needs to be rounded */
945 /* Formatting options for Numbers */
946 #define NLS_NEG_PARENS 0 /* "(1.1)" */
947 #define NLS_NEG_LEFT 1 /* "-1.1" */
948 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
949 #define NLS_NEG_RIGHT 3 /* "1.1-" */
950 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
952 /**************************************************************************
953 * GetNumberFormatW (KERNEL32.@)
955 * See GetNumberFormatA.
957 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags,
958 LPCWSTR lpszValue, const NUMBERFMTW *lpFormat,
959 LPWSTR lpNumberStr, int cchOut)
961 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
962 WCHAR szNegBuff[8];
963 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc;
964 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0;
965 INT iRet;
967 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
968 lpFormat, lpNumberStr, cchOut);
970 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) ||
971 !IsValidLocale(lcid, 0) ||
972 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep)))
974 GetNumberFormatW_Error:
975 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
976 return 0;
979 if (!lpFormat)
981 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
983 if (!node)
984 goto GetNumberFormatW_Error;
985 lpFormat = &node->fmt;
986 lpszNegStart = lpszNeg = GetNegative(node);
988 else
990 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
991 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
992 lpszNegStart = lpszNeg = szNegBuff;
994 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
996 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
998 /* Format the number backwards into a temporary buffer */
1000 szSrc = lpszValue;
1001 *szOut-- = '\0';
1003 /* Check the number for validity */
1004 while (*szSrc)
1006 if (*szSrc >= '0' && *szSrc <= '9')
1008 dwState |= NF_DIGITS;
1009 if (dwState & NF_ISREAL)
1010 dwDecimals++;
1012 else if (*szSrc == '-')
1014 if (dwState)
1015 goto GetNumberFormatW_Error; /* '-' not first character */
1016 dwState |= NF_ISNEGATIVE;
1018 else if (*szSrc == '.')
1020 if (dwState & NF_ISREAL)
1021 goto GetNumberFormatW_Error; /* More than one '.' */
1022 dwState |= NF_ISREAL;
1024 else
1025 goto GetNumberFormatW_Error; /* Invalid char */
1026 szSrc++;
1028 szSrc--; /* Point to last character */
1030 if (!(dwState & NF_DIGITS))
1031 goto GetNumberFormatW_Error; /* No digits */
1033 /* Add any trailing negative sign */
1034 if (dwState & NF_ISNEGATIVE)
1036 switch (lpFormat->NegativeOrder)
1038 case NLS_NEG_PARENS:
1039 *szOut-- = ')';
1040 break;
1041 case NLS_NEG_RIGHT:
1042 case NLS_NEG_RIGHT_SPACE:
1043 while (lpszNeg >= lpszNegStart)
1044 *szOut-- = *lpszNeg--;
1045 if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE)
1046 *szOut-- = ' ';
1047 break;
1051 /* Copy all digits up to the decimal point */
1052 if (!lpFormat->NumDigits)
1054 if (dwState & NF_ISREAL)
1056 while (*szSrc != '.') /* Don't write any decimals or a separator */
1058 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1059 dwState |= NF_ROUND;
1060 else
1061 dwState &= ~NF_ROUND;
1062 szSrc--;
1064 szSrc--;
1067 else
1069 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1071 if (dwDecimals <= lpFormat->NumDigits)
1073 dwDecimals = lpFormat->NumDigits - dwDecimals;
1074 while (dwDecimals--)
1075 *szOut-- = '0'; /* Pad to correct number of dp */
1077 else
1079 dwDecimals -= lpFormat->NumDigits;
1080 /* Skip excess decimals, and determine if we have to round the number */
1081 while (dwDecimals--)
1083 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1084 dwState |= NF_ROUND;
1085 else
1086 dwState &= ~NF_ROUND;
1087 szSrc--;
1091 if (dwState & NF_ISREAL)
1093 while (*szSrc != '.')
1095 if (dwState & NF_ROUND)
1097 if (*szSrc == '9')
1098 *szOut-- = '0'; /* continue rounding */
1099 else
1101 dwState &= ~NF_ROUND;
1102 *szOut-- = (*szSrc)+1;
1104 szSrc--;
1106 else
1107 *szOut-- = *szSrc--; /* Write existing decimals */
1109 szSrc--; /* Skip '.' */
1112 while (lpszDec >= lpFormat->lpDecimalSep)
1113 *szOut-- = *lpszDec--; /* Write decimal separator */
1116 dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping;
1118 /* Write the remaining whole number digits, including grouping chars */
1119 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1121 if (dwState & NF_ROUND)
1123 if (*szSrc == '9')
1124 *szOut-- = '0'; /* continue rounding */
1125 else
1127 dwState &= ~NF_ROUND;
1128 *szOut-- = (*szSrc)+1;
1130 szSrc--;
1132 else
1133 *szOut-- = *szSrc--;
1135 dwState |= NF_DIGITS_OUT;
1136 dwCurrentGroupCount++;
1137 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-')
1139 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1141 while (lpszGrp >= lpFormat->lpThousandSep)
1142 *szOut-- = *lpszGrp--; /* Write grouping char */
1144 dwCurrentGroupCount = 0;
1145 if (lpFormat->Grouping == 32)
1146 dwGroupCount = 2; /* Indic grouping: 3 then 2 */
1149 if (dwState & NF_ROUND)
1151 *szOut-- = '1'; /* e.g. .6 > 1.0 */
1153 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1154 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1156 /* Add any leading negative sign */
1157 if (dwState & NF_ISNEGATIVE)
1159 switch (lpFormat->NegativeOrder)
1161 case NLS_NEG_PARENS:
1162 *szOut-- = '(';
1163 break;
1164 case NLS_NEG_LEFT_SPACE:
1165 *szOut-- = ' ';
1166 /* Fall through */
1167 case NLS_NEG_LEFT:
1168 while (lpszNeg >= lpszNegStart)
1169 *szOut-- = *lpszNeg--;
1170 break;
1173 szOut++;
1175 iRet = strlenW(szOut) + 1;
1176 if (cchOut)
1178 if (iRet <= cchOut)
1179 memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR));
1180 else
1182 memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR));
1183 lpNumberStr[cchOut - 1] = '\0';
1184 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1185 iRet = 0;
1188 return iRet;
1191 /**************************************************************************
1192 * GetCurrencyFormatA (KERNEL32.@)
1194 * Format a currency string for a given locale.
1196 * PARAMS
1197 * lcid [I] Locale to format for
1198 * dwFlags [I] LOCALE_ flags from "winnls.h"
1199 * lpszValue [I] String to format
1200 * lpFormat [I] Formatting overrides
1201 * lpCurrencyStr [O] Destination for formatted string
1202 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1204 * NOTES
1205 * - lpszValue can contain only '0' - '9', '-' and '.'.
1206 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1207 * be formatted according to the format details returned by GetLocaleInfoA().
1208 * - This function rounds the currency if the number of decimals exceeds the
1209 * locales number of currency decimal places.
1210 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1211 * - The Ascii version of this function fails if lcid is Unicode only.
1213 * RETURNS
1214 * Success: The number of character written to lpNumberStr, or that would
1215 * have been written, if cchOut is 0.
1216 * Failure: 0. Use GetLastError() to determine the cause.
1218 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags,
1219 LPCSTR lpszValue, const CURRENCYFMTA *lpFormat,
1220 LPSTR lpCurrencyStr, int cchOut)
1222 DWORD cp = CP_ACP;
1223 WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128];
1224 CURRENCYFMTW fmt;
1225 const CURRENCYFMTW *pfmt = NULL;
1226 INT iRet;
1228 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue),
1229 lpFormat, lpCurrencyStr, cchOut);
1231 if (NLS_IsUnicodeOnlyLcid(lcid))
1233 GetCurrencyFormatA_InvalidParameter:
1234 SetLastError(ERROR_INVALID_PARAMETER);
1235 return 0;
1238 if (!(dwFlags & LOCALE_USE_CP_ACP))
1240 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1241 if (!node)
1242 goto GetCurrencyFormatA_InvalidParameter;
1243 cp = node->dwCodePage;
1246 if (lpFormat)
1248 memcpy(&fmt, lpFormat, sizeof(fmt));
1249 pfmt = &fmt;
1250 if (lpFormat->lpDecimalSep)
1252 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR));
1253 fmt.lpDecimalSep = szDec;
1255 if (lpFormat->lpThousandSep)
1257 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR));
1258 fmt.lpThousandSep = szGrp;
1260 if (lpFormat->lpCurrencySymbol)
1262 MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR));
1263 fmt.lpCurrencySymbol = szCy;
1267 if (lpszValue)
1268 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR));
1270 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR)))
1271 cchOut = sizeof(szOut)/sizeof(WCHAR);
1273 szOut[0] = '\0';
1275 iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt,
1276 lpCurrencyStr ? szOut : NULL, cchOut);
1278 if (szOut[0] && lpCurrencyStr)
1279 WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0);
1280 return iRet;
1283 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1284 #define CF_PARENS 0x1 /* Parentheses */
1285 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1286 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1287 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1288 #define CF_CY_LEFT 0x10 /* '$' to the left */
1289 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1290 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1292 /**************************************************************************
1293 * GetCurrencyFormatW (KERNEL32.@)
1295 * See GetCurrencyFormatA.
1297 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags,
1298 LPCWSTR lpszValue, const CURRENCYFMTW *lpFormat,
1299 LPWSTR lpCurrencyStr, int cchOut)
1301 static const BYTE NLS_NegCyFormats[16] =
1303 CF_PARENS|CF_CY_LEFT, /* ($1.1) */
1304 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT, /* -$1.1 */
1305 CF_MINUS_LEFT|CF_CY_LEFT, /* $-1.1 */
1306 CF_MINUS_RIGHT|CF_CY_LEFT, /* $1.1- */
1307 CF_PARENS|CF_CY_RIGHT, /* (1.1$) */
1308 CF_MINUS_LEFT|CF_CY_RIGHT, /* -1.1$ */
1309 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$ */
1310 CF_MINUS_RIGHT|CF_CY_RIGHT, /* 1.1$- */
1311 CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE, /* -1.1 $ */
1312 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE, /* -$ 1.1 */
1313 CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $- */
1314 CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1- */
1315 CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE, /* $ -1.1 */
1316 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */
1317 CF_PARENS|CF_CY_LEFT|CF_CY_SPACE, /* ($ 1.1) */
1318 CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE, /* (1.1 $) */
1320 static const BYTE NLS_PosCyFormats[4] =
1322 CF_CY_LEFT, /* $1.1 */
1323 CF_CY_RIGHT, /* 1.1$ */
1324 CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1 */
1325 CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */
1327 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1;
1328 WCHAR szNegBuff[8];
1329 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart;
1330 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt;
1331 INT iRet;
1333 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue),
1334 lpFormat, lpCurrencyStr, cchOut);
1336 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) ||
1337 !IsValidLocale(lcid, 0) ||
1338 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep ||
1339 !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 ||
1340 lpFormat->PositiveOrder > 3)))
1342 GetCurrencyFormatW_Error:
1343 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER);
1344 return 0;
1347 if (!lpFormat)
1349 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags);
1351 if (!node)
1352 goto GetCurrencyFormatW_Error;
1353 lpFormat = &node->cyfmt;
1354 lpszNegStart = lpszNeg = GetNegative(node);
1356 else
1358 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE),
1359 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR));
1360 lpszNegStart = lpszNeg = szNegBuff;
1362 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP);
1364 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1;
1365 lpszCyStart = lpFormat->lpCurrencySymbol;
1366 lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1;
1368 /* Format the currency backwards into a temporary buffer */
1370 szSrc = lpszValue;
1371 *szOut-- = '\0';
1373 /* Check the number for validity */
1374 while (*szSrc)
1376 if (*szSrc >= '0' && *szSrc <= '9')
1378 dwState |= NF_DIGITS;
1379 if (dwState & NF_ISREAL)
1380 dwDecimals++;
1382 else if (*szSrc == '-')
1384 if (dwState)
1385 goto GetCurrencyFormatW_Error; /* '-' not first character */
1386 dwState |= NF_ISNEGATIVE;
1388 else if (*szSrc == '.')
1390 if (dwState & NF_ISREAL)
1391 goto GetCurrencyFormatW_Error; /* More than one '.' */
1392 dwState |= NF_ISREAL;
1394 else
1395 goto GetCurrencyFormatW_Error; /* Invalid char */
1396 szSrc++;
1398 szSrc--; /* Point to last character */
1400 if (!(dwState & NF_DIGITS))
1401 goto GetCurrencyFormatW_Error; /* No digits */
1403 if (dwState & NF_ISNEGATIVE)
1404 dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder];
1405 else
1406 dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder];
1408 /* Add any trailing negative or currency signs */
1409 if (dwFmt & CF_PARENS)
1410 *szOut-- = ')';
1412 while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT))
1414 switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT))
1416 case CF_MINUS_RIGHT:
1417 case CF_MINUS_RIGHT|CF_CY_RIGHT:
1418 while (lpszNeg >= lpszNegStart)
1419 *szOut-- = *lpszNeg--;
1420 dwFmt &= ~CF_MINUS_RIGHT;
1421 break;
1423 case CF_CY_RIGHT:
1424 case CF_MINUS_BEFORE|CF_CY_RIGHT:
1425 case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT:
1426 while (lpszCy >= lpszCyStart)
1427 *szOut-- = *lpszCy--;
1428 if (dwFmt & CF_CY_SPACE)
1429 *szOut-- = ' ';
1430 dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE);
1431 break;
1435 /* Copy all digits up to the decimal point */
1436 if (!lpFormat->NumDigits)
1438 if (dwState & NF_ISREAL)
1440 while (*szSrc != '.') /* Don't write any decimals or a separator */
1442 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1443 dwState |= NF_ROUND;
1444 else
1445 dwState &= ~NF_ROUND;
1446 szSrc--;
1448 szSrc--;
1451 else
1453 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1;
1455 if (dwDecimals <= lpFormat->NumDigits)
1457 dwDecimals = lpFormat->NumDigits - dwDecimals;
1458 while (dwDecimals--)
1459 *szOut-- = '0'; /* Pad to correct number of dp */
1461 else
1463 dwDecimals -= lpFormat->NumDigits;
1464 /* Skip excess decimals, and determine if we have to round the number */
1465 while (dwDecimals--)
1467 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND)))
1468 dwState |= NF_ROUND;
1469 else
1470 dwState &= ~NF_ROUND;
1471 szSrc--;
1475 if (dwState & NF_ISREAL)
1477 while (*szSrc != '.')
1479 if (dwState & NF_ROUND)
1481 if (*szSrc == '9')
1482 *szOut-- = '0'; /* continue rounding */
1483 else
1485 dwState &= ~NF_ROUND;
1486 *szOut-- = (*szSrc)+1;
1488 szSrc--;
1490 else
1491 *szOut-- = *szSrc--; /* Write existing decimals */
1493 szSrc--; /* Skip '.' */
1495 while (lpszDec >= lpFormat->lpDecimalSep)
1496 *szOut-- = *lpszDec--; /* Write decimal separator */
1499 dwGroupCount = lpFormat->Grouping;
1501 /* Write the remaining whole number digits, including grouping chars */
1502 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9')
1504 if (dwState & NF_ROUND)
1506 if (*szSrc == '9')
1507 *szOut-- = '0'; /* continue rounding */
1508 else
1510 dwState &= ~NF_ROUND;
1511 *szOut-- = (*szSrc)+1;
1513 szSrc--;
1515 else
1516 *szOut-- = *szSrc--;
1518 dwState |= NF_DIGITS_OUT;
1519 dwCurrentGroupCount++;
1520 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount)
1522 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1;
1524 while (lpszGrp >= lpFormat->lpThousandSep)
1525 *szOut-- = *lpszGrp--; /* Write grouping char */
1527 dwCurrentGroupCount = 0;
1530 if (dwState & NF_ROUND)
1531 *szOut-- = '1'; /* e.g. .6 > 1.0 */
1532 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero)
1533 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1535 /* Add any leading negative or currency sign */
1536 while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT))
1538 switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT))
1540 case CF_MINUS_LEFT:
1541 case CF_MINUS_LEFT|CF_CY_LEFT:
1542 while (lpszNeg >= lpszNegStart)
1543 *szOut-- = *lpszNeg--;
1544 dwFmt &= ~CF_MINUS_LEFT;
1545 break;
1547 case CF_CY_LEFT:
1548 case CF_CY_LEFT|CF_MINUS_BEFORE:
1549 case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT:
1550 if (dwFmt & CF_CY_SPACE)
1551 *szOut-- = ' ';
1552 while (lpszCy >= lpszCyStart)
1553 *szOut-- = *lpszCy--;
1554 dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE);
1555 break;
1558 if (dwFmt & CF_PARENS)
1559 *szOut-- = '(';
1560 szOut++;
1562 iRet = strlenW(szOut) + 1;
1563 if (cchOut)
1565 if (iRet <= cchOut)
1566 memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR));
1567 else
1569 memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR));
1570 lpCurrencyStr[cchOut - 1] = '\0';
1571 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1572 iRet = 0;
1575 return iRet;
1578 /* FIXME: Everything below here needs to move somewhere else along with the
1579 * other EnumXXX functions, when a method for storing resources for
1580 * alternate calendars is determined.
1583 /**************************************************************************
1584 * EnumDateFormatsExA (KERNEL32.@)
1586 BOOL WINAPI EnumDateFormatsExA( DATEFMT_ENUMPROCEXA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
1588 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
1589 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1590 return FALSE;
1593 /**************************************************************************
1594 * EnumDateFormatsExW (KERNEL32.@)
1596 BOOL WINAPI EnumDateFormatsExW( DATEFMT_ENUMPROCEXW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
1598 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
1599 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1600 return FALSE;
1603 /**************************************************************************
1604 * EnumDateFormatsA (KERNEL32.@)
1606 BOOL WINAPI EnumDateFormatsA( DATEFMT_ENUMPROCA lpDateFmtEnumProc, LCID Locale, DWORD dwFlags)
1608 LCID Loc = GetUserDefaultLCID();
1609 if(!lpDateFmtEnumProc)
1611 SetLastError(ERROR_INVALID_PARAMETER);
1612 return FALSE;
1615 switch( Loc )
1618 case 0x00000407: /* (Loc,"de_DE") */
1620 switch(dwFlags)
1622 case DATE_SHORTDATE:
1623 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1624 if(!(*lpDateFmtEnumProc)("d.M.yyyy")) return TRUE;
1625 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1626 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1627 return TRUE;
1628 case DATE_LONGDATE:
1629 if(!(*lpDateFmtEnumProc)("dddd,d. MMMM yyyy")) return TRUE;
1630 if(!(*lpDateFmtEnumProc)("d. MMMM yyyy")) return TRUE;
1631 if(!(*lpDateFmtEnumProc)("d. MMM yyyy")) return TRUE;
1632 return TRUE;
1633 default:
1634 FIXME("Unknown date format (%ld)\n", dwFlags);
1635 SetLastError(ERROR_INVALID_PARAMETER);
1636 return FALSE;
1640 case 0x0000040c: /* (Loc,"fr_FR") */
1642 switch(dwFlags)
1644 case DATE_SHORTDATE:
1645 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1646 if(!(*lpDateFmtEnumProc)("dd.MM.yy")) return TRUE;
1647 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1648 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1649 return TRUE;
1650 case DATE_LONGDATE:
1651 if(!(*lpDateFmtEnumProc)("dddd d MMMM yyyy")) return TRUE;
1652 if(!(*lpDateFmtEnumProc)("d MMM yy")) return TRUE;
1653 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1654 return TRUE;
1655 default:
1656 FIXME("Unknown date format (%ld)\n", dwFlags);
1657 SetLastError(ERROR_INVALID_PARAMETER);
1658 return FALSE;
1662 case 0x00000c0c: /* (Loc,"fr_CA") */
1664 switch(dwFlags)
1666 case DATE_SHORTDATE:
1667 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1668 if(!(*lpDateFmtEnumProc)("dd-MM-yy")) return TRUE;
1669 if(!(*lpDateFmtEnumProc)("yy MM dd")) return TRUE;
1670 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1671 return TRUE;
1672 case DATE_LONGDATE:
1673 if(!(*lpDateFmtEnumProc)("d MMMM, yyyy")) return TRUE;
1674 if(!(*lpDateFmtEnumProc)("d MMM yyyy")) return TRUE;
1675 return TRUE;
1676 default:
1677 FIXME("Unknown date format (%ld)\n", dwFlags);
1678 SetLastError(ERROR_INVALID_PARAMETER);
1679 return FALSE;
1683 case 0x00000809: /* (Loc,"en_UK") */
1685 switch(dwFlags)
1687 case DATE_SHORTDATE:
1688 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1689 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1690 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1691 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1692 return TRUE;
1693 case DATE_LONGDATE:
1694 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1695 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1696 return TRUE;
1697 default:
1698 FIXME("Unknown date format (%ld)\n", dwFlags);
1699 SetLastError(ERROR_INVALID_PARAMETER);
1700 return FALSE;
1704 case 0x00000c09: /* (Loc,"en_AU") */
1706 switch(dwFlags)
1708 case DATE_SHORTDATE:
1709 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1710 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1711 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1712 return TRUE;
1713 case DATE_LONGDATE:
1714 if(!(*lpDateFmtEnumProc)("dddd,d MMMM yyyy")) return TRUE;
1715 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1716 return TRUE;
1717 default:
1718 FIXME("Unknown date format (%ld)\n", dwFlags);
1719 SetLastError(ERROR_INVALID_PARAMETER);
1720 return FALSE;
1724 case 0x00001009: /* (Loc,"en_CA") */
1726 switch(dwFlags)
1728 case DATE_SHORTDATE:
1729 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1730 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1731 if(!(*lpDateFmtEnumProc)("yy-MM-dd")) return TRUE;
1732 if(!(*lpDateFmtEnumProc)("M/dd/yy")) return TRUE;
1733 return TRUE;
1734 case DATE_LONGDATE:
1735 if(!(*lpDateFmtEnumProc)("d-MMM-yy")) return TRUE;
1736 if(!(*lpDateFmtEnumProc)("MMMM d, yyyy")) return TRUE;
1737 return TRUE;
1738 default:
1739 FIXME("Unknown date format (%ld)\n", dwFlags);
1740 SetLastError(ERROR_INVALID_PARAMETER);
1741 return FALSE;
1745 case 0x00001409: /* (Loc,"en_NZ") */
1747 switch(dwFlags)
1749 case DATE_SHORTDATE:
1750 if(!(*lpDateFmtEnumProc)("d/MM/yy")) return TRUE;
1751 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1752 if(!(*lpDateFmtEnumProc)("d.MM.yy")) return TRUE;
1753 return TRUE;
1754 case DATE_LONGDATE:
1755 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1756 if(!(*lpDateFmtEnumProc)("dddd, d MMMM yyyy")) return TRUE;
1757 return TRUE;
1758 default:
1759 FIXME("Unknown date format (%ld)\n", dwFlags);
1760 SetLastError(ERROR_INVALID_PARAMETER);
1761 return FALSE;
1765 case 0x00001809: /* (Loc,"en_IE") */
1767 switch(dwFlags)
1769 case DATE_SHORTDATE:
1770 if(!(*lpDateFmtEnumProc)("dd/MM/yy")) return TRUE;
1771 if(!(*lpDateFmtEnumProc)("d/M/yy")) return TRUE;
1772 if(!(*lpDateFmtEnumProc)("d.M.yy")) return TRUE;
1773 return TRUE;
1774 case DATE_LONGDATE:
1775 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1776 if(!(*lpDateFmtEnumProc)("d MMMM yyyy")) return TRUE;
1777 return TRUE;
1778 default:
1779 FIXME("Unknown date format (%ld)\n", dwFlags);
1780 SetLastError(ERROR_INVALID_PARAMETER);
1781 return FALSE;
1785 case 0x00001c09: /* (Loc,"en_ZA") */
1787 switch(dwFlags)
1789 case DATE_SHORTDATE:
1790 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
1791 return TRUE;
1792 case DATE_LONGDATE:
1793 if(!(*lpDateFmtEnumProc)("dd MMMM yyyy")) return TRUE;
1794 return TRUE;
1795 default:
1796 FIXME("Unknown date format (%ld)\n", dwFlags);
1797 SetLastError(ERROR_INVALID_PARAMETER);
1798 return FALSE;
1802 case 0x00002009: /* (Loc,"en_JM") */
1804 switch(dwFlags)
1806 case DATE_SHORTDATE:
1807 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1808 return TRUE;
1809 case DATE_LONGDATE:
1810 if(!(*lpDateFmtEnumProc)("dddd,MMMM dd,yyyy")) return TRUE;
1811 if(!(*lpDateFmtEnumProc)("MMMM dd,yyyy")) return TRUE;
1812 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM,yyyy")) return TRUE;
1813 if(!(*lpDateFmtEnumProc)("dd MMMM,yyyy")) return TRUE;
1814 return TRUE;
1815 default:
1816 FIXME("Unknown date format (%ld)\n", dwFlags);
1817 SetLastError(ERROR_INVALID_PARAMETER);
1818 return FALSE;
1822 case 0x00002809: /* (Loc,"en_BZ") */
1823 case 0x00002c09: /* (Loc,"en_TT") */
1825 switch(dwFlags)
1827 case DATE_SHORTDATE:
1828 if(!(*lpDateFmtEnumProc)("dd/MM/yyyy")) return TRUE;
1829 return TRUE;
1830 case DATE_LONGDATE:
1831 if(!(*lpDateFmtEnumProc)("dddd,dd MMMM yyyy")) return TRUE;
1832 return TRUE;
1833 default:
1834 FIXME("Unknown date format (%ld)\n", dwFlags);
1835 SetLastError(ERROR_INVALID_PARAMETER);
1836 return FALSE;
1840 default: /* default to US English "en_US" */
1842 switch(dwFlags)
1844 case DATE_SHORTDATE:
1845 if(!(*lpDateFmtEnumProc)("M/d/yy")) return TRUE;
1846 if(!(*lpDateFmtEnumProc)("M/d/yyyy")) return TRUE;
1847 if(!(*lpDateFmtEnumProc)("MM/dd/yy")) return TRUE;
1848 if(!(*lpDateFmtEnumProc)("MM/dd/yyyy")) return TRUE;
1849 if(!(*lpDateFmtEnumProc)("yy/MM/dd")) return TRUE;
1850 if(!(*lpDateFmtEnumProc)("dd-MMM-yy")) return TRUE;
1851 return TRUE;
1852 case DATE_LONGDATE:
1853 if(!(*lpDateFmtEnumProc)("dddd, MMMM dd, yyyy")) return TRUE;
1854 if(!(*lpDateFmtEnumProc)("MMMM dd, yyyy")) return TRUE;
1855 if(!(*lpDateFmtEnumProc)("dddd, dd MMMM, yyyy")) return TRUE;
1856 if(!(*lpDateFmtEnumProc)("dd MMMM, yyyy")) return TRUE;
1857 return TRUE;
1858 default:
1859 FIXME("Unknown date format (%ld)\n", dwFlags);
1860 SetLastError(ERROR_INVALID_PARAMETER);
1861 return FALSE;
1867 /**************************************************************************
1868 * EnumDateFormatsW (KERNEL32.@)
1870 BOOL WINAPI EnumDateFormatsW( DATEFMT_ENUMPROCW lpDateFmtEnumProc, LCID Locale, DWORD dwFlags )
1872 FIXME("(%p, %ld, %ld): stub\n", lpDateFmtEnumProc, Locale, dwFlags);
1873 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1874 return FALSE;
1877 /**************************************************************************
1878 * EnumTimeFormatsA (KERNEL32.@)
1880 BOOL WINAPI EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
1882 LCID Loc = GetUserDefaultLCID();
1883 if(!lpTimeFmtEnumProc)
1885 SetLastError(ERROR_INVALID_PARAMETER);
1886 return FALSE;
1888 if(dwFlags)
1890 FIXME("Unknown time format (%ld)\n", dwFlags);
1893 switch( Loc )
1895 case 0x00000407: /* (Loc,"de_DE") */
1897 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
1898 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1899 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1900 if(!(*lpTimeFmtEnumProc)("H.mm")) return TRUE;
1901 if(!(*lpTimeFmtEnumProc)("H.mm'Uhr'")) return TRUE;
1902 return TRUE;
1905 case 0x0000040c: /* (Loc,"fr_FR") */
1906 case 0x00000c0c: /* (Loc,"fr_CA") */
1908 if(!(*lpTimeFmtEnumProc)("H:mm")) return TRUE;
1909 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1910 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1911 if(!(*lpTimeFmtEnumProc)("HH.mm")) return TRUE;
1912 if(!(*lpTimeFmtEnumProc)("HH'h'mm")) return TRUE;
1913 return TRUE;
1916 case 0x00000809: /* (Loc,"en_UK") */
1917 case 0x00000c09: /* (Loc,"en_AU") */
1918 case 0x00001409: /* (Loc,"en_NZ") */
1919 case 0x00001809: /* (Loc,"en_IE") */
1921 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1922 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1923 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1924 return TRUE;
1927 case 0x00001c09: /* (Loc,"en_ZA") */
1928 case 0x00002809: /* (Loc,"en_BZ") */
1929 case 0x00002c09: /* (Loc,"en_TT") */
1931 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1932 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
1933 return TRUE;
1936 default: /* default to US style "en_US" */
1938 if(!(*lpTimeFmtEnumProc)("h:mm:ss tt")) return TRUE;
1939 if(!(*lpTimeFmtEnumProc)("hh:mm:ss tt")) return TRUE;
1940 if(!(*lpTimeFmtEnumProc)("H:mm:ss")) return TRUE;
1941 if(!(*lpTimeFmtEnumProc)("HH:mm:ss")) return TRUE;
1942 return TRUE;
1947 /**************************************************************************
1948 * EnumTimeFormatsW (KERNEL32.@)
1950 BOOL WINAPI EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc, LCID Locale, DWORD dwFlags )
1952 FIXME("(%p,%ld,%ld): stub\n", lpTimeFmtEnumProc, Locale, dwFlags);
1953 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1954 return FALSE;
1957 /******************************************************************************
1958 * NLS_EnumCalendarInfoAW <internal>
1959 * Enumerates calendar information for a specified locale.
1961 * PARAMS
1962 * calinfoproc [I] Pointer to the callback
1963 * locale [I] The locale for which to retrieve calendar information.
1964 * This parameter can be a locale identifier created by the
1965 * MAKELCID macro, or one of the following values:
1966 * LOCALE_SYSTEM_DEFAULT
1967 * Use the default system locale.
1968 * LOCALE_USER_DEFAULT
1969 * Use the default user locale.
1970 * calendar [I] The calendar for which information is requested, or
1971 * ENUM_ALL_CALENDARS.
1972 * caltype [I] The type of calendar information to be returned. Note
1973 * that only one CALTYPE value can be specified per call
1974 * of this function, except where noted.
1975 * unicode [I] Specifies if the callback expects a unicode string.
1976 * ex [I] Specifies if the callback needs the calendar identifier.
1978 * RETURNS
1979 * Success: TRUE.
1980 * Failure: FALSE. Use GetLastError() to determine the cause.
1982 * NOTES
1983 * When the ANSI version of this function is used with a Unicode-only LCID,
1984 * the call can succeed because the system uses the system code page.
1985 * However, characters that are undefined in the system code page appear
1986 * in the string as a question mark (?).
1988 * TODO
1989 * The above note should be respected by GetCalendarInfoA.
1991 BOOL WINAPI NLS_EnumCalendarInfoAW(void *calinfoproc, LCID locale,
1992 CALID calendar, CALTYPE caltype, BOOL unicode, BOOL ex )
1994 WCHAR *buf, *opt = NULL, *iter = NULL;
1995 BOOL ret = FALSE;
1996 int bufSz = 200; /* the size of the buffer */
1998 if (calinfoproc == NULL)
2000 SetLastError(ERROR_INVALID_PARAMETER);
2001 return FALSE;
2004 buf = HeapAlloc(GetProcessHeap(), 0, bufSz);
2005 if (buf == NULL)
2007 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2008 return FALSE;
2011 if (calendar == ENUM_ALL_CALENDARS)
2013 int optSz = GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, NULL, 0);
2014 if (optSz > 1)
2016 opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR));
2017 if (opt == NULL)
2019 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2020 goto NLS_EnumCalendarInfoAW_Cleanup;
2022 if (GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, opt, optSz))
2023 iter = opt;
2025 calendar = NLS_GetLocaleNumber(locale, LOCALE_ICALENDARTYPE);
2028 while (TRUE) /* loop through calendars */
2030 do /* loop until there's no error */
2032 if (unicode)
2033 ret = GetCalendarInfoW(locale, calendar, caltype, buf, bufSz / sizeof(WCHAR), NULL);
2034 else ret = GetCalendarInfoA(locale, calendar, caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL);
2036 if (!ret)
2038 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
2039 { /* so resize it */
2040 int newSz;
2041 if (unicode)
2042 newSz = GetCalendarInfoW(locale, calendar, caltype, NULL, 0, NULL) * sizeof(WCHAR);
2043 else newSz = GetCalendarInfoA(locale, calendar, caltype, NULL, 0, NULL) * sizeof(CHAR);
2044 if (bufSz >= newSz)
2046 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz);
2047 goto NLS_EnumCalendarInfoAW_Cleanup;
2049 bufSz = newSz;
2050 WARN("Buffer too small; resizing to %d bytes.\n", bufSz);
2051 buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz);
2052 if (buf == NULL)
2053 goto NLS_EnumCalendarInfoAW_Cleanup;
2054 } else goto NLS_EnumCalendarInfoAW_Cleanup;
2056 } while (!ret);
2058 /* Here we are. We pass the buffer to the correct version of
2059 * the callback. Because it's not the same number of params,
2060 * we must check for Ex, but we don't care about Unicode
2061 * because the buffer is already in the correct format.
2063 if (ex) {
2064 ret = ((CALINFO_ENUMPROCEXW)calinfoproc)(buf, calendar);
2065 } else
2066 ret = ((CALINFO_ENUMPROCW)calinfoproc)(buf);
2068 if (!ret) { /* the callback told to stop */
2069 ret = TRUE;
2070 break;
2073 if ((iter == NULL) || (*iter == 0)) /* no more calendars */
2074 break;
2076 calendar = 0;
2077 while ((*iter >= '0') && (*iter <= '9'))
2078 calendar = calendar * 10 + *iter++ - '0';
2080 if (*iter++ != 0)
2082 SetLastError(ERROR_BADDB);
2083 ret = FALSE;
2084 break;
2088 NLS_EnumCalendarInfoAW_Cleanup:
2089 HeapFree(GetProcessHeap(), 0, opt);
2090 HeapFree(GetProcessHeap(), 0, buf);
2091 return ret;
2094 /******************************************************************************
2095 * EnumCalendarInfoA [KERNEL32.@]
2097 * See EnumCalendarInfoAW.
2099 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale,
2100 CALID calendar,CALTYPE caltype )
2102 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc, locale, calendar, caltype);
2103 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, FALSE);
2106 /******************************************************************************
2107 * EnumCalendarInfoW [KERNEL32.@]
2109 * See EnumCalendarInfoAW.
2111 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale,
2112 CALID calendar,CALTYPE caltype )
2114 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc, locale, calendar, caltype);
2115 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, FALSE);
2118 /******************************************************************************
2119 * EnumCalendarInfoExA [KERNEL32.@]
2121 * See EnumCalendarInfoAW.
2123 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale,
2124 CALID calendar,CALTYPE caltype )
2126 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc, locale, calendar, caltype);
2127 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, TRUE);
2130 /******************************************************************************
2131 * EnumCalendarInfoExW [KERNEL32.@]
2133 * See EnumCalendarInfoAW.
2135 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale,
2136 CALID calendar,CALTYPE caltype )
2138 TRACE("(%p,0x%08lx,0x%08lx,0x%08lx)\n", calinfoproc, locale, calendar, caltype);
2139 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, TRUE);