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
8 * Copyright 2005 Dmitry Timoshkov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 //#include "wine/port.h"
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
39 #define CRITICAL_SECTION RTL_CRITICAL_SECTION
40 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG
41 #define CALINFO_MAX_YEAR 2029
43 #define HeapAlloc RtlAllocateHeap
44 #define HeapReAlloc RtlReAllocateHeap
45 #define HeapFree RtlFreeHeap
47 WINE_DEFAULT_DEBUG_CHANNEL(nls
);
49 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
50 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
52 /* Since calculating the formatting data for each locale is time-consuming,
53 * we get the format data for each locale only once and cache it in memory.
54 * We cache both the system default and user overridden data, after converting
55 * them into the formats that the functions here expect. Since these functions
56 * will typically be called with only a small number of the total locales
57 * installed, the memory overhead is minimal while the speedup is significant.
59 * Our cache takes the form of a singly linked list, whose node is below:
61 #define NLS_NUM_CACHED_STRINGS 57
63 typedef struct _NLS_FORMAT_NODE
65 LCID lcid
; /* Locale Id */
66 DWORD dwFlags
; /* 0 or LOCALE_NOUSEROVERRIDE */
67 DWORD dwCodePage
; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
68 NUMBERFMTW fmt
; /* Default format for numbers */
69 CURRENCYFMTW cyfmt
; /* Default format for currencies */
70 LPWSTR lppszStrings
[NLS_NUM_CACHED_STRINGS
]; /* Default formats,day/month names */
71 WCHAR szShortAM
[2]; /* Short 'AM' marker */
72 WCHAR szShortPM
[2]; /* Short 'PM' marker */
73 struct _NLS_FORMAT_NODE
*next
;
76 /* Macros to get particular data strings from a format node */
77 #define GetNegative(fmt) fmt->lppszStrings[0]
78 #define GetLongDate(fmt) fmt->lppszStrings[1]
79 #define GetShortDate(fmt) fmt->lppszStrings[2]
80 #define GetTime(fmt) fmt->lppszStrings[3]
81 #define GetAM(fmt) fmt->lppszStrings[54]
82 #define GetPM(fmt) fmt->lppszStrings[55]
83 #define GetYearMonth(fmt) fmt->lppszStrings[56]
85 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
86 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
87 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
88 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
89 #define GetShortMonth(fmt,mth) fmt->lppszStrings[42 + mth]
91 /* Write access to the cache is protected by this critical section */
92 static CRITICAL_SECTION NLS_FormatsCS
;
93 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug
=
96 { &NLS_FormatsCS_debug
.ProcessLocksList
,
97 &NLS_FormatsCS_debug
.ProcessLocksList
},
100 static CRITICAL_SECTION NLS_FormatsCS
= { &NLS_FormatsCS_debug
, -1, 0, 0, 0, 0 };
102 /**************************************************************************
103 * NLS_GetLocaleNumber <internal>
105 * Get a numeric locale format value.
107 static DWORD
NLS_GetLocaleNumber(LCID lcid
, DWORD dwFlags
)
113 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
115 if (szBuff
[0] && szBuff
[1] == ';' && szBuff
[2] != '0')
116 dwVal
= (szBuff
[0] - '0') * 10 + (szBuff
[2] - '0');
119 const WCHAR
* iter
= szBuff
;
121 while(*iter
>= '0' && *iter
<= '9')
122 dwVal
= dwVal
* 10 + (*iter
++ - '0');
127 /**************************************************************************
128 * NLS_GetLocaleString <internal>
130 * Get a string locale format value.
132 static WCHAR
* NLS_GetLocaleString(LCID lcid
, DWORD dwFlags
)
134 WCHAR szBuff
[80], *str
;
138 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
139 dwLen
= strlenW(szBuff
) + 1;
140 str
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
142 memcpy(str
, szBuff
, dwLen
* sizeof(WCHAR
));
146 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
147 TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
149 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
150 TRACE( #type ": %s\n", debugstr_w(str))
152 /**************************************************************************
153 * NLS_GetFormats <internal>
155 * Calculate (and cache) the number formats for a locale.
157 static const NLS_FORMAT_NODE
*NLS_GetFormats(LCID lcid
, DWORD dwFlags
)
159 /* GetLocaleInfo() identifiers for cached formatting strings */
160 static const LCTYPE NLS_LocaleIndices
[] = {
161 LOCALE_SNEGATIVESIGN
,
162 LOCALE_SLONGDATE
, LOCALE_SSHORTDATE
,
164 LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
165 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
, LOCALE_SDAYNAME7
,
166 LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
, LOCALE_SABBREVDAYNAME3
,
167 LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
, LOCALE_SABBREVDAYNAME6
,
168 LOCALE_SABBREVDAYNAME7
,
169 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
,
170 LOCALE_SMONTHNAME4
, LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
,
171 LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
, LOCALE_SMONTHNAME9
,
172 LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
173 LOCALE_SMONTHNAME1
| LOCALE_RETURN_GENITIVE_NAMES
,
174 LOCALE_SMONTHNAME2
| LOCALE_RETURN_GENITIVE_NAMES
,
175 LOCALE_SMONTHNAME3
| LOCALE_RETURN_GENITIVE_NAMES
,
176 LOCALE_SMONTHNAME4
| LOCALE_RETURN_GENITIVE_NAMES
,
177 LOCALE_SMONTHNAME5
| LOCALE_RETURN_GENITIVE_NAMES
,
178 LOCALE_SMONTHNAME6
| LOCALE_RETURN_GENITIVE_NAMES
,
179 LOCALE_SMONTHNAME7
| LOCALE_RETURN_GENITIVE_NAMES
,
180 LOCALE_SMONTHNAME8
| LOCALE_RETURN_GENITIVE_NAMES
,
181 LOCALE_SMONTHNAME9
| LOCALE_RETURN_GENITIVE_NAMES
,
182 LOCALE_SMONTHNAME10
| LOCALE_RETURN_GENITIVE_NAMES
,
183 LOCALE_SMONTHNAME11
| LOCALE_RETURN_GENITIVE_NAMES
,
184 LOCALE_SMONTHNAME12
| LOCALE_RETURN_GENITIVE_NAMES
,
185 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
186 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
187 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
188 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
189 LOCALE_S1159
, LOCALE_S2359
,
192 static NLS_FORMAT_NODE
*NLS_CachedFormats
= NULL
;
193 NLS_FORMAT_NODE
*node
= NLS_CachedFormats
;
195 dwFlags
&= LOCALE_NOUSEROVERRIDE
;
197 TRACE("(0x%04x,0x%08x)\n", lcid
, dwFlags
);
199 /* See if we have already cached the locales number format */
200 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
203 if (!node
|| node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
205 NLS_FORMAT_NODE
*new_node
;
208 TRACE("Creating new cache entry\n");
210 if (!(new_node
= HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE
))))
213 GET_LOCALE_NUMBER(new_node
->dwCodePage
, LOCALE_IDEFAULTANSICODEPAGE
);
216 new_node
->lcid
= lcid
;
217 new_node
->dwFlags
= dwFlags
;
218 new_node
->next
= NULL
;
220 GET_LOCALE_NUMBER(new_node
->fmt
.NumDigits
, LOCALE_IDIGITS
);
221 GET_LOCALE_NUMBER(new_node
->fmt
.LeadingZero
, LOCALE_ILZERO
);
222 GET_LOCALE_NUMBER(new_node
->fmt
.NegativeOrder
, LOCALE_INEGNUMBER
);
224 GET_LOCALE_NUMBER(new_node
->fmt
.Grouping
, LOCALE_SGROUPING
);
225 if (new_node
->fmt
.Grouping
> 9 && new_node
->fmt
.Grouping
!= 32)
227 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
228 new_node
->fmt
.Grouping
);
229 new_node
->fmt
.Grouping
= 0;
232 GET_LOCALE_STRING(new_node
->fmt
.lpDecimalSep
, LOCALE_SDECIMAL
);
233 GET_LOCALE_STRING(new_node
->fmt
.lpThousandSep
, LOCALE_STHOUSAND
);
235 /* Currency Format */
236 new_node
->cyfmt
.NumDigits
= new_node
->fmt
.NumDigits
;
237 new_node
->cyfmt
.LeadingZero
= new_node
->fmt
.LeadingZero
;
239 GET_LOCALE_NUMBER(new_node
->cyfmt
.Grouping
, LOCALE_SGROUPING
);
241 if (new_node
->cyfmt
.Grouping
> 9)
243 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
244 new_node
->cyfmt
.Grouping
);
245 new_node
->cyfmt
.Grouping
= 0;
248 GET_LOCALE_NUMBER(new_node
->cyfmt
.NegativeOrder
, LOCALE_INEGCURR
);
249 if (new_node
->cyfmt
.NegativeOrder
> 15)
251 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
252 new_node
->cyfmt
.NegativeOrder
);
253 new_node
->cyfmt
.NegativeOrder
= 0;
255 GET_LOCALE_NUMBER(new_node
->cyfmt
.PositiveOrder
, LOCALE_ICURRENCY
);
256 if (new_node
->cyfmt
.PositiveOrder
> 3)
258 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
259 new_node
->cyfmt
.PositiveOrder
);
260 new_node
->cyfmt
.PositiveOrder
= 0;
262 GET_LOCALE_STRING(new_node
->cyfmt
.lpDecimalSep
, LOCALE_SMONDECIMALSEP
);
263 GET_LOCALE_STRING(new_node
->cyfmt
.lpThousandSep
, LOCALE_SMONTHOUSANDSEP
);
264 GET_LOCALE_STRING(new_node
->cyfmt
.lpCurrencySymbol
, LOCALE_SCURRENCY
);
266 /* Date/Time Format info, negative character, etc */
267 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
269 GET_LOCALE_STRING(new_node
->lppszStrings
[i
], NLS_LocaleIndices
[i
]);
271 /* Save some memory if month genitive name is the same or not present */
272 for (i
= 0; i
< 12; i
++)
274 if (strcmpW(GetLongMonth(new_node
, i
), GetGenitiveMonth(new_node
, i
)) == 0)
276 HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node
, i
));
277 GetGenitiveMonth(new_node
, i
) = NULL
;
281 new_node
->szShortAM
[0] = GetAM(new_node
)[0]; new_node
->szShortAM
[1] = '\0';
282 new_node
->szShortPM
[0] = GetPM(new_node
)[0]; new_node
->szShortPM
[1] = '\0';
284 /* Now add the computed format to the cache */
285 RtlEnterCriticalSection(&NLS_FormatsCS
);
287 /* Search again: We may have raced to add the node */
288 node
= NLS_CachedFormats
;
289 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
294 node
= NLS_CachedFormats
= new_node
; /* Empty list */
297 else if (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
299 node
->next
= new_node
; /* Not in the list, add to end */
304 RtlLeaveCriticalSection(&NLS_FormatsCS
);
308 /* We raced and lost: The node was already added by another thread.
309 * node points to the currently cached node, so free new_node.
311 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
312 HeapFree(GetProcessHeap(), 0, new_node
->lppszStrings
[i
]);
313 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpDecimalSep
);
314 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpThousandSep
);
315 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpDecimalSep
);
316 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpThousandSep
);
317 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpCurrencySymbol
);
318 HeapFree(GetProcessHeap(), 0, new_node
);
324 /**************************************************************************
325 * NLS_IsUnicodeOnlyLcid <internal>
327 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
329 BOOL
NLS_IsUnicodeOnlyLcid(LCID lcid
)
331 lcid
= ConvertDefaultLocale(lcid
);
333 switch (PRIMARYLANGID(lcid
))
345 TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid
, PRIMARYLANGID(lcid
));
353 * Formatting of dates, times, numbers and currencies.
356 #define IsLiteralMarker(p) (p == '\'')
357 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
358 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
360 /* Only the following flags can be given if a date/time format is specified */
361 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
362 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
363 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
366 /******************************************************************************
367 * NLS_GetDateTimeFormatW <internal>
369 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
372 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
373 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
375 static INT
NLS_GetDateTimeFormatW(LCID lcid
, DWORD dwFlags
,
376 const SYSTEMTIME
* lpTime
, LPCWSTR lpFormat
,
377 LPWSTR lpStr
, INT cchOut
)
379 const NLS_FORMAT_NODE
*node
;
382 INT lastFormatPos
= 0;
383 BOOL bSkipping
= FALSE
; /* Skipping text around marker? */
384 BOOL d_dd_formatted
= FALSE
; /* previous formatted part was for d or dd */
386 /* Verify our arguments */
387 if ((cchOut
&& !lpStr
) || !(node
= NLS_GetFormats(lcid
, dwFlags
)))
388 goto invalid_parameter
;
390 if (dwFlags
& ~(DATE_DATEVARSONLY
|TIME_TIMEVARSONLY
))
393 ((dwFlags
& DATE_DATEVARSONLY
&& dwFlags
& ~DATE_FORMAT_FLAGS
) ||
394 (dwFlags
& TIME_TIMEVARSONLY
&& dwFlags
& ~TIME_FORMAT_FLAGS
)))
399 if (dwFlags
& DATE_DATEVARSONLY
)
401 if ((dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
)) == (DATE_LTRREADING
|DATE_RTLREADING
))
403 else if (dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
))
404 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
406 switch (dwFlags
& (DATE_SHORTDATE
|DATE_LONGDATE
|DATE_YEARMONTH
))
424 /* Use the appropriate default format */
425 if (dwFlags
& DATE_DATEVARSONLY
)
427 if (dwFlags
& DATE_YEARMONTH
)
428 lpFormat
= GetYearMonth(node
);
429 else if (dwFlags
& DATE_LONGDATE
)
430 lpFormat
= GetLongDate(node
);
432 lpFormat
= GetShortDate(node
);
435 lpFormat
= GetTime(node
);
440 GetLocalTime(&st
); /* Default to current time */
445 if (dwFlags
& DATE_DATEVARSONLY
)
449 /* Verify the date and correct the D.O.W. if needed */
450 memset(&st
, 0, sizeof(st
));
451 st
.wYear
= lpTime
->wYear
;
452 st
.wMonth
= lpTime
->wMonth
;
453 st
.wDay
= lpTime
->wDay
;
455 if (st
.wDay
> 31 || st
.wMonth
> 12 || !SystemTimeToFileTime(&st
, &ftTmp
))
456 goto invalid_parameter
;
458 FileTimeToSystemTime(&ftTmp
, &st
);
462 if (dwFlags
& TIME_TIMEVARSONLY
)
464 /* Verify the time */
465 if (lpTime
->wHour
> 24 || lpTime
->wMinute
> 59 || lpTime
->wSecond
> 59)
466 goto invalid_parameter
;
470 /* Format the output */
473 if (IsLiteralMarker(*lpFormat
))
475 /* Start of a literal string */
478 /* Loop until the end of the literal marker or end of the string */
481 if (IsLiteralMarker(*lpFormat
))
484 if (!IsLiteralMarker(*lpFormat
))
485 break; /* Terminating literal marker */
489 cchWritten
++; /* Count size only */
490 else if (cchWritten
>= cchOut
)
494 lpStr
[cchWritten
] = *lpFormat
;
500 else if ((dwFlags
& DATE_DATEVARSONLY
&& IsDateFmtChar(*lpFormat
)) ||
501 (dwFlags
& TIME_TIMEVARSONLY
&& IsTimeFmtChar(*lpFormat
)))
503 WCHAR buff
[32], fmtChar
;
504 LPCWSTR szAdd
= NULL
;
506 int count
= 0, dwLen
;
511 while (*lpFormat
== fmtChar
)
518 if (fmtChar
!= 'M') d_dd_formatted
= FALSE
;
523 szAdd
= GetLongDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
525 szAdd
= GetShortDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
528 dwVal
= lpTime
->wDay
;
530 d_dd_formatted
= TRUE
;
537 LPCWSTR genitive
= GetGenitiveMonth(node
, lpTime
->wMonth
- 1);
547 LPCWSTR format
= lpFormat
;
548 /* Look forward now, if next format pattern is for day genitive
549 name should be used */
552 /* Skip parts within markers */
553 if (IsLiteralMarker(*format
))
558 if (IsLiteralMarker(*format
))
561 if (!IsLiteralMarker(*format
)) break;
565 if (*format
!= ' ') break;
568 /* Only numeric day form matters */
569 if (*format
&& *format
== 'd')
572 while (*++format
== 'd') dcount
++;
581 szAdd
= GetLongMonth(node
, lpTime
->wMonth
- 1);
584 szAdd
= GetShortMonth(node
, lpTime
->wMonth
- 1);
587 dwVal
= lpTime
->wMonth
;
596 dwVal
= lpTime
->wYear
;
600 count
= count
> 2 ? 2 : count
;
601 dwVal
= lpTime
->wYear
% 100;
609 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
610 * When it is fixed, this string should be cached in 'node'.
612 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
613 buff
[0] = 'A'; buff
[1] = 'D'; buff
[2] = '\0';
617 buff
[0] = 'g'; buff
[1] = '\0'; /* Add a literal 'g' */
623 if (!(dwFlags
& TIME_FORCE24HOURFORMAT
))
625 count
= count
> 2 ? 2 : count
;
626 dwVal
= lpTime
->wHour
== 0 ? 12 : (lpTime
->wHour
- 1) % 12 + 1;
630 /* .. fall through if we are forced to output in 24 hour format */
633 count
= count
> 2 ? 2 : count
;
634 dwVal
= lpTime
->wHour
;
639 if (dwFlags
& TIME_NOMINUTESORSECONDS
)
641 cchWritten
= lastFormatPos
; /* Skip */
646 count
= count
> 2 ? 2 : count
;
647 dwVal
= lpTime
->wMinute
;
653 if (dwFlags
& (TIME_NOSECONDS
|TIME_NOMINUTESORSECONDS
))
655 cchWritten
= lastFormatPos
; /* Skip */
660 count
= count
> 2 ? 2 : count
;
661 dwVal
= lpTime
->wSecond
;
667 if (dwFlags
& TIME_NOTIMEMARKER
)
669 cchWritten
= lastFormatPos
; /* Skip */
675 szAdd
= lpTime
->wHour
< 12 ? node
->szShortAM
: node
->szShortPM
;
677 szAdd
= lpTime
->wHour
< 12 ? GetAM(node
) : GetPM(node
);
682 if (szAdd
== buff
&& buff
[0] == '\0')
684 static const WCHAR fmtW
[] = {'%','.','*','d',0};
685 /* We have a numeric value to add */
686 snprintfW(buff
, sizeof(buff
)/sizeof(WCHAR
), fmtW
, count
, dwVal
);
689 dwLen
= szAdd
? strlenW(szAdd
) : 0;
693 if (cchWritten
+ dwLen
< cchOut
)
694 memcpy(lpStr
+ cchWritten
, szAdd
, dwLen
* sizeof(WCHAR
));
697 memcpy(lpStr
+ cchWritten
, szAdd
, (cchOut
- cchWritten
) * sizeof(WCHAR
));
702 lastFormatPos
= cchWritten
; /* Save position of last output format text */
706 /* Literal character */
708 cchWritten
++; /* Count size only */
709 else if (cchWritten
>= cchOut
)
711 else if (!bSkipping
|| *lpFormat
== ' ')
713 lpStr
[cchWritten
] = *lpFormat
;
720 /* Final string terminator and sanity check */
723 if (cchWritten
>= cchOut
)
726 lpStr
[cchWritten
] = '\0';
728 cchWritten
++; /* Include terminating NUL */
730 TRACE("returning length=%d, ouput=%s\n", cchWritten
, debugstr_w(lpStr
));
734 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
735 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
739 SetLastError(ERROR_INVALID_PARAMETER
);
743 SetLastError(ERROR_INVALID_FLAGS
);
747 /******************************************************************************
748 * NLS_GetDateTimeFormatA <internal>
750 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
752 static INT
NLS_GetDateTimeFormatA(LCID lcid
, DWORD dwFlags
,
753 const SYSTEMTIME
* lpTime
,
754 LPCSTR lpFormat
, LPSTR lpStr
, INT cchOut
)
757 WCHAR szFormat
[128], szOut
[128];
760 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
761 debugstr_a(lpFormat
), lpStr
, cchOut
);
763 if (NLS_IsUnicodeOnlyLcid(lcid
))
765 SetLastError(ERROR_INVALID_PARAMETER
);
769 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
771 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
774 SetLastError(ERROR_INVALID_PARAMETER
);
778 cp
= node
->dwCodePage
;
782 MultiByteToWideChar(cp
, 0, lpFormat
, -1, szFormat
, sizeof(szFormat
)/sizeof(WCHAR
));
784 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
785 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
789 iRet
= NLS_GetDateTimeFormatW(lcid
, dwFlags
, lpTime
, lpFormat
? szFormat
: NULL
,
790 lpStr
? szOut
: NULL
, cchOut
);
795 WideCharToMultiByte(cp
, 0, szOut
, iRet
? -1 : cchOut
, lpStr
, cchOut
, 0, 0);
796 else if (cchOut
&& iRet
)
802 /******************************************************************************
803 * GetDateFormatA [KERNEL32.@]
805 * Format a date for a given locale.
808 * lcid [I] Locale to format for
809 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
810 * lpTime [I] Date to format
811 * lpFormat [I] Format string, or NULL to use the system defaults
812 * lpDateStr [O] Destination for formatted string
813 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
816 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
817 * details returned by GetLocaleInfoA() and modified by dwFlags.
818 * - lpFormat is a string of characters and formatting tokens. Any characters
819 * in the string are copied verbatim to lpDateStr, with tokens being replaced
820 * by the date values they represent.
821 * - The following tokens have special meanings in a date format string:
824 *| d Single digit day of the month (no leading 0)
825 *| dd Double digit day of the month
826 *| ddd Short name for the day of the week
827 *| dddd Long name for the day of the week
828 *| M Single digit month of the year (no leading 0)
829 *| MM Double digit month of the year
830 *| MMM Short name for the month of the year
831 *| MMMM Long name for the month of the year
832 *| y Double digit year number (no leading 0)
833 *| yy Double digit year number
834 *| yyyy Four digit year number
835 *| gg Era string, for example 'AD'.
836 * - To output any literal character that could be misidentified as a token,
837 * enclose it in single quotes.
838 * - The Ascii version of this function fails if lcid is Unicode only.
841 * Success: The number of character written to lpDateStr, or that would
842 * have been written, if cchOut is 0.
843 * Failure: 0. Use GetLastError() to determine the cause.
845 INT WINAPI
GetDateFormatA( LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
846 LPCSTR lpFormat
, LPSTR lpDateStr
, INT cchOut
)
848 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
849 debugstr_a(lpFormat
), lpDateStr
, cchOut
);
851 return NLS_GetDateTimeFormatA(lcid
, dwFlags
| DATE_DATEVARSONLY
, lpTime
,
852 lpFormat
, lpDateStr
, cchOut
);
856 /******************************************************************************
857 * GetDateFormatW [KERNEL32.@]
859 * See GetDateFormatA.
861 INT WINAPI
GetDateFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
862 LPCWSTR lpFormat
, LPWSTR lpDateStr
, INT cchOut
)
864 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
865 debugstr_w(lpFormat
), lpDateStr
, cchOut
);
867 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|DATE_DATEVARSONLY
, lpTime
,
868 lpFormat
, lpDateStr
, cchOut
);
871 /******************************************************************************
872 * GetTimeFormatA [KERNEL32.@]
874 * Format a time for a given locale.
877 * lcid [I] Locale to format for
878 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
879 * lpTime [I] Time to format
880 * lpFormat [I] Formatting overrides
881 * lpTimeStr [O] Destination for formatted string
882 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
885 * - If lpFormat is NULL, lpszValue will be formatted according to the format
886 * details returned by GetLocaleInfoA() and modified by dwFlags.
887 * - lpFormat is a string of characters and formatting tokens. Any characters
888 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
889 * by the time values they represent.
890 * - The following tokens have special meanings in a time format string:
893 *| h Hours with no leading zero (12-hour clock)
894 *| hh Hours with full two digits (12-hour clock)
895 *| H Hours with no leading zero (24-hour clock)
896 *| HH Hours with full two digits (24-hour clock)
897 *| m Minutes with no leading zero
898 *| mm Minutes with full two digits
899 *| s Seconds with no leading zero
900 *| ss Seconds with full two digits
901 *| t Short time marker (e.g. "A" or "P")
902 *| tt Long time marker (e.g. "AM", "PM")
903 * - To output any literal character that could be misidentified as a token,
904 * enclose it in single quotes.
905 * - The Ascii version of this function fails if lcid is Unicode only.
908 * Success: The number of character written to lpTimeStr, or that would
909 * have been written, if cchOut is 0.
910 * Failure: 0. Use GetLastError() to determine the cause.
912 INT WINAPI
GetTimeFormatA(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
913 LPCSTR lpFormat
, LPSTR lpTimeStr
, INT cchOut
)
915 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
916 debugstr_a(lpFormat
), lpTimeStr
, cchOut
);
918 return NLS_GetDateTimeFormatA(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
919 lpFormat
, lpTimeStr
, cchOut
);
922 /******************************************************************************
923 * GetTimeFormatW [KERNEL32.@]
925 * See GetTimeFormatA.
927 INT WINAPI
GetTimeFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
928 LPCWSTR lpFormat
, LPWSTR lpTimeStr
, INT cchOut
)
930 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
931 debugstr_w(lpFormat
), lpTimeStr
, cchOut
);
933 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
934 lpFormat
, lpTimeStr
, cchOut
);
937 /**************************************************************************
938 * GetNumberFormatA (KERNEL32.@)
940 * Format a number string for a given locale.
943 * lcid [I] Locale to format for
944 * dwFlags [I] LOCALE_ flags from "winnls.h"
945 * lpszValue [I] String to format
946 * lpFormat [I] Formatting overrides
947 * lpNumberStr [O] Destination for formatted string
948 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
951 * - lpszValue can contain only '0' - '9', '-' and '.'.
952 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
953 * be formatted according to the format details returned by GetLocaleInfoA().
954 * - This function rounds the number string if the number of decimals exceeds the
955 * locales normal number of decimal places.
956 * - If cchOut is 0, this function does not write to lpNumberStr.
957 * - The Ascii version of this function fails if lcid is Unicode only.
960 * Success: The number of character written to lpNumberStr, or that would
961 * have been written, if cchOut is 0.
962 * Failure: 0. Use GetLastError() to determine the cause.
964 INT WINAPI
GetNumberFormatA(LCID lcid
, DWORD dwFlags
,
965 LPCSTR lpszValue
, const NUMBERFMTA
*lpFormat
,
966 LPSTR lpNumberStr
, int cchOut
)
969 WCHAR szDec
[8], szGrp
[8], szIn
[128], szOut
[128];
971 const NUMBERFMTW
*pfmt
= NULL
;
974 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
975 lpFormat
, lpNumberStr
, cchOut
);
977 if (NLS_IsUnicodeOnlyLcid(lcid
))
979 SetLastError(ERROR_INVALID_PARAMETER
);
983 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
985 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
988 SetLastError(ERROR_INVALID_PARAMETER
);
992 cp
= node
->dwCodePage
;
997 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
999 if (lpFormat
->lpDecimalSep
)
1001 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
1002 fmt
.lpDecimalSep
= szDec
;
1004 if (lpFormat
->lpThousandSep
)
1006 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
1007 fmt
.lpThousandSep
= szGrp
;
1012 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1014 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1015 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1019 iRet
= GetNumberFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1020 lpNumberStr
? szOut
: NULL
, cchOut
);
1022 if (szOut
[0] && lpNumberStr
)
1023 WideCharToMultiByte(cp
, 0, szOut
, -1, lpNumberStr
, cchOut
, 0, 0);
1027 /* Number parsing state flags */
1028 #define NF_ISNEGATIVE 0x1 /* '-' found */
1029 #define NF_ISREAL 0x2 /* '.' found */
1030 #define NF_DIGITS 0x4 /* '0'-'9' found */
1031 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
1032 #define NF_ROUND 0x10 /* Number needs to be rounded */
1034 /* Formatting options for Numbers */
1035 #define NLS_NEG_PARENS 0 /* "(1.1)" */
1036 #define NLS_NEG_LEFT 1 /* "-1.1" */
1037 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
1038 #define NLS_NEG_RIGHT 3 /* "1.1-" */
1039 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1041 /**************************************************************************
1042 * GetNumberFormatW (KERNEL32.@)
1044 * See GetNumberFormatA.
1046 INT WINAPI
GetNumberFormatW(LCID lcid
, DWORD dwFlags
,
1047 LPCWSTR lpszValue
, const NUMBERFMTW
*lpFormat
,
1048 LPWSTR lpNumberStr
, int cchOut
)
1050 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1052 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
;
1053 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0;
1056 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1057 lpFormat
, lpNumberStr
, cchOut
);
1059 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpNumberStr
) ||
1060 !IsValidLocale(lcid
, 0) ||
1061 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
)))
1068 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1072 lpFormat
= &node
->fmt
;
1073 lpszNegStart
= lpszNeg
= GetNegative(node
);
1077 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1078 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1079 lpszNegStart
= lpszNeg
= szNegBuff
;
1081 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1083 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1085 /* Format the number backwards into a temporary buffer */
1090 /* Check the number for validity */
1093 if (*szSrc
>= '0' && *szSrc
<= '9')
1095 dwState
|= NF_DIGITS
;
1096 if (dwState
& NF_ISREAL
)
1099 else if (*szSrc
== '-')
1102 goto error
; /* '-' not first character */
1103 dwState
|= NF_ISNEGATIVE
;
1105 else if (*szSrc
== '.')
1107 if (dwState
& NF_ISREAL
)
1108 goto error
; /* More than one '.' */
1109 dwState
|= NF_ISREAL
;
1112 goto error
; /* Invalid char */
1115 szSrc
--; /* Point to last character */
1117 if (!(dwState
& NF_DIGITS
))
1118 goto error
; /* No digits */
1120 /* Add any trailing negative sign */
1121 if (dwState
& NF_ISNEGATIVE
)
1123 switch (lpFormat
->NegativeOrder
)
1125 case NLS_NEG_PARENS
:
1129 case NLS_NEG_RIGHT_SPACE
:
1130 while (lpszNeg
>= lpszNegStart
)
1131 *szOut
-- = *lpszNeg
--;
1132 if (lpFormat
->NegativeOrder
== NLS_NEG_RIGHT_SPACE
)
1138 /* Copy all digits up to the decimal point */
1139 if (!lpFormat
->NumDigits
)
1141 if (dwState
& NF_ISREAL
)
1143 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1145 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1146 dwState
|= NF_ROUND
;
1148 dwState
&= ~NF_ROUND
;
1156 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1158 if (dwDecimals
<= lpFormat
->NumDigits
)
1160 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1161 while (dwDecimals
--)
1162 *szOut
-- = '0'; /* Pad to correct number of dp */
1166 dwDecimals
-= lpFormat
->NumDigits
;
1167 /* Skip excess decimals, and determine if we have to round the number */
1168 while (dwDecimals
--)
1170 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1171 dwState
|= NF_ROUND
;
1173 dwState
&= ~NF_ROUND
;
1178 if (dwState
& NF_ISREAL
)
1180 while (*szSrc
!= '.')
1182 if (dwState
& NF_ROUND
)
1185 *szOut
-- = '0'; /* continue rounding */
1188 dwState
&= ~NF_ROUND
;
1189 *szOut
-- = (*szSrc
)+1;
1194 *szOut
-- = *szSrc
--; /* Write existing decimals */
1196 szSrc
--; /* Skip '.' */
1199 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1200 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1203 dwGroupCount
= lpFormat
->Grouping
== 32 ? 3 : lpFormat
->Grouping
;
1205 /* Write the remaining whole number digits, including grouping chars */
1206 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1208 if (dwState
& NF_ROUND
)
1211 *szOut
-- = '0'; /* continue rounding */
1214 dwState
&= ~NF_ROUND
;
1215 *szOut
-- = (*szSrc
)+1;
1220 *szOut
-- = *szSrc
--;
1222 dwState
|= NF_DIGITS_OUT
;
1223 dwCurrentGroupCount
++;
1224 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1226 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1228 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1229 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1231 dwCurrentGroupCount
= 0;
1232 if (lpFormat
->Grouping
== 32)
1233 dwGroupCount
= 2; /* Indic grouping: 3 then 2 */
1236 if (dwState
& NF_ROUND
)
1238 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1240 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1241 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1243 /* Add any leading negative sign */
1244 if (dwState
& NF_ISNEGATIVE
)
1246 switch (lpFormat
->NegativeOrder
)
1248 case NLS_NEG_PARENS
:
1251 case NLS_NEG_LEFT_SPACE
:
1255 while (lpszNeg
>= lpszNegStart
)
1256 *szOut
-- = *lpszNeg
--;
1262 iRet
= strlenW(szOut
) + 1;
1266 memcpy(lpNumberStr
, szOut
, iRet
* sizeof(WCHAR
));
1269 memcpy(lpNumberStr
, szOut
, cchOut
* sizeof(WCHAR
));
1270 lpNumberStr
[cchOut
- 1] = '\0';
1271 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1278 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1282 /**************************************************************************
1283 * GetCurrencyFormatA (KERNEL32.@)
1285 * Format a currency string for a given locale.
1288 * lcid [I] Locale to format for
1289 * dwFlags [I] LOCALE_ flags from "winnls.h"
1290 * lpszValue [I] String to format
1291 * lpFormat [I] Formatting overrides
1292 * lpCurrencyStr [O] Destination for formatted string
1293 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1296 * - lpszValue can contain only '0' - '9', '-' and '.'.
1297 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1298 * be formatted according to the format details returned by GetLocaleInfoA().
1299 * - This function rounds the currency if the number of decimals exceeds the
1300 * locales number of currency decimal places.
1301 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1302 * - The Ascii version of this function fails if lcid is Unicode only.
1305 * Success: The number of character written to lpNumberStr, or that would
1306 * have been written, if cchOut is 0.
1307 * Failure: 0. Use GetLastError() to determine the cause.
1309 INT WINAPI
GetCurrencyFormatA(LCID lcid
, DWORD dwFlags
,
1310 LPCSTR lpszValue
, const CURRENCYFMTA
*lpFormat
,
1311 LPSTR lpCurrencyStr
, int cchOut
)
1314 WCHAR szDec
[8], szGrp
[8], szCy
[8], szIn
[128], szOut
[128];
1316 const CURRENCYFMTW
*pfmt
= NULL
;
1319 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
1320 lpFormat
, lpCurrencyStr
, cchOut
);
1322 if (NLS_IsUnicodeOnlyLcid(lcid
))
1324 SetLastError(ERROR_INVALID_PARAMETER
);
1328 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1330 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1333 SetLastError(ERROR_INVALID_PARAMETER
);
1337 cp
= node
->dwCodePage
;
1342 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1344 if (lpFormat
->lpDecimalSep
)
1346 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
1347 fmt
.lpDecimalSep
= szDec
;
1349 if (lpFormat
->lpThousandSep
)
1351 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
1352 fmt
.lpThousandSep
= szGrp
;
1354 if (lpFormat
->lpCurrencySymbol
)
1356 MultiByteToWideChar(cp
, 0, lpFormat
->lpCurrencySymbol
, -1, szCy
, sizeof(szCy
)/sizeof(WCHAR
));
1357 fmt
.lpCurrencySymbol
= szCy
;
1362 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1364 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1365 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1369 iRet
= GetCurrencyFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1370 lpCurrencyStr
? szOut
: NULL
, cchOut
);
1372 if (szOut
[0] && lpCurrencyStr
)
1373 WideCharToMultiByte(cp
, 0, szOut
, -1, lpCurrencyStr
, cchOut
, 0, 0);
1377 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1378 #define CF_PARENS 0x1 /* Parentheses */
1379 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1380 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1381 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1382 #define CF_CY_LEFT 0x10 /* '$' to the left */
1383 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1384 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1386 /**************************************************************************
1387 * GetCurrencyFormatW (KERNEL32.@)
1389 * See GetCurrencyFormatA.
1391 INT WINAPI
GetCurrencyFormatW(LCID lcid
, DWORD dwFlags
,
1392 LPCWSTR lpszValue
, const CURRENCYFMTW
*lpFormat
,
1393 LPWSTR lpCurrencyStr
, int cchOut
)
1395 static const BYTE NLS_NegCyFormats
[16] =
1397 CF_PARENS
|CF_CY_LEFT
, /* ($1.1) */
1398 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
, /* -$1.1 */
1399 CF_MINUS_LEFT
|CF_CY_LEFT
, /* $-1.1 */
1400 CF_MINUS_RIGHT
|CF_CY_LEFT
, /* $1.1- */
1401 CF_PARENS
|CF_CY_RIGHT
, /* (1.1$) */
1402 CF_MINUS_LEFT
|CF_CY_RIGHT
, /* -1.1$ */
1403 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
, /* 1.1-$ */
1404 CF_MINUS_RIGHT
|CF_CY_RIGHT
, /* 1.1$- */
1405 CF_MINUS_LEFT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* -1.1 $ */
1406 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
|CF_CY_SPACE
, /* -$ 1.1 */
1407 CF_MINUS_RIGHT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $- */
1408 CF_MINUS_RIGHT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1- */
1409 CF_MINUS_LEFT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ -1.1 */
1410 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1- $ */
1411 CF_PARENS
|CF_CY_LEFT
|CF_CY_SPACE
, /* ($ 1.1) */
1412 CF_PARENS
|CF_CY_RIGHT
|CF_CY_SPACE
, /* (1.1 $) */
1414 static const BYTE NLS_PosCyFormats
[4] =
1416 CF_CY_LEFT
, /* $1.1 */
1417 CF_CY_RIGHT
, /* 1.1$ */
1418 CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1 */
1419 CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $ */
1421 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1423 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
, *lpszCy
, *lpszCyStart
;
1424 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0, dwFmt
;
1427 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1428 lpFormat
, lpCurrencyStr
, cchOut
);
1430 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpCurrencyStr
) ||
1431 !IsValidLocale(lcid
, 0) ||
1432 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
||
1433 !lpFormat
->lpCurrencySymbol
|| lpFormat
->NegativeOrder
> 15 ||
1434 lpFormat
->PositiveOrder
> 3)))
1441 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1446 lpFormat
= &node
->cyfmt
;
1447 lpszNegStart
= lpszNeg
= GetNegative(node
);
1451 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1452 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1453 lpszNegStart
= lpszNeg
= szNegBuff
;
1455 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1457 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1458 lpszCyStart
= lpFormat
->lpCurrencySymbol
;
1459 lpszCy
= lpszCyStart
+ strlenW(lpszCyStart
) - 1;
1461 /* Format the currency backwards into a temporary buffer */
1466 /* Check the number for validity */
1469 if (*szSrc
>= '0' && *szSrc
<= '9')
1471 dwState
|= NF_DIGITS
;
1472 if (dwState
& NF_ISREAL
)
1475 else if (*szSrc
== '-')
1478 goto error
; /* '-' not first character */
1479 dwState
|= NF_ISNEGATIVE
;
1481 else if (*szSrc
== '.')
1483 if (dwState
& NF_ISREAL
)
1484 goto error
; /* More than one '.' */
1485 dwState
|= NF_ISREAL
;
1488 goto error
; /* Invalid char */
1491 szSrc
--; /* Point to last character */
1493 if (!(dwState
& NF_DIGITS
))
1494 goto error
; /* No digits */
1496 if (dwState
& NF_ISNEGATIVE
)
1497 dwFmt
= NLS_NegCyFormats
[lpFormat
->NegativeOrder
];
1499 dwFmt
= NLS_PosCyFormats
[lpFormat
->PositiveOrder
];
1501 /* Add any trailing negative or currency signs */
1502 if (dwFmt
& CF_PARENS
)
1505 while (dwFmt
& (CF_MINUS_RIGHT
|CF_CY_RIGHT
))
1507 switch (dwFmt
& (CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
))
1509 case CF_MINUS_RIGHT
:
1510 case CF_MINUS_RIGHT
|CF_CY_RIGHT
:
1511 while (lpszNeg
>= lpszNegStart
)
1512 *szOut
-- = *lpszNeg
--;
1513 dwFmt
&= ~CF_MINUS_RIGHT
;
1517 case CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1518 case CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1519 while (lpszCy
>= lpszCyStart
)
1520 *szOut
-- = *lpszCy
--;
1521 if (dwFmt
& CF_CY_SPACE
)
1523 dwFmt
&= ~(CF_CY_RIGHT
|CF_MINUS_BEFORE
);
1528 /* Copy all digits up to the decimal point */
1529 if (!lpFormat
->NumDigits
)
1531 if (dwState
& NF_ISREAL
)
1533 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1535 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1536 dwState
|= NF_ROUND
;
1538 dwState
&= ~NF_ROUND
;
1546 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1548 if (dwDecimals
<= lpFormat
->NumDigits
)
1550 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1551 while (dwDecimals
--)
1552 *szOut
-- = '0'; /* Pad to correct number of dp */
1556 dwDecimals
-= lpFormat
->NumDigits
;
1557 /* Skip excess decimals, and determine if we have to round the number */
1558 while (dwDecimals
--)
1560 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1561 dwState
|= NF_ROUND
;
1563 dwState
&= ~NF_ROUND
;
1568 if (dwState
& NF_ISREAL
)
1570 while (*szSrc
!= '.')
1572 if (dwState
& NF_ROUND
)
1575 *szOut
-- = '0'; /* continue rounding */
1578 dwState
&= ~NF_ROUND
;
1579 *szOut
-- = (*szSrc
)+1;
1584 *szOut
-- = *szSrc
--; /* Write existing decimals */
1586 szSrc
--; /* Skip '.' */
1588 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1589 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1592 dwGroupCount
= lpFormat
->Grouping
;
1594 /* Write the remaining whole number digits, including grouping chars */
1595 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1597 if (dwState
& NF_ROUND
)
1600 *szOut
-- = '0'; /* continue rounding */
1603 dwState
&= ~NF_ROUND
;
1604 *szOut
-- = (*szSrc
)+1;
1609 *szOut
-- = *szSrc
--;
1611 dwState
|= NF_DIGITS_OUT
;
1612 dwCurrentGroupCount
++;
1613 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1615 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1617 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1618 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1620 dwCurrentGroupCount
= 0;
1623 if (dwState
& NF_ROUND
)
1624 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1625 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1626 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1628 /* Add any leading negative or currency sign */
1629 while (dwFmt
& (CF_MINUS_LEFT
|CF_CY_LEFT
))
1631 switch (dwFmt
& (CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
))
1634 case CF_MINUS_LEFT
|CF_CY_LEFT
:
1635 while (lpszNeg
>= lpszNegStart
)
1636 *szOut
-- = *lpszNeg
--;
1637 dwFmt
&= ~CF_MINUS_LEFT
;
1641 case CF_CY_LEFT
|CF_MINUS_BEFORE
:
1642 case CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
:
1643 if (dwFmt
& CF_CY_SPACE
)
1645 while (lpszCy
>= lpszCyStart
)
1646 *szOut
-- = *lpszCy
--;
1647 dwFmt
&= ~(CF_CY_LEFT
|CF_MINUS_BEFORE
);
1651 if (dwFmt
& CF_PARENS
)
1655 iRet
= strlenW(szOut
) + 1;
1659 memcpy(lpCurrencyStr
, szOut
, iRet
* sizeof(WCHAR
));
1662 memcpy(lpCurrencyStr
, szOut
, cchOut
* sizeof(WCHAR
));
1663 lpCurrencyStr
[cchOut
- 1] = '\0';
1664 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1671 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1675 /* FIXME: Everything below here needs to move somewhere else along with the
1676 * other EnumXXX functions, when a method for storing resources for
1677 * alternate calendars is determined.
1680 /**************************************************************************
1681 * EnumDateFormatsExA (KERNEL32.@)
1683 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1684 * LOCALE_NOUSEROVERRIDE here as well?
1686 BOOL WINAPI
EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc
, LCID lcid
, DWORD flags
)
1693 SetLastError(ERROR_INVALID_PARAMETER
);
1697 if (!GetLocaleInfoW(lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
)))
1700 switch (flags
& ~LOCALE_USE_CP_ACP
)
1703 case DATE_SHORTDATE
:
1704 if (GetLocaleInfoA(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1709 if (GetLocaleInfoA(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1713 case DATE_YEARMONTH
:
1714 if (GetLocaleInfoA(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1719 FIXME("Unknown date format (%d)\n", flags
);
1720 SetLastError(ERROR_INVALID_PARAMETER
);
1726 /**************************************************************************
1727 * EnumDateFormatsExW (KERNEL32.@)
1729 BOOL WINAPI
EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc
, LCID lcid
, DWORD flags
)
1736 SetLastError(ERROR_INVALID_PARAMETER
);
1740 if (!GetLocaleInfoW(lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
)))
1743 switch (flags
& ~LOCALE_USE_CP_ACP
)
1746 case DATE_SHORTDATE
:
1747 if (GetLocaleInfoW(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1752 if (GetLocaleInfoW(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1756 case DATE_YEARMONTH
:
1757 if (GetLocaleInfoW(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1762 FIXME("Unknown date format (%d)\n", flags
);
1763 SetLastError(ERROR_INVALID_PARAMETER
);
1769 /**************************************************************************
1770 * EnumDateFormatsA (KERNEL32.@)
1772 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1773 * LOCALE_NOUSEROVERRIDE here as well?
1775 BOOL WINAPI
EnumDateFormatsA(DATEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
1781 SetLastError(ERROR_INVALID_PARAMETER
);
1785 switch (flags
& ~LOCALE_USE_CP_ACP
)
1788 case DATE_SHORTDATE
:
1789 if (GetLocaleInfoA(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1794 if (GetLocaleInfoA(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1798 case DATE_YEARMONTH
:
1799 if (GetLocaleInfoA(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1804 FIXME("Unknown date format (%d)\n", flags
);
1805 SetLastError(ERROR_INVALID_PARAMETER
);
1811 /**************************************************************************
1812 * EnumDateFormatsW (KERNEL32.@)
1814 BOOL WINAPI
EnumDateFormatsW(DATEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
1820 SetLastError(ERROR_INVALID_PARAMETER
);
1824 switch (flags
& ~LOCALE_USE_CP_ACP
)
1827 case DATE_SHORTDATE
:
1828 if (GetLocaleInfoW(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1833 if (GetLocaleInfoW(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1837 case DATE_YEARMONTH
:
1838 if (GetLocaleInfoW(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1843 FIXME("Unknown date format (%d)\n", flags
);
1844 SetLastError(ERROR_INVALID_PARAMETER
);
1850 /**************************************************************************
1851 * EnumTimeFormatsA (KERNEL32.@)
1853 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1854 * LOCALE_NOUSEROVERRIDE here as well?
1856 BOOL WINAPI
EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
1862 SetLastError(ERROR_INVALID_PARAMETER
);
1866 switch (flags
& ~LOCALE_USE_CP_ACP
)
1869 if (GetLocaleInfoA(lcid
, LOCALE_STIMEFORMAT
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1874 FIXME("Unknown time format (%d)\n", flags
);
1875 SetLastError(ERROR_INVALID_PARAMETER
);
1881 /**************************************************************************
1882 * EnumTimeFormatsW (KERNEL32.@)
1884 BOOL WINAPI
EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
1890 SetLastError(ERROR_INVALID_PARAMETER
);
1894 switch (flags
& ~LOCALE_USE_CP_ACP
)
1897 if (GetLocaleInfoW(lcid
, LOCALE_STIMEFORMAT
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1902 FIXME("Unknown time format (%d)\n", flags
);
1903 SetLastError(ERROR_INVALID_PARAMETER
);
1909 /******************************************************************************
1910 * NLS_EnumCalendarInfoAW <internal>
1911 * Enumerates calendar information for a specified locale.
1914 * calinfoproc [I] Pointer to the callback
1915 * locale [I] The locale for which to retrieve calendar information.
1916 * This parameter can be a locale identifier created by the
1917 * MAKELCID macro, or one of the following values:
1918 * LOCALE_SYSTEM_DEFAULT
1919 * Use the default system locale.
1920 * LOCALE_USER_DEFAULT
1921 * Use the default user locale.
1922 * calendar [I] The calendar for which information is requested, or
1923 * ENUM_ALL_CALENDARS.
1924 * caltype [I] The type of calendar information to be returned. Note
1925 * that only one CALTYPE value can be specified per call
1926 * of this function, except where noted.
1927 * unicode [I] Specifies if the callback expects a unicode string.
1928 * ex [I] Specifies if the callback needs the calendar identifier.
1932 * Failure: FALSE. Use GetLastError() to determine the cause.
1935 * When the ANSI version of this function is used with a Unicode-only LCID,
1936 * the call can succeed because the system uses the system code page.
1937 * However, characters that are undefined in the system code page appear
1938 * in the string as a question mark (?).
1941 * The above note should be respected by GetCalendarInfoA.
1943 static BOOL
NLS_EnumCalendarInfoAW(void *calinfoproc
, LCID locale
,
1944 CALID calendar
, CALTYPE caltype
, BOOL unicode
, BOOL ex
)
1946 WCHAR
*buf
, *opt
= NULL
, *iter
= NULL
;
1948 int bufSz
= 200; /* the size of the buffer */
1950 if (calinfoproc
== NULL
)
1952 SetLastError(ERROR_INVALID_PARAMETER
);
1956 buf
= HeapAlloc(GetProcessHeap(), 0, bufSz
);
1959 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1963 if (calendar
== ENUM_ALL_CALENDARS
)
1965 int optSz
= GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, NULL
, 0);
1968 opt
= HeapAlloc(GetProcessHeap(), 0, optSz
* sizeof(WCHAR
));
1971 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1974 if (GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, opt
, optSz
))
1977 calendar
= NLS_GetLocaleNumber(locale
, LOCALE_ICALENDARTYPE
);
1980 while (TRUE
) /* loop through calendars */
1982 do /* loop until there's no error */
1985 ret
= GetCalendarInfoW(locale
, calendar
, caltype
, buf
, bufSz
/ sizeof(WCHAR
), NULL
);
1986 else ret
= GetCalendarInfoA(locale
, calendar
, caltype
, (CHAR
*)buf
, bufSz
/ sizeof(CHAR
), NULL
);
1990 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1991 { /* so resize it */
1994 newSz
= GetCalendarInfoW(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(WCHAR
);
1995 else newSz
= GetCalendarInfoA(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(CHAR
);
1998 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz
, newSz
);
2002 WARN("Buffer too small; resizing to %d bytes.\n", bufSz
);
2003 buf
= HeapReAlloc(GetProcessHeap(), 0, buf
, bufSz
);
2006 } else goto cleanup
;
2010 /* Here we are. We pass the buffer to the correct version of
2011 * the callback. Because it's not the same number of params,
2012 * we must check for Ex, but we don't care about Unicode
2013 * because the buffer is already in the correct format.
2016 ret
= ((CALINFO_ENUMPROCEXW
)calinfoproc
)(buf
, calendar
);
2018 ret
= ((CALINFO_ENUMPROCW
)calinfoproc
)(buf
);
2020 if (!ret
) { /* the callback told to stop */
2025 if ((iter
== NULL
) || (*iter
== 0)) /* no more calendars */
2029 while ((*iter
>= '0') && (*iter
<= '9'))
2030 calendar
= calendar
* 10 + *iter
++ - '0';
2034 SetLastError(ERROR_BADDB
);
2041 HeapFree(GetProcessHeap(), 0, opt
);
2042 HeapFree(GetProcessHeap(), 0, buf
);
2046 /******************************************************************************
2047 * EnumCalendarInfoA [KERNEL32.@]
2049 * See EnumCalendarInfoAW.
2051 BOOL WINAPI
EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc
,LCID locale
,
2052 CALID calendar
,CALTYPE caltype
)
2054 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2055 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, FALSE
);
2058 /******************************************************************************
2059 * EnumCalendarInfoW [KERNEL32.@]
2061 * See EnumCalendarInfoAW.
2063 BOOL WINAPI
EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc
,LCID locale
,
2064 CALID calendar
,CALTYPE caltype
)
2066 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2067 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, FALSE
);
2070 /******************************************************************************
2071 * EnumCalendarInfoExA [KERNEL32.@]
2073 * See EnumCalendarInfoAW.
2075 BOOL WINAPI
EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc
,LCID locale
,
2076 CALID calendar
,CALTYPE caltype
)
2078 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2079 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, TRUE
);
2082 /******************************************************************************
2083 * EnumCalendarInfoExW [KERNEL32.@]
2085 * See EnumCalendarInfoAW.
2087 BOOL WINAPI
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc
,LCID locale
,
2088 CALID calendar
,CALTYPE caltype
)
2090 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2091 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, TRUE
);
2094 /*********************************************************************
2095 * GetCalendarInfoA (KERNEL32.@)
2098 int WINAPI
GetCalendarInfoA(LCID lcid
, CALID Calendar
, CALTYPE CalType
,
2099 LPSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2102 LPWSTR lpCalDataW
= NULL
;
2104 if (NLS_IsUnicodeOnlyLcid(lcid
))
2106 SetLastError(ERROR_INVALID_PARAMETER
);
2111 !(lpCalDataW
= HeapAlloc(GetProcessHeap(), 0, cchData
*sizeof(WCHAR
))))
2114 ret
= GetCalendarInfoW(lcid
, Calendar
, CalType
, lpCalDataW
, cchData
, lpValue
);
2115 if(ret
&& lpCalDataW
&& lpCalData
)
2116 WideCharToMultiByte(CP_ACP
, 0, lpCalDataW
, cchData
, lpCalData
, cchData
, NULL
, NULL
);
2117 else if (CalType
& CAL_RETURN_NUMBER
)
2118 ret
*= sizeof(WCHAR
);
2119 HeapFree(GetProcessHeap(), 0, lpCalDataW
);
2124 /*********************************************************************
2125 * GetCalendarInfoW (KERNEL32.@)
2128 int WINAPI
GetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
,
2129 LPWSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2131 if (CalType
& CAL_NOUSEROVERRIDE
)
2132 FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
2133 if (CalType
& CAL_USE_CP_ACP
)
2134 FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
2136 if (CalType
& CAL_RETURN_NUMBER
) {
2139 SetLastError( ERROR_INVALID_PARAMETER
);
2142 if (lpCalData
!= NULL
)
2143 WARN("lpCalData not NULL (%p) when it should!\n", lpCalData
);
2145 WARN("cchData not 0 (%d) when it should!\n", cchData
);
2147 if (lpValue
!= NULL
)
2148 WARN("lpValue not NULL (%p) when it should!\n", lpValue
);
2151 /* FIXME: No verification is made yet wrt Locale
2152 * for the CALTYPES not requiring GetLocaleInfoA */
2153 switch (CalType
& ~(CAL_NOUSEROVERRIDE
|CAL_RETURN_NUMBER
|CAL_USE_CP_ACP
)) {
2154 case CAL_ICALINTVALUE
:
2155 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2158 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2160 case CAL_IYEAROFFSETRANGE
:
2161 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2163 case CAL_SERASTRING
:
2164 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2166 case CAL_SSHORTDATE
:
2167 return GetLocaleInfoW(Locale
, LOCALE_SSHORTDATE
, lpCalData
, cchData
);
2169 return GetLocaleInfoW(Locale
, LOCALE_SLONGDATE
, lpCalData
, cchData
);
2171 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME1
, lpCalData
, cchData
);
2173 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME2
, lpCalData
, cchData
);
2175 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME3
, lpCalData
, cchData
);
2177 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME4
, lpCalData
, cchData
);
2179 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME5
, lpCalData
, cchData
);
2181 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME6
, lpCalData
, cchData
);
2183 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME7
, lpCalData
, cchData
);
2184 case CAL_SABBREVDAYNAME1
:
2185 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME1
, lpCalData
, cchData
);
2186 case CAL_SABBREVDAYNAME2
:
2187 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME2
, lpCalData
, cchData
);
2188 case CAL_SABBREVDAYNAME3
:
2189 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME3
, lpCalData
, cchData
);
2190 case CAL_SABBREVDAYNAME4
:
2191 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME4
, lpCalData
, cchData
);
2192 case CAL_SABBREVDAYNAME5
:
2193 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME5
, lpCalData
, cchData
);
2194 case CAL_SABBREVDAYNAME6
:
2195 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME6
, lpCalData
, cchData
);
2196 case CAL_SABBREVDAYNAME7
:
2197 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME7
, lpCalData
, cchData
);
2198 case CAL_SMONTHNAME1
:
2199 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME1
, lpCalData
, cchData
);
2200 case CAL_SMONTHNAME2
:
2201 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME2
, lpCalData
, cchData
);
2202 case CAL_SMONTHNAME3
:
2203 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME3
, lpCalData
, cchData
);
2204 case CAL_SMONTHNAME4
:
2205 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME4
, lpCalData
, cchData
);
2206 case CAL_SMONTHNAME5
:
2207 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME5
, lpCalData
, cchData
);
2208 case CAL_SMONTHNAME6
:
2209 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME6
, lpCalData
, cchData
);
2210 case CAL_SMONTHNAME7
:
2211 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME7
, lpCalData
, cchData
);
2212 case CAL_SMONTHNAME8
:
2213 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME8
, lpCalData
, cchData
);
2214 case CAL_SMONTHNAME9
:
2215 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME9
, lpCalData
, cchData
);
2216 case CAL_SMONTHNAME10
:
2217 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME10
, lpCalData
, cchData
);
2218 case CAL_SMONTHNAME11
:
2219 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME11
, lpCalData
, cchData
);
2220 case CAL_SMONTHNAME12
:
2221 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME12
, lpCalData
, cchData
);
2222 case CAL_SMONTHNAME13
:
2223 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME13
, lpCalData
, cchData
);
2224 case CAL_SABBREVMONTHNAME1
:
2225 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME1
, lpCalData
, cchData
);
2226 case CAL_SABBREVMONTHNAME2
:
2227 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME2
, lpCalData
, cchData
);
2228 case CAL_SABBREVMONTHNAME3
:
2229 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME3
, lpCalData
, cchData
);
2230 case CAL_SABBREVMONTHNAME4
:
2231 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME4
, lpCalData
, cchData
);
2232 case CAL_SABBREVMONTHNAME5
:
2233 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME5
, lpCalData
, cchData
);
2234 case CAL_SABBREVMONTHNAME6
:
2235 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME6
, lpCalData
, cchData
);
2236 case CAL_SABBREVMONTHNAME7
:
2237 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME7
, lpCalData
, cchData
);
2238 case CAL_SABBREVMONTHNAME8
:
2239 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME8
, lpCalData
, cchData
);
2240 case CAL_SABBREVMONTHNAME9
:
2241 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME9
, lpCalData
, cchData
);
2242 case CAL_SABBREVMONTHNAME10
:
2243 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME10
, lpCalData
, cchData
);
2244 case CAL_SABBREVMONTHNAME11
:
2245 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME11
, lpCalData
, cchData
);
2246 case CAL_SABBREVMONTHNAME12
:
2247 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME12
, lpCalData
, cchData
);
2248 case CAL_SABBREVMONTHNAME13
:
2249 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME13
, lpCalData
, cchData
);
2250 case CAL_SYEARMONTH
:
2251 return GetLocaleInfoW(Locale
, LOCALE_SYEARMONTH
, lpCalData
, cchData
);
2252 case CAL_ITWODIGITYEARMAX
:
2253 if (CalType
& CAL_RETURN_NUMBER
)
2255 *lpValue
= CALINFO_MAX_YEAR
;
2256 return sizeof(DWORD
) / sizeof(WCHAR
);
2260 static const WCHAR fmtW
[] = {'%','u',0};
2262 int ret
= snprintfW( buffer
, 10, fmtW
, CALINFO_MAX_YEAR
) + 1;
2263 if (!lpCalData
) return ret
;
2266 strcpyW( lpCalData
, buffer
);
2269 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2274 FIXME("Unknown caltype %d\n",CalType
& 0xffff);
2275 SetLastError(ERROR_INVALID_FLAGS
);
2281 /*********************************************************************
2282 * SetCalendarInfoA (KERNEL32.@)
2285 int WINAPI
SetCalendarInfoA(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCSTR lpCalData
)
2287 FIXME("(%08x,%08x,%08x,%s): stub\n",
2288 Locale
, Calendar
, CalType
, debugstr_a(lpCalData
));
2292 /*********************************************************************
2293 * SetCalendarInfoW (KERNEL32.@)
2297 int WINAPI
SetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCWSTR lpCalData
)
2299 FIXME("(%08x,%08x,%08x,%s): stub\n",
2300 Locale
, Calendar
, CalType
, debugstr_w(lpCalData
));