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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * Whole file ripped from Wine's dlls\kernel\lcformat.c, rev 1.7 and is
26 * unchanged except that includes are different. I thought about adding
27 * @implemeted to each exported function, but this might make merging harder?
33 #include "wine/config.h"
34 #include "wine/unicode.h"
43 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
44 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
46 /* Since calculating the formatting data for each locale is time-consuming,
47 * we get the format data for each locale only once and cache it in memory.
48 * We cache both the system default and user overridden data, after converting
49 * them into the formats that the functions here expect. Since these functions
50 * will typically be called with only a small number of the total locales
51 * installed, the memory overhead is minimal while the speedup is significant.
53 * Our cache takes the form of a singly linked list, whose node is below:
55 #define NLS_NUM_CACHED_STRINGS 45
57 typedef struct _NLS_FORMAT_NODE
59 LCID lcid
; /* Locale Id */
60 DWORD dwFlags
; /* 0 or LOCALE_NOUSEROVERRIDE */
61 DWORD dwCodePage
; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
62 NUMBERFMTW fmt
; /* Default format for numbers */
63 CURRENCYFMTW cyfmt
; /* Default format for currencies */
64 LPWSTR lppszStrings
[NLS_NUM_CACHED_STRINGS
]; /* Default formats,day/month names */
65 WCHAR szShortAM
[2]; /* Short 'AM' marker */
66 WCHAR szShortPM
[2]; /* Short 'PM' marker */
67 struct _NLS_FORMAT_NODE
*next
;
70 /* Macros to get particular data strings from a format node */
71 #define GetNegative(fmt) fmt->lppszStrings[0]
72 #define GetLongDate(fmt) fmt->lppszStrings[1]
73 #define GetShortDate(fmt) fmt->lppszStrings[2]
74 #define GetTime(fmt) fmt->lppszStrings[3]
75 #define GetAM(fmt) fmt->lppszStrings[42]
76 #define GetPM(fmt) fmt->lppszStrings[43]
77 #define GetYearMonth(fmt) fmt->lppszStrings[44]
79 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
80 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
81 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
82 #define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
84 /* Write access to the cache is protected by this critical section */
85 static RTL_CRITICAL_SECTION NLS_FormatsCS
;
86 static RTL_CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug
=
89 { &NLS_FormatsCS_debug
.ProcessLocksList
,
90 &NLS_FormatsCS_debug
.ProcessLocksList
},
93 static RTL_CRITICAL_SECTION NLS_FormatsCS
= { &NLS_FormatsCS_debug
, -1, 0, 0, 0, 0 };
95 /**************************************************************************
96 * NLS_isSystemLocale <internal>
98 * Return TRUE, if locale is system-type
100 BOOL
NLS_isSystemLocale(LCID lcid
)
102 if(lcid
== LOCALE_SYSTEM_DEFAULT
||
103 lcid
== LOCALE_NEUTRAL
||
104 lcid
== LOCALE_USER_DEFAULT
)
111 /**************************************************************************
112 * NLS_isSystemLocale <internal>
114 * Return default system or user locale
116 LCID
NLS_getDefaultLocale(LCID lcid
)
120 DPRINT("Called NLS_getDefaultLocale(0x%04lx)\n", lcid
);
124 case LOCALE_SYSTEM_DEFAULT
:
125 NtQueryDefaultLocale(FALSE
, &lcidTmp
);
129 case LOCALE_USER_DEFAULT
:
131 NtQueryDefaultLocale(TRUE
, &lcidTmp
);
136 DPRINT1("FIXME: unknown system lcid\n");
142 /**************************************************************************
143 * NLS_GetLocaleNumber <internal>
145 * Get a numeric locale format value.
147 static DWORD
NLS_GetLocaleNumber(LCID lcid
, DWORD dwFlags
)
153 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
155 if (szBuff
[0] && szBuff
[1] == ';' && szBuff
[2] != '0')
156 dwVal
= (szBuff
[0] - '0') * 10 + (szBuff
[2] - '0');
159 const WCHAR
* iter
= szBuff
;
161 while(*iter
>= '0' && *iter
<= '9')
162 dwVal
= dwVal
* 10 + (*iter
++ - '0');
167 /**************************************************************************
168 * NLS_GetLocaleString <internal>
170 * Get a string locale format value.
172 static WCHAR
* NLS_GetLocaleString(LCID lcid
, DWORD dwFlags
)
174 WCHAR szBuff
[80], *str
;
178 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
179 dwLen
= strlenW(szBuff
) + 1;
180 str
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
182 memcpy(str
, szBuff
, dwLen
* sizeof(WCHAR
));
186 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
187 TRACE( #type ": %ld (%08lx)\n", (DWORD)num, (DWORD)num)
189 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
190 TRACE( #type ": '%S'\n", (str))
192 /**************************************************************************
193 * NLS_GetFormats <internal>
195 * Calculate (and cache) the number formats for a locale.
197 static const NLS_FORMAT_NODE
*NLS_GetFormats(LCID lcid
, DWORD dwFlags
)
199 /* GetLocaleInfo() identifiers for cached formatting strings */
200 static const USHORT NLS_LocaleIndices
[] = {
201 LOCALE_SNEGATIVESIGN
,
202 LOCALE_SLONGDATE
, LOCALE_SSHORTDATE
,
204 LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
205 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
, LOCALE_SDAYNAME7
,
206 LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
, LOCALE_SABBREVDAYNAME3
,
207 LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
, LOCALE_SABBREVDAYNAME6
,
208 LOCALE_SABBREVDAYNAME7
,
209 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
,
210 LOCALE_SMONTHNAME4
, LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
,
211 LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
, LOCALE_SMONTHNAME9
,
212 LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
213 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
214 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
215 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
216 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
217 LOCALE_S1159
, LOCALE_S2359
,
220 static NLS_FORMAT_NODE
*NLS_CachedFormats
= NULL
;
221 NLS_FORMAT_NODE
*node
= NLS_CachedFormats
;
223 dwFlags
&= LOCALE_NOUSEROVERRIDE
;
225 TRACE("(0x%04lx,0x%08lx)\n", lcid
, dwFlags
);
227 /* See if we have already cached the locales number format */
228 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
231 if (!node
|| node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
233 NLS_FORMAT_NODE
*new_node
;
236 TRACE("Creating new cache entry\n");
238 if (!(new_node
= HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE
))))
241 GET_LOCALE_NUMBER(new_node
->dwCodePage
, LOCALE_IDEFAULTANSICODEPAGE
);
244 new_node
->lcid
= lcid
;
245 new_node
->dwFlags
= dwFlags
;
246 new_node
->next
= NULL
;
248 GET_LOCALE_NUMBER(new_node
->fmt
.NumDigits
, LOCALE_IDIGITS
);
249 GET_LOCALE_NUMBER(new_node
->fmt
.LeadingZero
, LOCALE_ILZERO
);
250 GET_LOCALE_NUMBER(new_node
->fmt
.NegativeOrder
, LOCALE_INEGNUMBER
);
252 GET_LOCALE_NUMBER(new_node
->fmt
.Grouping
, LOCALE_SGROUPING
);
253 if (new_node
->fmt
.Grouping
> 9 && new_node
->fmt
.Grouping
!= 32)
255 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
256 new_node
->fmt
.Grouping
);
257 new_node
->fmt
.Grouping
= 0;
260 GET_LOCALE_STRING(new_node
->fmt
.lpDecimalSep
, LOCALE_SDECIMAL
);
261 GET_LOCALE_STRING(new_node
->fmt
.lpThousandSep
, LOCALE_STHOUSAND
);
263 /* Currency Format */
264 new_node
->cyfmt
.NumDigits
= new_node
->fmt
.NumDigits
;
265 new_node
->cyfmt
.LeadingZero
= new_node
->fmt
.LeadingZero
;
267 GET_LOCALE_NUMBER(new_node
->cyfmt
.Grouping
, LOCALE_SGROUPING
);
269 if (new_node
->cyfmt
.Grouping
> 9)
271 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
272 new_node
->cyfmt
.Grouping
);
273 new_node
->cyfmt
.Grouping
= 0;
276 GET_LOCALE_NUMBER(new_node
->cyfmt
.NegativeOrder
, LOCALE_INEGCURR
);
277 if (new_node
->cyfmt
.NegativeOrder
> 15)
279 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
280 new_node
->cyfmt
.NegativeOrder
);
281 new_node
->cyfmt
.NegativeOrder
= 0;
283 GET_LOCALE_NUMBER(new_node
->cyfmt
.PositiveOrder
, LOCALE_ICURRENCY
);
284 if (new_node
->cyfmt
.PositiveOrder
> 3)
286 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
287 new_node
->cyfmt
.PositiveOrder
);
288 new_node
->cyfmt
.PositiveOrder
= 0;
290 GET_LOCALE_STRING(new_node
->cyfmt
.lpDecimalSep
, LOCALE_SMONDECIMALSEP
);
291 GET_LOCALE_STRING(new_node
->cyfmt
.lpThousandSep
, LOCALE_SMONTHOUSANDSEP
);
292 GET_LOCALE_STRING(new_node
->cyfmt
.lpCurrencySymbol
, LOCALE_SCURRENCY
);
294 /* Date/Time Format info, negative character, etc */
295 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
297 GET_LOCALE_STRING(new_node
->lppszStrings
[i
], NLS_LocaleIndices
[i
]);
299 new_node
->szShortAM
[0] = GetAM(new_node
)[0]; new_node
->szShortAM
[1] = '\0';
300 new_node
->szShortPM
[0] = GetPM(new_node
)[0]; new_node
->szShortPM
[1] = '\0';
302 /* Now add the computed format to the cache */
303 RtlEnterCriticalSection(&NLS_FormatsCS
);
305 /* Search again: We may have raced to add the node */
306 node
= NLS_CachedFormats
;
307 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
312 node
= NLS_CachedFormats
= new_node
; /* Empty list */
315 else if (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
317 node
->next
= new_node
; /* Not in the list, add to end */
322 RtlLeaveCriticalSection(&NLS_FormatsCS
);
326 /* We raced and lost: The node was already added by another thread.
327 * node points to the currently cached node, so free new_node.
329 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
330 HeapFree(GetProcessHeap(), 0, new_node
->lppszStrings
[i
]);
331 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpDecimalSep
);
332 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpThousandSep
);
333 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpDecimalSep
);
334 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpThousandSep
);
335 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpCurrencySymbol
);
336 HeapFree(GetProcessHeap(), 0, new_node
);
342 /**************************************************************************
343 * NLS_IsUnicodeOnlyLcid <internal>
345 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
347 BOOL
NLS_IsUnicodeOnlyLcid(LCID lcid
)
349 lcid
= ConvertDefaultLocale(lcid
);
351 switch (PRIMARYLANGID(lcid
))
363 TRACE("lcid 0x%08lx: langid 0x%4x is Unicode Only\n", lcid
, PRIMARYLANGID(lcid
));
371 * Formatting of dates, times, numbers and currencies.
374 #define IsLiteralMarker(p) (p == '\'')
375 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
376 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
378 /* Only the following flags can be given if a date/time format is specified */
379 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
380 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
381 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
384 /******************************************************************************
385 * NLS_GetDateTimeFormatW <internal>
387 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
390 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
391 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
393 static INT
NLS_GetDateTimeFormatW(LCID lcid
, DWORD dwFlags
,
394 const SYSTEMTIME
* lpTime
, LPCWSTR lpFormat
,
395 LPWSTR lpStr
, INT cchOut
)
397 const NLS_FORMAT_NODE
*node
;
400 INT lastFormatPos
= 0;
401 BOOL bSkipping
= FALSE
; /* Skipping text around marker? */
403 /* Verify our arguments */
404 if ((cchOut
&& !lpStr
) || !(node
= NLS_GetFormats(lcid
, dwFlags
)))
406 NLS_GetDateTimeFormatW_InvalidParameter
:
407 SetLastError(ERROR_INVALID_PARAMETER
);
411 if (dwFlags
& ~(DATE_DATEVARSONLY
|TIME_TIMEVARSONLY
))
414 ((dwFlags
& DATE_DATEVARSONLY
&& dwFlags
& ~DATE_FORMAT_FLAGS
) ||
415 (dwFlags
& TIME_TIMEVARSONLY
&& dwFlags
& ~TIME_FORMAT_FLAGS
)))
417 NLS_GetDateTimeFormatW_InvalidFlags
:
418 SetLastError(ERROR_INVALID_FLAGS
);
422 if (dwFlags
& DATE_DATEVARSONLY
)
424 if ((dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
)) == (DATE_LTRREADING
|DATE_RTLREADING
))
425 goto NLS_GetDateTimeFormatW_InvalidFlags
;
426 else if (dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
))
427 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
429 switch (dwFlags
& (DATE_SHORTDATE
|DATE_LONGDATE
|DATE_YEARMONTH
))
437 goto NLS_GetDateTimeFormatW_InvalidFlags
;
440 goto NLS_GetDateTimeFormatW_InvalidFlags
;
447 /* Use the appropriate default format */
448 if (dwFlags
& DATE_DATEVARSONLY
)
450 if (dwFlags
& DATE_YEARMONTH
)
451 lpFormat
= GetYearMonth(node
);
452 else if (dwFlags
& DATE_LONGDATE
)
453 lpFormat
= GetLongDate(node
);
455 lpFormat
= GetShortDate(node
);
458 lpFormat
= GetTime(node
);
463 GetLocalTime(&st
); /* Default to current time */
468 if (dwFlags
& DATE_DATEVARSONLY
)
472 /* Verify the date and correct the D.O.W. if needed */
473 memset(&st
, 0, sizeof(st
));
474 st
.wYear
= lpTime
->wYear
;
475 st
.wMonth
= lpTime
->wMonth
;
476 st
.wDay
= lpTime
->wDay
;
478 if (st
.wDay
> 31 || st
.wMonth
> 12 || !SystemTimeToFileTime(&st
, &ftTmp
))
479 goto NLS_GetDateTimeFormatW_InvalidParameter
;
481 FileTimeToSystemTime(&ftTmp
, &st
);
485 if (dwFlags
& TIME_TIMEVARSONLY
)
487 /* Verify the time */
488 if (lpTime
->wHour
> 24 || lpTime
->wMinute
> 59 || lpTime
->wSecond
> 59)
489 goto NLS_GetDateTimeFormatW_InvalidParameter
;
493 /* Format the output */
496 if (IsLiteralMarker(*lpFormat
))
498 /* Start of a literal string */
501 /* Loop until the end of the literal marker or end of the string */
504 if (IsLiteralMarker(*lpFormat
))
507 if (!IsLiteralMarker(*lpFormat
))
508 break; /* Terminating literal marker */
512 cchWritten
++; /* Count size only */
513 else if (cchWritten
>= cchOut
)
514 goto NLS_GetDateTimeFormatW_Overrun
;
517 lpStr
[cchWritten
] = *lpFormat
;
523 else if ((dwFlags
& DATE_DATEVARSONLY
&& IsDateFmtChar(*lpFormat
)) ||
524 (dwFlags
& TIME_TIMEVARSONLY
&& IsTimeFmtChar(*lpFormat
)))
527 WCHAR buff
[32], fmtChar
;
528 LPCWSTR szAdd
= NULL
;
530 int count
= 0, dwLen
;
535 while (*lpFormat
== fmtChar
)
546 szAdd
= GetLongDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
548 szAdd
= GetShortDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
551 dwVal
= lpTime
->wDay
;
558 szAdd
= GetLongMonth(node
, lpTime
->wMonth
- 1);
560 szAdd
= GetShortMonth(node
, lpTime
->wMonth
- 1);
563 dwVal
= lpTime
->wMonth
;
572 dwVal
= lpTime
->wYear
;
576 count
= count
> 2 ? 2 : count
;
577 dwVal
= lpTime
->wYear
% 100;
585 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
586 * When it is fixed, this string should be cached in 'node'.
588 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
589 buff
[0] = 'A'; buff
[1] = 'D'; buff
[2] = '\0';
593 buff
[0] = 'g'; buff
[1] = '\0'; /* Add a literal 'g' */
599 if (!(dwFlags
& TIME_FORCE24HOURFORMAT
))
601 count
= count
> 2 ? 2 : count
;
602 dwVal
= lpTime
->wHour
== 0 ? 12 : (lpTime
->wHour
- 1) % 12 + 1;
606 /* .. fall through if we are forced to output in 24 hour format */
609 count
= count
> 2 ? 2 : count
;
610 dwVal
= lpTime
->wHour
;
615 if (dwFlags
& TIME_NOMINUTESORSECONDS
)
617 cchWritten
= lastFormatPos
; /* Skip */
622 count
= count
> 2 ? 2 : count
;
623 dwVal
= lpTime
->wMinute
;
629 if (dwFlags
& (TIME_NOSECONDS
|TIME_NOMINUTESORSECONDS
))
631 cchWritten
= lastFormatPos
; /* Skip */
636 count
= count
> 2 ? 2 : count
;
637 dwVal
= lpTime
->wSecond
;
643 if (dwFlags
& TIME_NOTIMEMARKER
)
645 cchWritten
= lastFormatPos
; /* Skip */
651 szAdd
= lpTime
->wHour
< 12 ? node
->szShortAM
: node
->szShortPM
;
653 szAdd
= lpTime
->wHour
< 12 ? GetAM(node
) : GetPM(node
);
658 if (szAdd
== buff
&& buff
[0] == '\0')
660 /* We have a numeric value to add */
661 sprintf(buffA
, "%.*ld", count
, dwVal
);
662 MultiByteToWideChar(CP_ACP
, 0, buffA
, -1, buff
, sizeof(buff
)/sizeof(WCHAR
));
665 dwLen
= szAdd
? strlenW(szAdd
) : 0;
669 if (cchWritten
+ dwLen
< cchOut
)
670 memcpy(lpStr
+ cchWritten
, szAdd
, dwLen
* sizeof(WCHAR
));
673 memcpy(lpStr
+ cchWritten
, szAdd
, (cchOut
- cchWritten
) * sizeof(WCHAR
));
674 goto NLS_GetDateTimeFormatW_Overrun
;
678 lastFormatPos
= cchWritten
; /* Save position of last output format text */
682 /* Literal character */
684 cchWritten
++; /* Count size only */
685 else if (cchWritten
>= cchOut
)
686 goto NLS_GetDateTimeFormatW_Overrun
;
687 else if (!bSkipping
|| *lpFormat
== ' ')
689 lpStr
[cchWritten
] = *lpFormat
;
696 /* Final string terminator and sanity check */
699 if (cchWritten
>= cchOut
)
700 goto NLS_GetDateTimeFormatW_Overrun
;
702 lpStr
[cchWritten
] = '\0';
704 cchWritten
++; /* Include terminating NUL */
706 TRACE("returning length=%d, ouput='%S'\n", cchWritten
, lpStr
);
709 NLS_GetDateTimeFormatW_Overrun
:
710 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
711 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
715 /******************************************************************************
716 * NLS_GetDateTimeFormatA <internal>
718 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
720 static INT
NLS_GetDateTimeFormatA(LCID lcid
, DWORD dwFlags
,
721 const SYSTEMTIME
* lpTime
,
722 LPCSTR lpFormat
, LPSTR lpStr
, INT cchOut
)
725 WCHAR szFormat
[128], szOut
[128];
728 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
729 lpFormat
, lpStr
, cchOut
);
731 if (NLS_IsUnicodeOnlyLcid(lcid
))
733 GetDateTimeFormatA_InvalidParameter
:
734 SetLastError(ERROR_INVALID_PARAMETER
);
738 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
740 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
742 goto GetDateTimeFormatA_InvalidParameter
;
743 cp
= node
->dwCodePage
;
747 MultiByteToWideChar(cp
, 0, lpFormat
, -1, szFormat
, sizeof(szFormat
)/sizeof(WCHAR
));
749 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
750 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
754 iRet
= NLS_GetDateTimeFormatW(lcid
, dwFlags
, lpTime
, lpFormat
? szFormat
: NULL
,
755 lpStr
? szOut
: NULL
, cchOut
);
760 WideCharToMultiByte(cp
, 0, szOut
, iRet
? -1 : cchOut
, lpStr
, cchOut
, 0, 0);
761 else if (cchOut
&& iRet
)
767 /******************************************************************************
768 * GetDateFormatA [KERNEL32.@]
770 * Format a date for a given locale.
773 * lcid [I] Locale to format for
774 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
775 * lpTime [I] Date to format
776 * lpFormat [I] Format string, or NULL to use the system defaults
777 * lpDateStr [O] Destination for formatted string
778 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
781 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
782 * details returned by GetLocaleInfoA() and modified by dwFlags.
783 * - lpFormat is a string of characters and formatting tokens. Any characters
784 * in the string are copied verbatim to lpDateStr, with tokens being replaced
785 * by the date values they represent.
786 * - The following tokens have special meanings in a date format string:
789 *| d Single digit day of the month (no leading 0)
790 *| dd Double digit day of the month
791 *| ddd Short name for the day of the week
792 *| dddd Long name for the day of the week
793 *| M Single digit month of the year (no leading 0)
794 *| MM Double digit month of the year
795 *| MMM Short name for the month of the year
796 *| MMMM Long name for the month of the year
797 *| y Double digit year number (no leading 0)
798 *| yy Double digit year number
799 *| yyyy Four digit year number
800 *| gg Era string, for example 'AD'.
801 * - To output any literal character that could be misidentified as a token,
802 * enclose it in single quotes.
803 * - The Ascii version of this function fails if lcid is Unicode only.
806 * Success: The number of character written to lpDateStr, or that would
807 * have been written, if cchOut is 0.
808 * Failure: 0. Use GetLastError() to determine the cause.
810 INT WINAPI
GetDateFormatA( LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
811 LPCSTR lpFormat
, LPSTR lpDateStr
, INT cchOut
)
813 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
814 lpFormat
, lpDateStr
, cchOut
);
816 return NLS_GetDateTimeFormatA(lcid
, dwFlags
| DATE_DATEVARSONLY
, lpTime
,
817 lpFormat
, lpDateStr
, cchOut
);
821 /******************************************************************************
822 * GetDateFormatW [KERNEL32.@]
824 * See GetDateFormatA.
826 INT WINAPI
GetDateFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
827 LPCWSTR lpFormat
, LPWSTR lpDateStr
, INT cchOut
)
829 TRACE("(0x%04lx,0x%08lx,%p,%S,%p,%d)\n", lcid
, dwFlags
, lpTime
,
830 lpFormat
, lpDateStr
, cchOut
);
832 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|DATE_DATEVARSONLY
, lpTime
,
833 lpFormat
, lpDateStr
, cchOut
);
836 /******************************************************************************
837 * GetTimeFormatA [KERNEL32.@]
839 * Format a time for a given locale.
842 * lcid [I] Locale to format for
843 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
844 * lpTime [I] Time to format
845 * lpFormat [I] Formatting overrides
846 * lpTimeStr [O] Destination for formatted string
847 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
850 * - If lpFormat is NULL, lpszValue will be formatted according to the format
851 * details returned by GetLocaleInfoA() and modified by dwFlags.
852 * - lpFormat is a string of characters and formatting tokens. Any characters
853 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
854 * by the time values they represent.
855 * - The following tokens have special meanings in a time format string:
858 *| h Hours with no leading zero (12-hour clock)
859 *| hh Hours with full two digits (12-hour clock)
860 *| H Hours with no leading zero (24-hour clock)
861 *| HH Hours with full two digits (24-hour clock)
862 *| m Minutes with no leading zero
863 *| mm Minutes with full two digits
864 *| s Seconds with no leading zero
865 *| ss Seconds with full two digits
866 *| t Short time marker (e.g. "A" or "P")
867 *| tt Long time marker (e.g. "AM", "PM")
868 * - To output any literal character that could be misidentified as a token,
869 * enclose it in single quotes.
870 * - The Ascii version of this function fails if lcid is Unicode only.
873 * Success: The number of character written to lpTimeStr, or that would
874 * have been written, if cchOut is 0.
875 * Failure: 0. Use GetLastError() to determine the cause.
877 INT WINAPI
GetTimeFormatA(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
878 LPCSTR lpFormat
, LPSTR lpTimeStr
, INT cchOut
)
880 TRACE("(0x%04lx,0x%08lx,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
881 lpFormat
, lpTimeStr
, cchOut
);
883 return NLS_GetDateTimeFormatA(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
884 lpFormat
, lpTimeStr
, cchOut
);
887 /******************************************************************************
888 * GetTimeFormatW [KERNEL32.@]
890 * See GetTimeFormatA.
892 INT WINAPI
GetTimeFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
893 LPCWSTR lpFormat
, LPWSTR lpTimeStr
, INT cchOut
)
895 TRACE("(0x%04lx,0x%08lx,%p,%S,%p,%d)\n",lcid
, dwFlags
, lpTime
,
896 lpFormat
, lpTimeStr
, cchOut
);
898 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
899 lpFormat
, lpTimeStr
, cchOut
);
902 /**************************************************************************
903 * GetNumberFormatA (KERNEL32.@)
905 * Format a number string for a given locale.
908 * lcid [I] Locale to format for
909 * dwFlags [I] LOCALE_ flags from "winnls.h"
910 * lpszValue [I] String to format
911 * lpFormat [I] Formatting overrides
912 * lpNumberStr [O] Destination for formatted string
913 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
916 * - lpszValue can contain only '0' - '9', '-' and '.'.
917 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
918 * be formatted according to the format details returned by GetLocaleInfoA().
919 * - This function rounds the number string if the number of decimals exceeds the
920 * locales normal number of decimal places.
921 * - If cchOut is 0, this function does not write to lpNumberStr.
922 * - The Ascii version of this function fails if lcid is Unicode only.
925 * Success: The number of character written to lpNumberStr, or that would
926 * have been written, if cchOut is 0.
927 * Failure: 0. Use GetLastError() to determine the cause.
929 INT WINAPI
GetNumberFormatA(LCID lcid
, DWORD dwFlags
,
930 LPCSTR lpszValue
, const NUMBERFMTA
*lpFormat
,
931 LPSTR lpNumberStr
, int cchOut
)
934 WCHAR szDec
[8], szGrp
[8], szIn
[128], szOut
[128];
936 const NUMBERFMTW
*pfmt
= NULL
;
939 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, lpszValue
,
940 lpFormat
, lpNumberStr
, cchOut
);
942 if (NLS_IsUnicodeOnlyLcid(lcid
))
944 GetNumberFormatA_InvalidParameter
:
945 SetLastError(ERROR_INVALID_PARAMETER
);
949 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
951 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
953 goto GetNumberFormatA_InvalidParameter
;
954 cp
= node
->dwCodePage
;
959 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
961 if (lpFormat
->lpDecimalSep
)
963 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
964 fmt
.lpDecimalSep
= szDec
;
966 if (lpFormat
->lpThousandSep
)
968 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
969 fmt
.lpThousandSep
= szGrp
;
974 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
976 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
977 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
981 iRet
= GetNumberFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
982 lpNumberStr
? szOut
: NULL
, cchOut
);
984 if (szOut
[0] && lpNumberStr
)
985 WideCharToMultiByte(cp
, 0, szOut
, -1, lpNumberStr
, cchOut
, 0, 0);
989 /* Number parsing state flags */
990 #define NF_ISNEGATIVE 0x1 /* '-' found */
991 #define NF_ISREAL 0x2 /* '.' found */
992 #define NF_DIGITS 0x4 /* '0'-'9' found */
993 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
994 #define NF_ROUND 0x10 /* Number needs to be rounded */
996 /* Formatting options for Numbers */
997 #define NLS_NEG_PARENS 0 /* "(1.1)" */
998 #define NLS_NEG_LEFT 1 /* "-1.1" */
999 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
1000 #define NLS_NEG_RIGHT 3 /* "1.1-" */
1001 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1003 /**************************************************************************
1004 * GetNumberFormatW (KERNEL32.@)
1006 * See GetNumberFormatA.
1008 INT WINAPI
GetNumberFormatW(LCID lcid
, DWORD dwFlags
,
1009 LPCWSTR lpszValue
, const NUMBERFMTW
*lpFormat
,
1010 LPWSTR lpNumberStr
, int cchOut
)
1012 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1014 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
;
1015 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0;
1016 DWORD dwLeadingZeros
= 0;
1019 TRACE("(0x%04lx,0x%08lx,%S,%p,%p,%d)\n", lcid
, dwFlags
, lpszValue
,
1020 lpFormat
, lpNumberStr
, cchOut
);
1022 if(NLS_isSystemLocale(lcid
))
1024 lcid
= NLS_getDefaultLocale(lcid
);
1026 else if(!IsValidLocale(lcid
, 0))
1028 SetLastError(ERROR_INVALID_PARAMETER
);
1032 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpNumberStr
) ||
1033 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
)))
1035 GetNumberFormatW_Error
:
1036 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1042 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1045 goto GetNumberFormatW_Error
;
1046 lpFormat
= &node
->fmt
;
1047 lpszNegStart
= lpszNeg
= GetNegative(node
);
1051 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1052 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1053 lpszNegStart
= lpszNeg
= szNegBuff
;
1055 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1057 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1059 /* Format the number backwards into a temporary buffer */
1064 /* Check the number for validity */
1067 if (*szSrc
== '0' && !(dwState
& NF_DIGITS
))
1071 else if ((*szSrc
>= '1' && *szSrc
<= '9') || (*szSrc
== '0' && (dwState
& NF_DIGITS
)))
1073 dwState
|= NF_DIGITS
;
1074 if (dwState
& NF_ISREAL
)
1077 else if (*szSrc
== '-')
1080 goto GetNumberFormatW_Error
; /* '-' not first character */
1081 dwState
|= NF_ISNEGATIVE
;
1083 else if (*szSrc
== '.')
1085 if (dwState
& NF_ISREAL
)
1086 goto GetNumberFormatW_Error
; /* More than one '.' */
1087 dwState
|= NF_ISREAL
;
1090 goto GetNumberFormatW_Error
; /* Invalid char */
1093 szSrc
--; /* Point to last character */
1095 if (!(dwState
& NF_DIGITS
))
1096 goto GetNumberFormatW_Error
; /* No digits */
1098 /* Add any trailing negative sign */
1099 if (dwState
& NF_ISNEGATIVE
)
1101 switch (lpFormat
->NegativeOrder
)
1103 case NLS_NEG_PARENS
:
1107 case NLS_NEG_RIGHT_SPACE
:
1108 while (lpszNeg
>= lpszNegStart
)
1109 *szOut
-- = *lpszNeg
--;
1110 if (lpFormat
->NegativeOrder
== NLS_NEG_RIGHT_SPACE
)
1116 /* Copy all digits up to the decimal point */
1117 if (!lpFormat
->NumDigits
)
1119 if (dwState
& NF_ISREAL
)
1121 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1123 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1124 dwState
|= NF_ROUND
;
1126 dwState
&= ~NF_ROUND
;
1134 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1136 if (dwDecimals
<= lpFormat
->NumDigits
)
1138 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1139 while (dwDecimals
--)
1140 *szOut
-- = '0'; /* Pad to correct number of dp */
1144 dwDecimals
-= lpFormat
->NumDigits
;
1145 /* Skip excess decimals, and determine if we have to round the number */
1146 while (dwDecimals
--)
1148 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1149 dwState
|= NF_ROUND
;
1151 dwState
&= ~NF_ROUND
;
1156 if (dwState
& NF_ISREAL
)
1158 while (*szSrc
!= '.')
1160 if (dwState
& NF_ROUND
)
1163 *szOut
-- = '0'; /* continue rounding */
1166 dwState
&= ~NF_ROUND
;
1167 *szOut
-- = (*szSrc
)+1;
1172 *szOut
-- = *szSrc
--; /* Write existing decimals */
1174 szSrc
--; /* Skip '.' */
1177 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1178 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1181 dwGroupCount
= lpFormat
->Grouping
== 32 ? 3 : lpFormat
->Grouping
;
1183 /* Write the remaining whole number digits, including grouping chars */
1184 while (szSrc
>= (lpszValue
+ dwLeadingZeros
) && *szSrc
>= '0' && *szSrc
<= '9')
1186 if (dwState
& NF_ROUND
)
1189 *szOut
-- = '0'; /* continue rounding */
1192 dwState
&= ~NF_ROUND
;
1193 *szOut
-- = (*szSrc
)+1;
1198 *szOut
-- = *szSrc
--;
1200 dwState
|= NF_DIGITS_OUT
;
1201 dwCurrentGroupCount
++;
1202 if (szSrc
>= (lpszValue
+ dwLeadingZeros
) && dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1204 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1206 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1207 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1209 dwCurrentGroupCount
= 0;
1210 if (lpFormat
->Grouping
== 32)
1211 dwGroupCount
= 2; /* Indic grouping: 3 then 2 */
1214 if (dwState
& NF_ROUND
)
1216 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1218 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1219 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1221 /* Add any leading negative sign */
1222 if (dwState
& NF_ISNEGATIVE
)
1224 switch (lpFormat
->NegativeOrder
)
1226 case NLS_NEG_PARENS
:
1229 case NLS_NEG_LEFT_SPACE
:
1233 while (lpszNeg
>= lpszNegStart
)
1234 *szOut
-- = *lpszNeg
--;
1240 iRet
= strlenW(szOut
) + 1;
1244 memcpy(lpNumberStr
, szOut
, iRet
* sizeof(WCHAR
));
1247 memcpy(lpNumberStr
, szOut
, cchOut
* sizeof(WCHAR
));
1248 lpNumberStr
[cchOut
- 1] = '\0';
1249 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1256 /**************************************************************************
1257 * GetCurrencyFormatA (KERNEL32.@)
1259 * Format a currency string for a given locale.
1262 * lcid [I] Locale to format for
1263 * dwFlags [I] LOCALE_ flags from "winnls.h"
1264 * lpszValue [I] String to format
1265 * lpFormat [I] Formatting overrides
1266 * lpCurrencyStr [O] Destination for formatted string
1267 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1270 * - lpszValue can contain only '0' - '9', '-' and '.'.
1271 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1272 * be formatted according to the format details returned by GetLocaleInfoA().
1273 * - This function rounds the currency if the number of decimals exceeds the
1274 * locales number of currency decimal places.
1275 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1276 * - The Ascii version of this function fails if lcid is Unicode only.
1279 * Success: The number of character written to lpNumberStr, or that would
1280 * have been written, if cchOut is 0.
1281 * Failure: 0. Use GetLastError() to determine the cause.
1283 INT WINAPI
GetCurrencyFormatA(LCID lcid
, DWORD dwFlags
,
1284 LPCSTR lpszValue
, const CURRENCYFMTA
*lpFormat
,
1285 LPSTR lpCurrencyStr
, int cchOut
)
1288 WCHAR szDec
[8], szGrp
[8], szCy
[8], szIn
[128], szOut
[128];
1290 const CURRENCYFMTW
*pfmt
= NULL
;
1293 TRACE("(0x%04lx,0x%08lx,%s,%p,%p,%d)\n", lcid
, dwFlags
, lpszValue
,
1294 lpFormat
, lpCurrencyStr
, cchOut
);
1296 if (NLS_IsUnicodeOnlyLcid(lcid
))
1298 GetCurrencyFormatA_InvalidParameter
:
1299 SetLastError(ERROR_INVALID_PARAMETER
);
1303 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1305 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1307 goto GetCurrencyFormatA_InvalidParameter
;
1308 cp
= node
->dwCodePage
;
1313 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1315 if (lpFormat
->lpDecimalSep
)
1317 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
1318 fmt
.lpDecimalSep
= szDec
;
1320 if (lpFormat
->lpThousandSep
)
1322 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
1323 fmt
.lpThousandSep
= szGrp
;
1325 if (lpFormat
->lpCurrencySymbol
)
1327 MultiByteToWideChar(cp
, 0, lpFormat
->lpCurrencySymbol
, -1, szCy
, sizeof(szCy
)/sizeof(WCHAR
));
1328 fmt
.lpCurrencySymbol
= szCy
;
1333 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1335 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1336 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1340 iRet
= GetCurrencyFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1341 lpCurrencyStr
? szOut
: NULL
, cchOut
);
1343 if (szOut
[0] && lpCurrencyStr
)
1344 WideCharToMultiByte(cp
, 0, szOut
, -1, lpCurrencyStr
, cchOut
, 0, 0);
1348 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1349 #define CF_PARENS 0x1 /* Parentheses */
1350 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1351 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1352 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1353 #define CF_CY_LEFT 0x10 /* '$' to the left */
1354 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1355 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1357 /**************************************************************************
1358 * GetCurrencyFormatW (KERNEL32.@)
1360 * See GetCurrencyFormatA.
1362 INT WINAPI
GetCurrencyFormatW(LCID lcid
, DWORD dwFlags
,
1363 LPCWSTR lpszValue
, const CURRENCYFMTW
*lpFormat
,
1364 LPWSTR lpCurrencyStr
, int cchOut
)
1366 static const BYTE NLS_NegCyFormats
[16] =
1368 CF_PARENS
|CF_CY_LEFT
, /* ($1.1) */
1369 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
, /* -$1.1 */
1370 CF_MINUS_LEFT
|CF_CY_LEFT
, /* $-1.1 */
1371 CF_MINUS_RIGHT
|CF_CY_LEFT
, /* $1.1- */
1372 CF_PARENS
|CF_CY_RIGHT
, /* (1.1$) */
1373 CF_MINUS_LEFT
|CF_CY_RIGHT
, /* -1.1$ */
1374 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
, /* 1.1-$ */
1375 CF_MINUS_RIGHT
|CF_CY_RIGHT
, /* 1.1$- */
1376 CF_MINUS_LEFT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* -1.1 $ */
1377 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
|CF_CY_SPACE
, /* -$ 1.1 */
1378 CF_MINUS_RIGHT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $- */
1379 CF_MINUS_RIGHT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1- */
1380 CF_MINUS_LEFT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ -1.1 */
1381 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1- $ */
1382 CF_PARENS
|CF_CY_LEFT
|CF_CY_SPACE
, /* ($ 1.1) */
1383 CF_PARENS
|CF_CY_RIGHT
|CF_CY_SPACE
, /* (1.1 $) */
1385 static const BYTE NLS_PosCyFormats
[4] =
1387 CF_CY_LEFT
, /* $1.1 */
1388 CF_CY_RIGHT
, /* 1.1$ */
1389 CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1 */
1390 CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $ */
1392 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1394 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
, *lpszCy
, *lpszCyStart
;
1395 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0, dwFmt
;
1398 DPRINT1("GetCurrencyFormatW(0x%04lx,0x%08lx,%S,%p,%p,%d)\n",
1406 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpCurrencyStr
) ||
1407 !IsValidLocale(lcid
, 0) ||
1408 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
||
1409 !lpFormat
->lpCurrencySymbol
|| lpFormat
->NegativeOrder
> 15 ||
1410 lpFormat
->PositiveOrder
> 3)))
1412 GetCurrencyFormatW_Error
:
1413 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1419 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1422 goto GetCurrencyFormatW_Error
;
1423 lpFormat
= &node
->cyfmt
;
1424 lpszNegStart
= lpszNeg
= GetNegative(node
);
1428 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1429 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1430 lpszNegStart
= lpszNeg
= szNegBuff
;
1432 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1434 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1435 lpszCyStart
= lpFormat
->lpCurrencySymbol
;
1436 lpszCy
= lpszCyStart
+ strlenW(lpszCyStart
) - 1;
1438 /* Format the currency backwards into a temporary buffer */
1443 /* Check the number for validity */
1446 if (*szSrc
>= '0' && *szSrc
<= '9')
1448 dwState
|= NF_DIGITS
;
1449 if (dwState
& NF_ISREAL
)
1452 else if (*szSrc
== '-')
1455 goto GetCurrencyFormatW_Error
; /* '-' not first character */
1456 dwState
|= NF_ISNEGATIVE
;
1458 else if (*szSrc
== '.')
1460 if (dwState
& NF_ISREAL
)
1461 goto GetCurrencyFormatW_Error
; /* More than one '.' */
1462 dwState
|= NF_ISREAL
;
1465 goto GetCurrencyFormatW_Error
; /* Invalid char */
1468 szSrc
--; /* Point to last character */
1470 if (!(dwState
& NF_DIGITS
))
1471 goto GetCurrencyFormatW_Error
; /* No digits */
1473 if (dwState
& NF_ISNEGATIVE
)
1474 dwFmt
= NLS_NegCyFormats
[lpFormat
->NegativeOrder
];
1476 dwFmt
= NLS_PosCyFormats
[lpFormat
->PositiveOrder
];
1478 /* Add any trailing negative or currency signs */
1479 if (dwFmt
& CF_PARENS
)
1482 while (dwFmt
& (CF_MINUS_RIGHT
|CF_CY_RIGHT
))
1484 switch (dwFmt
& (CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
))
1486 case CF_MINUS_RIGHT
:
1487 case CF_MINUS_RIGHT
|CF_CY_RIGHT
:
1488 while (lpszNeg
>= lpszNegStart
)
1489 *szOut
-- = *lpszNeg
--;
1490 dwFmt
&= ~CF_MINUS_RIGHT
;
1494 case CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1495 case CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1496 while (lpszCy
>= lpszCyStart
)
1497 *szOut
-- = *lpszCy
--;
1498 if (dwFmt
& CF_CY_SPACE
)
1500 dwFmt
&= ~(CF_CY_RIGHT
|CF_MINUS_BEFORE
);
1505 /* Copy all digits up to the decimal point */
1506 if (!lpFormat
->NumDigits
)
1508 if (dwState
& NF_ISREAL
)
1510 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1512 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1513 dwState
|= NF_ROUND
;
1515 dwState
&= ~NF_ROUND
;
1523 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1525 if (dwDecimals
<= lpFormat
->NumDigits
)
1527 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1528 while (dwDecimals
--)
1529 *szOut
-- = '0'; /* Pad to correct number of dp */
1533 dwDecimals
-= lpFormat
->NumDigits
;
1534 /* Skip excess decimals, and determine if we have to round the number */
1535 while (dwDecimals
--)
1537 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1538 dwState
|= NF_ROUND
;
1540 dwState
&= ~NF_ROUND
;
1545 if (dwState
& NF_ISREAL
)
1547 while (*szSrc
!= '.')
1549 if (dwState
& NF_ROUND
)
1552 *szOut
-- = '0'; /* continue rounding */
1555 dwState
&= ~NF_ROUND
;
1556 *szOut
-- = (*szSrc
)+1;
1561 *szOut
-- = *szSrc
--; /* Write existing decimals */
1563 szSrc
--; /* Skip '.' */
1565 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1566 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1569 dwGroupCount
= lpFormat
->Grouping
;
1571 /* Write the remaining whole number digits, including grouping chars */
1572 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1574 if (dwState
& NF_ROUND
)
1577 *szOut
-- = '0'; /* continue rounding */
1580 dwState
&= ~NF_ROUND
;
1581 *szOut
-- = (*szSrc
)+1;
1586 *szOut
-- = *szSrc
--;
1588 dwState
|= NF_DIGITS_OUT
;
1589 dwCurrentGroupCount
++;
1590 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1592 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1594 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1595 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1597 dwCurrentGroupCount
= 0;
1600 if (dwState
& NF_ROUND
)
1601 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1602 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1603 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1605 /* Add any leading negative or currency sign */
1606 while (dwFmt
& (CF_MINUS_LEFT
|CF_CY_LEFT
))
1608 switch (dwFmt
& (CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
))
1611 case CF_MINUS_LEFT
|CF_CY_LEFT
:
1612 while (lpszNeg
>= lpszNegStart
)
1613 *szOut
-- = *lpszNeg
--;
1614 dwFmt
&= ~CF_MINUS_LEFT
;
1618 case CF_CY_LEFT
|CF_MINUS_BEFORE
:
1619 case CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
:
1620 if (dwFmt
& CF_CY_SPACE
)
1622 while (lpszCy
>= lpszCyStart
)
1623 *szOut
-- = *lpszCy
--;
1624 dwFmt
&= ~(CF_CY_LEFT
|CF_MINUS_BEFORE
);
1628 if (dwFmt
& CF_PARENS
)
1632 iRet
= strlenW(szOut
) + 1;
1636 memcpy(lpCurrencyStr
, szOut
, iRet
* sizeof(WCHAR
));
1639 memcpy(lpCurrencyStr
, szOut
, cchOut
* sizeof(WCHAR
));
1640 lpCurrencyStr
[cchOut
- 1] = '\0';
1641 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1648 /* FIXME: Everything below here needs to move somewhere else along with the
1649 * other EnumXXX functions, when a method for storing resources for
1650 * alternate calendars is determined.
1653 /**************************************************************************
1654 * EnumDateFormatsA (KERNEL32.@)
1656 BOOL WINAPI
EnumDateFormatsA(DATEFMT_ENUMPROCA lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1660 if (!lpDateFmtEnumProc
)
1662 SetLastError(ERROR_INVALID_PARAMETER
);
1666 switch (dwFlags
& ~LOCALE_USE_CP_ACP
)
1669 case DATE_SHORTDATE
:
1670 if (GetLocaleInfoA(Locale
, LOCALE_SSHORTDATE
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1671 lpDateFmtEnumProc(buf
);
1675 if (GetLocaleInfoA(Locale
, LOCALE_SLONGDATE
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1676 lpDateFmtEnumProc(buf
);
1679 case DATE_YEARMONTH
:
1680 if (GetLocaleInfoA(Locale
, LOCALE_SYEARMONTH
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1681 lpDateFmtEnumProc(buf
);
1685 FIXME("Unknown date format (%d)\n", dwFlags
);
1686 SetLastError(ERROR_INVALID_PARAMETER
);
1692 /**************************************************************************
1693 * EnumDateFormatsW (KERNEL32.@)
1695 BOOL WINAPI
EnumDateFormatsW(DATEFMT_ENUMPROCW lpDateFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1699 if (!lpDateFmtEnumProc
)
1701 SetLastError(ERROR_INVALID_PARAMETER
);
1705 switch (dwFlags
& ~LOCALE_USE_CP_ACP
)
1708 case DATE_SHORTDATE
:
1709 if (GetLocaleInfoW(Locale
, LOCALE_SSHORTDATE
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1710 lpDateFmtEnumProc(buf
);
1714 if (GetLocaleInfoW(Locale
, LOCALE_SLONGDATE
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1715 lpDateFmtEnumProc(buf
);
1718 case DATE_YEARMONTH
:
1719 if (GetLocaleInfoW(Locale
, LOCALE_SYEARMONTH
| (dwFlags
& LOCALE_USE_CP_ACP
), buf
, 256))
1720 lpDateFmtEnumProc(buf
);
1724 FIXME("Unknown date format (%d)\n", dwFlags
);
1725 SetLastError(ERROR_INVALID_PARAMETER
);
1731 /**************************************************************************
1732 * EnumTimeFormatsA (KERNEL32.@)
1734 BOOL WINAPI
EnumTimeFormatsA( TIMEFMT_ENUMPROCA lpTimeFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1736 LCID Loc
= GetUserDefaultLCID();
1737 if(!lpTimeFmtEnumProc
)
1739 SetLastError(ERROR_INVALID_PARAMETER
);
1744 FIXME("Unknown time format (%ld)\n", dwFlags
);
1749 case 0x00000407: /* (Loc,"de_DE") */
1751 if(!(*lpTimeFmtEnumProc
)("HH.mm")) return TRUE
;
1752 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1753 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1754 if(!(*lpTimeFmtEnumProc
)("H.mm")) return TRUE
;
1755 if(!(*lpTimeFmtEnumProc
)("H.mm'Uhr'")) return TRUE
;
1759 case 0x0000040c: /* (Loc,"fr_FR") */
1760 case 0x00000c0c: /* (Loc,"fr_CA") */
1762 if(!(*lpTimeFmtEnumProc
)("H:mm")) return TRUE
;
1763 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1764 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1765 if(!(*lpTimeFmtEnumProc
)("HH.mm")) return TRUE
;
1766 if(!(*lpTimeFmtEnumProc
)("HH'h'mm")) return TRUE
;
1770 case 0x00000809: /* (Loc,"en_UK") */
1771 case 0x00000c09: /* (Loc,"en_AU") */
1772 case 0x00001409: /* (Loc,"en_NZ") */
1773 case 0x00001809: /* (Loc,"en_IE") */
1775 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1776 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1777 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1781 case 0x00001c09: /* (Loc,"en_ZA") */
1782 case 0x00002809: /* (Loc,"en_BZ") */
1783 case 0x00002c09: /* (Loc,"en_TT") */
1785 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1786 if(!(*lpTimeFmtEnumProc
)("hh:mm:ss tt")) return TRUE
;
1790 default: /* default to US style "en_US" */
1792 if(!(*lpTimeFmtEnumProc
)("h:mm:ss tt")) return TRUE
;
1793 if(!(*lpTimeFmtEnumProc
)("hh:mm:ss tt")) return TRUE
;
1794 if(!(*lpTimeFmtEnumProc
)("H:mm:ss")) return TRUE
;
1795 if(!(*lpTimeFmtEnumProc
)("HH:mm:ss")) return TRUE
;
1801 /**************************************************************************
1802 * EnumTimeFormatsW (KERNEL32.@)
1804 BOOL WINAPI
EnumTimeFormatsW( TIMEFMT_ENUMPROCW lpTimeFmtEnumProc
, LCID Locale
, DWORD dwFlags
)
1806 LCID Loc
= GetUserDefaultLCID();
1807 if(!lpTimeFmtEnumProc
)
1809 SetLastError(ERROR_INVALID_PARAMETER
);
1814 FIXME("Unknown time format (%ld)\n", dwFlags
);
1819 case 0x00000407: /* (Loc,"de_DE") */
1821 if(!(*lpTimeFmtEnumProc
)(L
"HH.mm")) return TRUE
;
1822 if(!(*lpTimeFmtEnumProc
)(L
"HH:mm:ss")) return TRUE
;
1823 if(!(*lpTimeFmtEnumProc
)(L
"H:mm:ss")) return TRUE
;
1824 if(!(*lpTimeFmtEnumProc
)(L
"H.mm")) return TRUE
;
1825 if(!(*lpTimeFmtEnumProc
)(L
"H.mm'Uhr'")) return TRUE
;
1829 case 0x0000040c: /* (Loc,"fr_FR") */
1830 case 0x00000c0c: /* (Loc,"fr_CA") */
1832 if(!(*lpTimeFmtEnumProc
)(L
"H:mm")) return TRUE
;
1833 if(!(*lpTimeFmtEnumProc
)(L
"HH:mm:ss")) return TRUE
;
1834 if(!(*lpTimeFmtEnumProc
)(L
"H:mm:ss")) return TRUE
;
1835 if(!(*lpTimeFmtEnumProc
)(L
"HH.mm")) return TRUE
;
1836 if(!(*lpTimeFmtEnumProc
)(L
"HH'h'mm")) return TRUE
;
1840 case 0x00000809: /* (Loc,"en_UK") */
1841 case 0x00000c09: /* (Loc,"en_AU") */
1842 case 0x00001409: /* (Loc,"en_NZ") */
1843 case 0x00001809: /* (Loc,"en_IE") */
1845 if(!(*lpTimeFmtEnumProc
)(L
"h:mm:ss tt")) return TRUE
;
1846 if(!(*lpTimeFmtEnumProc
)(L
"HH:mm:ss")) return TRUE
;
1847 if(!(*lpTimeFmtEnumProc
)(L
"H:mm:ss")) return TRUE
;
1851 case 0x00001c09: /* (Loc,"en_ZA") */
1852 case 0x00002809: /* (Loc,"en_BZ") */
1853 case 0x00002c09: /* (Loc,"en_TT") */
1855 if(!(*lpTimeFmtEnumProc
)(L
"h:mm:ss tt")) return TRUE
;
1856 if(!(*lpTimeFmtEnumProc
)(L
"hh:mm:ss tt")) return TRUE
;
1860 default: /* default to US style "en_US" */
1862 if(!(*lpTimeFmtEnumProc
)(L
"h:mm:ss tt")) return TRUE
;
1863 if(!(*lpTimeFmtEnumProc
)(L
"hh:mm:ss tt")) return TRUE
;
1864 if(!(*lpTimeFmtEnumProc
)(L
"H:mm:ss")) return TRUE
;
1865 if(!(*lpTimeFmtEnumProc
)(L
"HH:mm:ss")) return TRUE
;
1871 /******************************************************************************
1872 * NLS_EnumCalendarInfoAW <internal>
1873 * Enumerates calendar information for a specified locale.
1876 * calinfoproc [I] Pointer to the callback
1877 * locale [I] The locale for which to retrieve calendar information.
1878 * This parameter can be a locale identifier created by the
1879 * MAKELCID macro, or one of the following values:
1880 * LOCALE_SYSTEM_DEFAULT
1881 * Use the default system locale.
1882 * LOCALE_USER_DEFAULT
1883 * Use the default user locale.
1884 * calendar [I] The calendar for which information is requested, or
1885 * ENUM_ALL_CALENDARS.
1886 * caltype [I] The type of calendar information to be returned. Note
1887 * that only one CALTYPE value can be specified per call
1888 * of this function, except where noted.
1889 * unicode [I] Specifies if the callback expects a unicode string.
1890 * ex [I] Specifies if the callback needs the calendar identifier.
1894 * Failure: FALSE. Use GetLastError() to determine the cause.
1897 * When the ANSI version of this function is used with a Unicode-only LCID,
1898 * the call can succeed because the system uses the system code page.
1899 * However, characters that are undefined in the system code page appear
1900 * in the string as a question mark (?).
1903 * The above note should be respected by GetCalendarInfoA.
1905 static BOOL
NLS_EnumCalendarInfoAW(void *calinfoproc
, LCID locale
,
1906 CALID calendar
, CALTYPE caltype
, BOOL unicode
, BOOL ex
)
1908 WCHAR
*buf
, *opt
= NULL
, *iter
= NULL
;
1910 int bufSz
= 200; /* the size of the buffer */
1912 if (calinfoproc
== NULL
)
1914 SetLastError(ERROR_INVALID_PARAMETER
);
1918 buf
= HeapAlloc(GetProcessHeap(), 0, bufSz
);
1921 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1925 if (calendar
== ENUM_ALL_CALENDARS
)
1927 int optSz
= GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, NULL
, 0);
1930 opt
= HeapAlloc(GetProcessHeap(), 0, optSz
* sizeof(WCHAR
));
1933 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1934 goto NLS_EnumCalendarInfoAW_Cleanup
;
1936 if (GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, opt
, optSz
))
1939 calendar
= NLS_GetLocaleNumber(locale
, LOCALE_ICALENDARTYPE
);
1942 while (TRUE
) /* loop through calendars */
1944 do /* loop until there's no error */
1947 ret
= GetCalendarInfoW(locale
, calendar
, caltype
, buf
, bufSz
/ sizeof(WCHAR
), NULL
);
1948 else ret
= GetCalendarInfoA(locale
, calendar
, caltype
, (CHAR
*)buf
, bufSz
/ sizeof(CHAR
), NULL
);
1952 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1953 { /* so resize it */
1956 newSz
= GetCalendarInfoW(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(WCHAR
);
1957 else newSz
= GetCalendarInfoA(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(CHAR
);
1960 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz
, newSz
);
1961 goto NLS_EnumCalendarInfoAW_Cleanup
;
1964 WARN("Buffer too small; resizing to %d bytes.\n", bufSz
);
1965 buf
= HeapReAlloc(GetProcessHeap(), 0, buf
, bufSz
);
1967 goto NLS_EnumCalendarInfoAW_Cleanup
;
1968 } else goto NLS_EnumCalendarInfoAW_Cleanup
;
1972 /* Here we are. We pass the buffer to the correct version of
1973 * the callback. Because it's not the same number of params,
1974 * we must check for Ex, but we don't care about Unicode
1975 * because the buffer is already in the correct format.
1978 ret
= ((CALINFO_ENUMPROCEXW
)calinfoproc
)(buf
, calendar
);
1980 ret
= ((CALINFO_ENUMPROCW
)calinfoproc
)(buf
);
1982 if (!ret
) { /* the callback told to stop */
1987 if ((iter
== NULL
) || (*iter
== 0)) /* no more calendars */
1991 while ((*iter
>= '0') && (*iter
<= '9'))
1992 calendar
= calendar
* 10 + *iter
++ - '0';
1996 SetLastError(ERROR_BADDB
);
2002 NLS_EnumCalendarInfoAW_Cleanup
:
2003 HeapFree(GetProcessHeap(), 0, opt
);
2004 HeapFree(GetProcessHeap(), 0, buf
);
2008 /******************************************************************************
2009 * EnumCalendarInfoA [KERNEL32.@]
2011 BOOL WINAPI
EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc
,LCID locale
,
2012 CALID calendar
,CALTYPE caltype
)
2014 FIXME("(%p,0x%04lx,0x%08lx,0x%08lx),stub!\n",calinfoproc
,locale
,calendar
,caltype
);
2015 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, FALSE
);
2018 /******************************************************************************
2019 * EnumCalendarInfoW [KERNEL32.@]
2021 * See EnumCalendarInfoAW.
2023 BOOL WINAPI
EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc
,LCID locale
,
2024 CALID calendar
,CALTYPE caltype
)
2026 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2027 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, FALSE
);
2030 /******************************************************************************
2031 * EnumCalendarInfoExA [KERNEL32.@]
2033 * See EnumCalendarInfoAW.
2035 BOOL WINAPI
EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc
,LCID locale
,
2036 CALID calendar
,CALTYPE caltype
)
2038 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2039 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, TRUE
);
2042 /******************************************************************************
2043 * EnumCalendarInfoExW [KERNEL32.@]
2045 * See EnumCalendarInfoAW.
2047 BOOL WINAPI
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc
,LCID locale
,
2048 CALID calendar
,CALTYPE caltype
)
2050 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2051 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, TRUE
);