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
31 #define CRITICAL_SECTION RTL_CRITICAL_SECTION
32 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG
33 #define CALINFO_MAX_YEAR 2029
35 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
36 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
38 /* Since calculating the formatting data for each locale is time-consuming,
39 * we get the format data for each locale only once and cache it in memory.
40 * We cache both the system default and user overridden data, after converting
41 * them into the formats that the functions here expect. Since these functions
42 * will typically be called with only a small number of the total locales
43 * installed, the memory overhead is minimal while the speedup is significant.
45 * Our cache takes the form of a singly linked list, whose node is below:
47 #define NLS_NUM_CACHED_STRINGS 57
49 typedef struct _NLS_FORMAT_NODE
51 LCID lcid
; /* Locale Id */
52 DWORD dwFlags
; /* 0 or LOCALE_NOUSEROVERRIDE */
53 DWORD dwCodePage
; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
54 NUMBERFMTW fmt
; /* Default format for numbers */
55 CURRENCYFMTW cyfmt
; /* Default format for currencies */
56 LPWSTR lppszStrings
[NLS_NUM_CACHED_STRINGS
]; /* Default formats,day/month names */
57 WCHAR szShortAM
[2]; /* Short 'AM' marker */
58 WCHAR szShortPM
[2]; /* Short 'PM' marker */
59 struct _NLS_FORMAT_NODE
*next
;
62 /* Macros to get particular data strings from a format node */
63 #define GetNegative(fmt) fmt->lppszStrings[0]
64 #define GetLongDate(fmt) fmt->lppszStrings[1]
65 #define GetShortDate(fmt) fmt->lppszStrings[2]
66 #define GetTime(fmt) fmt->lppszStrings[3]
67 #define GetAM(fmt) fmt->lppszStrings[54]
68 #define GetPM(fmt) fmt->lppszStrings[55]
69 #define GetYearMonth(fmt) fmt->lppszStrings[56]
71 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
72 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
73 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
74 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth]
75 #define GetShortMonth(fmt,mth) fmt->lppszStrings[42 + mth]
77 /* Write access to the cache is protected by this critical section */
78 static CRITICAL_SECTION NLS_FormatsCS
;
79 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug
=
82 { &NLS_FormatsCS_debug
.ProcessLocksList
,
83 &NLS_FormatsCS_debug
.ProcessLocksList
},
86 static CRITICAL_SECTION NLS_FormatsCS
= { &NLS_FormatsCS_debug
, -1, 0, 0, 0, 0 };
88 /**************************************************************************
89 * NLS_GetLocaleNumber <internal>
91 * Get a numeric locale format value.
93 static DWORD
NLS_GetLocaleNumber(LCID lcid
, DWORD dwFlags
)
99 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
101 if (szBuff
[0] && szBuff
[1] == ';' && szBuff
[2] != '0')
102 dwVal
= (szBuff
[0] - '0') * 10 + (szBuff
[2] - '0');
105 const WCHAR
* iter
= szBuff
;
107 while(*iter
>= '0' && *iter
<= '9')
108 dwVal
= dwVal
* 10 + (*iter
++ - '0');
113 /**************************************************************************
114 * NLS_GetLocaleString <internal>
116 * Get a string locale format value.
118 static WCHAR
* NLS_GetLocaleString(LCID lcid
, DWORD dwFlags
)
120 WCHAR szBuff
[80], *str
;
124 GetLocaleInfoW(lcid
, dwFlags
, szBuff
, sizeof(szBuff
) / sizeof(WCHAR
));
125 dwLen
= strlenW(szBuff
) + 1;
126 str
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
128 memcpy(str
, szBuff
, dwLen
* sizeof(WCHAR
));
132 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
133 TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num)
135 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
136 TRACE( #type ": %s\n", debugstr_w(str))
138 /**************************************************************************
139 * NLS_GetFormats <internal>
141 * Calculate (and cache) the number formats for a locale.
143 static const NLS_FORMAT_NODE
*NLS_GetFormats(LCID lcid
, DWORD dwFlags
)
145 /* GetLocaleInfo() identifiers for cached formatting strings */
146 static const LCTYPE NLS_LocaleIndices
[] = {
147 LOCALE_SNEGATIVESIGN
,
148 LOCALE_SLONGDATE
, LOCALE_SSHORTDATE
,
150 LOCALE_SDAYNAME1
, LOCALE_SDAYNAME2
, LOCALE_SDAYNAME3
,
151 LOCALE_SDAYNAME4
, LOCALE_SDAYNAME5
, LOCALE_SDAYNAME6
, LOCALE_SDAYNAME7
,
152 LOCALE_SABBREVDAYNAME1
, LOCALE_SABBREVDAYNAME2
, LOCALE_SABBREVDAYNAME3
,
153 LOCALE_SABBREVDAYNAME4
, LOCALE_SABBREVDAYNAME5
, LOCALE_SABBREVDAYNAME6
,
154 LOCALE_SABBREVDAYNAME7
,
155 LOCALE_SMONTHNAME1
, LOCALE_SMONTHNAME2
, LOCALE_SMONTHNAME3
,
156 LOCALE_SMONTHNAME4
, LOCALE_SMONTHNAME5
, LOCALE_SMONTHNAME6
,
157 LOCALE_SMONTHNAME7
, LOCALE_SMONTHNAME8
, LOCALE_SMONTHNAME9
,
158 LOCALE_SMONTHNAME10
, LOCALE_SMONTHNAME11
, LOCALE_SMONTHNAME12
,
159 LOCALE_SMONTHNAME1
| LOCALE_RETURN_GENITIVE_NAMES
,
160 LOCALE_SMONTHNAME2
| LOCALE_RETURN_GENITIVE_NAMES
,
161 LOCALE_SMONTHNAME3
| LOCALE_RETURN_GENITIVE_NAMES
,
162 LOCALE_SMONTHNAME4
| LOCALE_RETURN_GENITIVE_NAMES
,
163 LOCALE_SMONTHNAME5
| LOCALE_RETURN_GENITIVE_NAMES
,
164 LOCALE_SMONTHNAME6
| LOCALE_RETURN_GENITIVE_NAMES
,
165 LOCALE_SMONTHNAME7
| LOCALE_RETURN_GENITIVE_NAMES
,
166 LOCALE_SMONTHNAME8
| LOCALE_RETURN_GENITIVE_NAMES
,
167 LOCALE_SMONTHNAME9
| LOCALE_RETURN_GENITIVE_NAMES
,
168 LOCALE_SMONTHNAME10
| LOCALE_RETURN_GENITIVE_NAMES
,
169 LOCALE_SMONTHNAME11
| LOCALE_RETURN_GENITIVE_NAMES
,
170 LOCALE_SMONTHNAME12
| LOCALE_RETURN_GENITIVE_NAMES
,
171 LOCALE_SABBREVMONTHNAME1
, LOCALE_SABBREVMONTHNAME2
, LOCALE_SABBREVMONTHNAME3
,
172 LOCALE_SABBREVMONTHNAME4
, LOCALE_SABBREVMONTHNAME5
, LOCALE_SABBREVMONTHNAME6
,
173 LOCALE_SABBREVMONTHNAME7
, LOCALE_SABBREVMONTHNAME8
, LOCALE_SABBREVMONTHNAME9
,
174 LOCALE_SABBREVMONTHNAME10
, LOCALE_SABBREVMONTHNAME11
, LOCALE_SABBREVMONTHNAME12
,
175 LOCALE_S1159
, LOCALE_S2359
,
178 static NLS_FORMAT_NODE
*NLS_CachedFormats
= NULL
;
179 NLS_FORMAT_NODE
*node
= NLS_CachedFormats
;
181 dwFlags
&= LOCALE_NOUSEROVERRIDE
;
183 TRACE("(0x%04x,0x%08x)\n", lcid
, dwFlags
);
185 /* See if we have already cached the locales number format */
186 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
189 if (!node
|| node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
191 NLS_FORMAT_NODE
*new_node
;
194 TRACE("Creating new cache entry\n");
196 if (!(new_node
= HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE
))))
199 GET_LOCALE_NUMBER(new_node
->dwCodePage
, LOCALE_IDEFAULTANSICODEPAGE
);
202 new_node
->lcid
= lcid
;
203 new_node
->dwFlags
= dwFlags
;
204 new_node
->next
= NULL
;
206 GET_LOCALE_NUMBER(new_node
->fmt
.NumDigits
, LOCALE_IDIGITS
);
207 GET_LOCALE_NUMBER(new_node
->fmt
.LeadingZero
, LOCALE_ILZERO
);
208 GET_LOCALE_NUMBER(new_node
->fmt
.NegativeOrder
, LOCALE_INEGNUMBER
);
210 GET_LOCALE_NUMBER(new_node
->fmt
.Grouping
, LOCALE_SGROUPING
);
211 if (new_node
->fmt
.Grouping
> 9 && new_node
->fmt
.Grouping
!= 32)
213 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
214 new_node
->fmt
.Grouping
);
215 new_node
->fmt
.Grouping
= 0;
218 GET_LOCALE_STRING(new_node
->fmt
.lpDecimalSep
, LOCALE_SDECIMAL
);
219 GET_LOCALE_STRING(new_node
->fmt
.lpThousandSep
, LOCALE_STHOUSAND
);
221 /* Currency Format */
222 new_node
->cyfmt
.NumDigits
= new_node
->fmt
.NumDigits
;
223 new_node
->cyfmt
.LeadingZero
= new_node
->fmt
.LeadingZero
;
225 GET_LOCALE_NUMBER(new_node
->cyfmt
.Grouping
, LOCALE_SGROUPING
);
227 if (new_node
->cyfmt
.Grouping
> 9)
229 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
230 new_node
->cyfmt
.Grouping
);
231 new_node
->cyfmt
.Grouping
= 0;
234 GET_LOCALE_NUMBER(new_node
->cyfmt
.NegativeOrder
, LOCALE_INEGCURR
);
235 if (new_node
->cyfmt
.NegativeOrder
> 15)
237 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
238 new_node
->cyfmt
.NegativeOrder
);
239 new_node
->cyfmt
.NegativeOrder
= 0;
241 GET_LOCALE_NUMBER(new_node
->cyfmt
.PositiveOrder
, LOCALE_ICURRENCY
);
242 if (new_node
->cyfmt
.PositiveOrder
> 3)
244 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
245 new_node
->cyfmt
.PositiveOrder
);
246 new_node
->cyfmt
.PositiveOrder
= 0;
248 GET_LOCALE_STRING(new_node
->cyfmt
.lpDecimalSep
, LOCALE_SMONDECIMALSEP
);
249 GET_LOCALE_STRING(new_node
->cyfmt
.lpThousandSep
, LOCALE_SMONTHOUSANDSEP
);
250 GET_LOCALE_STRING(new_node
->cyfmt
.lpCurrencySymbol
, LOCALE_SCURRENCY
);
252 /* Date/Time Format info, negative character, etc */
253 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
255 GET_LOCALE_STRING(new_node
->lppszStrings
[i
], NLS_LocaleIndices
[i
]);
257 /* Save some memory if month genitive name is the same or not present */
258 for (i
= 0; i
< 12; i
++)
260 if (strcmpW(GetLongMonth(new_node
, i
), GetGenitiveMonth(new_node
, i
)) == 0)
262 HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node
, i
));
263 GetGenitiveMonth(new_node
, i
) = NULL
;
267 new_node
->szShortAM
[0] = GetAM(new_node
)[0]; new_node
->szShortAM
[1] = '\0';
268 new_node
->szShortPM
[0] = GetPM(new_node
)[0]; new_node
->szShortPM
[1] = '\0';
270 /* Now add the computed format to the cache */
271 RtlEnterCriticalSection(&NLS_FormatsCS
);
273 /* Search again: We may have raced to add the node */
274 node
= NLS_CachedFormats
;
275 while (node
&& (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
) && node
->next
)
280 node
= NLS_CachedFormats
= new_node
; /* Empty list */
283 else if (node
->lcid
!= lcid
|| node
->dwFlags
!= dwFlags
)
285 node
->next
= new_node
; /* Not in the list, add to end */
290 RtlLeaveCriticalSection(&NLS_FormatsCS
);
294 /* We raced and lost: The node was already added by another thread.
295 * node points to the currently cached node, so free new_node.
297 for (i
= 0; i
< sizeof(NLS_LocaleIndices
)/sizeof(NLS_LocaleIndices
[0]); i
++)
298 HeapFree(GetProcessHeap(), 0, new_node
->lppszStrings
[i
]);
299 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpDecimalSep
);
300 HeapFree(GetProcessHeap(), 0, new_node
->fmt
.lpThousandSep
);
301 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpDecimalSep
);
302 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpThousandSep
);
303 HeapFree(GetProcessHeap(), 0, new_node
->cyfmt
.lpCurrencySymbol
);
304 HeapFree(GetProcessHeap(), 0, new_node
);
310 /**************************************************************************
311 * NLS_IsUnicodeOnlyLcid <internal>
313 * Determine if a locale is Unicode only, and thus invalid in ASCII calls.
315 BOOL
NLS_IsUnicodeOnlyLcid(LCID lcid
)
317 lcid
= ConvertDefaultLocale(lcid
);
319 switch (PRIMARYLANGID(lcid
))
331 TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid
, PRIMARYLANGID(lcid
));
339 * Formatting of dates, times, numbers and currencies.
342 #define IsLiteralMarker(p) (p == '\'')
343 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
344 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
346 /* Only the following flags can be given if a date/time format is specified */
347 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY)
348 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
349 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
352 /******************************************************************************
353 * NLS_GetDateTimeFormatW <internal>
355 * Performs the formatting for GetDateFormatW/GetTimeFormatW.
358 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
359 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
361 static INT
NLS_GetDateTimeFormatW(LCID lcid
, DWORD dwFlags
,
362 const SYSTEMTIME
* lpTime
, LPCWSTR lpFormat
,
363 LPWSTR lpStr
, INT cchOut
)
365 const NLS_FORMAT_NODE
*node
;
368 INT lastFormatPos
= 0;
369 BOOL bSkipping
= FALSE
; /* Skipping text around marker? */
370 BOOL d_dd_formatted
= FALSE
; /* previous formatted part was for d or dd */
372 /* Verify our arguments */
373 if ((cchOut
&& !lpStr
) || !(node
= NLS_GetFormats(lcid
, dwFlags
)))
374 goto invalid_parameter
;
376 if (dwFlags
& ~(DATE_DATEVARSONLY
|TIME_TIMEVARSONLY
))
379 ((dwFlags
& DATE_DATEVARSONLY
&& dwFlags
& ~DATE_FORMAT_FLAGS
) ||
380 (dwFlags
& TIME_TIMEVARSONLY
&& dwFlags
& ~TIME_FORMAT_FLAGS
)))
385 if (dwFlags
& DATE_DATEVARSONLY
)
387 if ((dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
)) == (DATE_LTRREADING
|DATE_RTLREADING
))
389 else if (dwFlags
& (DATE_LTRREADING
|DATE_RTLREADING
))
390 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
392 switch (dwFlags
& (DATE_SHORTDATE
|DATE_LONGDATE
|DATE_YEARMONTH
))
410 /* Use the appropriate default format */
411 if (dwFlags
& DATE_DATEVARSONLY
)
413 if (dwFlags
& DATE_YEARMONTH
)
414 lpFormat
= GetYearMonth(node
);
415 else if (dwFlags
& DATE_LONGDATE
)
416 lpFormat
= GetLongDate(node
);
418 lpFormat
= GetShortDate(node
);
421 lpFormat
= GetTime(node
);
426 GetLocalTime(&st
); /* Default to current time */
431 if (dwFlags
& DATE_DATEVARSONLY
)
435 /* Verify the date and correct the D.O.W. if needed */
436 memset(&st
, 0, sizeof(st
));
437 st
.wYear
= lpTime
->wYear
;
438 st
.wMonth
= lpTime
->wMonth
;
439 st
.wDay
= lpTime
->wDay
;
441 if (st
.wDay
> 31 || st
.wMonth
> 12 || !SystemTimeToFileTime(&st
, &ftTmp
))
442 goto invalid_parameter
;
444 FileTimeToSystemTime(&ftTmp
, &st
);
448 if (dwFlags
& TIME_TIMEVARSONLY
)
450 /* Verify the time */
451 if (lpTime
->wHour
> 24 || lpTime
->wMinute
> 59 || lpTime
->wSecond
> 59)
452 goto invalid_parameter
;
456 /* Format the output */
459 if (IsLiteralMarker(*lpFormat
))
461 /* Start of a literal string */
464 /* Loop until the end of the literal marker or end of the string */
467 if (IsLiteralMarker(*lpFormat
))
470 if (!IsLiteralMarker(*lpFormat
))
471 break; /* Terminating literal marker */
475 cchWritten
++; /* Count size only */
476 else if (cchWritten
>= cchOut
)
480 lpStr
[cchWritten
] = *lpFormat
;
486 else if ((dwFlags
& DATE_DATEVARSONLY
&& IsDateFmtChar(*lpFormat
)) ||
487 (dwFlags
& TIME_TIMEVARSONLY
&& IsTimeFmtChar(*lpFormat
)))
489 WCHAR buff
[32], fmtChar
;
490 LPCWSTR szAdd
= NULL
;
492 int count
= 0, dwLen
;
497 while (*lpFormat
== fmtChar
)
504 if (fmtChar
!= 'M') d_dd_formatted
= FALSE
;
509 szAdd
= GetLongDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
511 szAdd
= GetShortDay(node
, (lpTime
->wDayOfWeek
+ 6) % 7);
514 dwVal
= lpTime
->wDay
;
516 d_dd_formatted
= TRUE
;
523 LPCWSTR genitive
= GetGenitiveMonth(node
, lpTime
->wMonth
- 1);
533 LPCWSTR format
= lpFormat
;
534 /* Look forward now, if next format pattern is for day genitive
535 name should be used */
538 /* Skip parts within markers */
539 if (IsLiteralMarker(*format
))
544 if (IsLiteralMarker(*format
))
547 if (!IsLiteralMarker(*format
)) break;
551 if (*format
!= ' ') break;
554 /* Only numeric day form matters */
555 if (*format
&& *format
== 'd')
558 while (*++format
== 'd') dcount
++;
567 szAdd
= GetLongMonth(node
, lpTime
->wMonth
- 1);
570 szAdd
= GetShortMonth(node
, lpTime
->wMonth
- 1);
573 dwVal
= lpTime
->wMonth
;
582 dwVal
= lpTime
->wYear
;
586 count
= count
> 2 ? 2 : count
;
587 dwVal
= lpTime
->wYear
% 100;
595 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING.
596 * When it is fixed, this string should be cached in 'node'.
598 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n");
599 buff
[0] = 'A'; buff
[1] = 'D'; buff
[2] = '\0';
603 buff
[0] = 'g'; buff
[1] = '\0'; /* Add a literal 'g' */
609 if (!(dwFlags
& TIME_FORCE24HOURFORMAT
))
611 count
= count
> 2 ? 2 : count
;
612 dwVal
= lpTime
->wHour
== 0 ? 12 : (lpTime
->wHour
- 1) % 12 + 1;
616 /* .. fall through if we are forced to output in 24 hour format */
619 count
= count
> 2 ? 2 : count
;
620 dwVal
= lpTime
->wHour
;
625 if (dwFlags
& TIME_NOMINUTESORSECONDS
)
627 cchWritten
= lastFormatPos
; /* Skip */
632 count
= count
> 2 ? 2 : count
;
633 dwVal
= lpTime
->wMinute
;
639 if (dwFlags
& (TIME_NOSECONDS
|TIME_NOMINUTESORSECONDS
))
641 cchWritten
= lastFormatPos
; /* Skip */
646 count
= count
> 2 ? 2 : count
;
647 dwVal
= lpTime
->wSecond
;
653 if (dwFlags
& TIME_NOTIMEMARKER
)
655 cchWritten
= lastFormatPos
; /* Skip */
661 szAdd
= lpTime
->wHour
< 12 ? node
->szShortAM
: node
->szShortPM
;
663 szAdd
= lpTime
->wHour
< 12 ? GetAM(node
) : GetPM(node
);
668 if (szAdd
== buff
&& buff
[0] == '\0')
670 static const WCHAR fmtW
[] = {'%','.','*','d',0};
671 /* We have a numeric value to add */
672 snprintfW(buff
, sizeof(buff
)/sizeof(WCHAR
), fmtW
, count
, dwVal
);
675 dwLen
= szAdd
? strlenW(szAdd
) : 0;
679 if (cchWritten
+ dwLen
< cchOut
)
680 memcpy(lpStr
+ cchWritten
, szAdd
, dwLen
* sizeof(WCHAR
));
683 memcpy(lpStr
+ cchWritten
, szAdd
, (cchOut
- cchWritten
) * sizeof(WCHAR
));
688 lastFormatPos
= cchWritten
; /* Save position of last output format text */
692 /* Literal character */
694 cchWritten
++; /* Count size only */
695 else if (cchWritten
>= cchOut
)
697 else if (!bSkipping
|| *lpFormat
== ' ')
699 lpStr
[cchWritten
] = *lpFormat
;
706 /* Final string terminator and sanity check */
709 if (cchWritten
>= cchOut
)
712 lpStr
[cchWritten
] = '\0';
714 cchWritten
++; /* Include terminating NUL */
716 TRACE("returning length=%d, ouput=%s\n", cchWritten
, debugstr_w(lpStr
));
720 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n");
721 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
725 SetLastError(ERROR_INVALID_PARAMETER
);
729 SetLastError(ERROR_INVALID_FLAGS
);
733 /******************************************************************************
734 * NLS_GetDateTimeFormatA <internal>
736 * ASCII wrapper for GetDateFormatA/GetTimeFormatA.
738 static INT
NLS_GetDateTimeFormatA(LCID lcid
, DWORD dwFlags
,
739 const SYSTEMTIME
* lpTime
,
740 LPCSTR lpFormat
, LPSTR lpStr
, INT cchOut
)
743 WCHAR szFormat
[128], szOut
[128];
746 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
747 debugstr_a(lpFormat
), lpStr
, cchOut
);
749 if (NLS_IsUnicodeOnlyLcid(lcid
))
751 SetLastError(ERROR_INVALID_PARAMETER
);
755 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
757 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
760 SetLastError(ERROR_INVALID_PARAMETER
);
764 cp
= node
->dwCodePage
;
768 MultiByteToWideChar(cp
, 0, lpFormat
, -1, szFormat
, sizeof(szFormat
)/sizeof(WCHAR
));
770 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
771 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
775 iRet
= NLS_GetDateTimeFormatW(lcid
, dwFlags
, lpTime
, lpFormat
? szFormat
: NULL
,
776 lpStr
? szOut
: NULL
, cchOut
);
781 WideCharToMultiByte(cp
, 0, szOut
, iRet
? -1 : cchOut
, lpStr
, cchOut
, 0, 0);
782 else if (cchOut
&& iRet
)
788 /******************************************************************************
789 * GetDateFormatA [KERNEL32.@]
791 * Format a date for a given locale.
794 * lcid [I] Locale to format for
795 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h"
796 * lpTime [I] Date to format
797 * lpFormat [I] Format string, or NULL to use the system defaults
798 * lpDateStr [O] Destination for formatted string
799 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size
802 * - If lpFormat is NULL, lpDateStr will be formatted according to the format
803 * details returned by GetLocaleInfoA() and modified by dwFlags.
804 * - lpFormat is a string of characters and formatting tokens. Any characters
805 * in the string are copied verbatim to lpDateStr, with tokens being replaced
806 * by the date values they represent.
807 * - The following tokens have special meanings in a date format string:
810 *| d Single digit day of the month (no leading 0)
811 *| dd Double digit day of the month
812 *| ddd Short name for the day of the week
813 *| dddd Long name for the day of the week
814 *| M Single digit month of the year (no leading 0)
815 *| MM Double digit month of the year
816 *| MMM Short name for the month of the year
817 *| MMMM Long name for the month of the year
818 *| y Double digit year number (no leading 0)
819 *| yy Double digit year number
820 *| yyyy Four digit year number
821 *| gg Era string, for example 'AD'.
822 * - To output any literal character that could be misidentified as a token,
823 * enclose it in single quotes.
824 * - The Ascii version of this function fails if lcid is Unicode only.
827 * Success: The number of character written to lpDateStr, or that would
828 * have been written, if cchOut is 0.
829 * Failure: 0. Use GetLastError() to determine the cause.
831 INT WINAPI
GetDateFormatA( LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
832 LPCSTR lpFormat
, LPSTR lpDateStr
, INT cchOut
)
834 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
835 debugstr_a(lpFormat
), lpDateStr
, cchOut
);
837 return NLS_GetDateTimeFormatA(lcid
, dwFlags
| DATE_DATEVARSONLY
, lpTime
,
838 lpFormat
, lpDateStr
, cchOut
);
842 /******************************************************************************
843 * GetDateFormatW [KERNEL32.@]
845 * See GetDateFormatA.
847 INT WINAPI
GetDateFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
848 LPCWSTR lpFormat
, LPWSTR lpDateStr
, INT cchOut
)
850 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid
, dwFlags
, lpTime
,
851 debugstr_w(lpFormat
), lpDateStr
, cchOut
);
853 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|DATE_DATEVARSONLY
, lpTime
,
854 lpFormat
, lpDateStr
, cchOut
);
857 /******************************************************************************
858 * GetTimeFormatA [KERNEL32.@]
860 * Format a time for a given locale.
863 * lcid [I] Locale to format for
864 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h"
865 * lpTime [I] Time to format
866 * lpFormat [I] Formatting overrides
867 * lpTimeStr [O] Destination for formatted string
868 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size
871 * - If lpFormat is NULL, lpszValue will be formatted according to the format
872 * details returned by GetLocaleInfoA() and modified by dwFlags.
873 * - lpFormat is a string of characters and formatting tokens. Any characters
874 * in the string are copied verbatim to lpTimeStr, with tokens being replaced
875 * by the time values they represent.
876 * - The following tokens have special meanings in a time format string:
879 *| h Hours with no leading zero (12-hour clock)
880 *| hh Hours with full two digits (12-hour clock)
881 *| H Hours with no leading zero (24-hour clock)
882 *| HH Hours with full two digits (24-hour clock)
883 *| m Minutes with no leading zero
884 *| mm Minutes with full two digits
885 *| s Seconds with no leading zero
886 *| ss Seconds with full two digits
887 *| t Short time marker (e.g. "A" or "P")
888 *| tt Long time marker (e.g. "AM", "PM")
889 * - To output any literal character that could be misidentified as a token,
890 * enclose it in single quotes.
891 * - The Ascii version of this function fails if lcid is Unicode only.
894 * Success: The number of character written to lpTimeStr, or that would
895 * have been written, if cchOut is 0.
896 * Failure: 0. Use GetLastError() to determine the cause.
898 INT WINAPI
GetTimeFormatA(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
899 LPCSTR lpFormat
, LPSTR lpTimeStr
, INT cchOut
)
901 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
902 debugstr_a(lpFormat
), lpTimeStr
, cchOut
);
904 return NLS_GetDateTimeFormatA(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
905 lpFormat
, lpTimeStr
, cchOut
);
908 /******************************************************************************
909 * GetTimeFormatW [KERNEL32.@]
911 * See GetTimeFormatA.
913 INT WINAPI
GetTimeFormatW(LCID lcid
, DWORD dwFlags
, const SYSTEMTIME
* lpTime
,
914 LPCWSTR lpFormat
, LPWSTR lpTimeStr
, INT cchOut
)
916 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid
, dwFlags
, lpTime
,
917 debugstr_w(lpFormat
), lpTimeStr
, cchOut
);
919 return NLS_GetDateTimeFormatW(lcid
, dwFlags
|TIME_TIMEVARSONLY
, lpTime
,
920 lpFormat
, lpTimeStr
, cchOut
);
923 /**************************************************************************
924 * GetNumberFormatA (KERNEL32.@)
926 * Format a number string for a given locale.
929 * lcid [I] Locale to format for
930 * dwFlags [I] LOCALE_ flags from "winnls.h"
931 * lpszValue [I] String to format
932 * lpFormat [I] Formatting overrides
933 * lpNumberStr [O] Destination for formatted string
934 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size
937 * - lpszValue can contain only '0' - '9', '-' and '.'.
938 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
939 * be formatted according to the format details returned by GetLocaleInfoA().
940 * - This function rounds the number string if the number of decimals exceeds the
941 * locales normal number of decimal places.
942 * - If cchOut is 0, this function does not write to lpNumberStr.
943 * - The Ascii version of this function fails if lcid is Unicode only.
946 * Success: The number of character written to lpNumberStr, or that would
947 * have been written, if cchOut is 0.
948 * Failure: 0. Use GetLastError() to determine the cause.
950 INT WINAPI
GetNumberFormatA(LCID lcid
, DWORD dwFlags
,
951 LPCSTR lpszValue
, const NUMBERFMTA
*lpFormat
,
952 LPSTR lpNumberStr
, int cchOut
)
955 WCHAR szDec
[8], szGrp
[8], szIn
[128], szOut
[128];
957 const NUMBERFMTW
*pfmt
= NULL
;
960 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
961 lpFormat
, lpNumberStr
, cchOut
);
963 if (NLS_IsUnicodeOnlyLcid(lcid
))
965 SetLastError(ERROR_INVALID_PARAMETER
);
969 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
971 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
974 SetLastError(ERROR_INVALID_PARAMETER
);
978 cp
= node
->dwCodePage
;
983 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
985 if (lpFormat
->lpDecimalSep
)
987 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
988 fmt
.lpDecimalSep
= szDec
;
990 if (lpFormat
->lpThousandSep
)
992 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
993 fmt
.lpThousandSep
= szGrp
;
998 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1000 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1001 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1005 iRet
= GetNumberFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1006 lpNumberStr
? szOut
: NULL
, cchOut
);
1008 if (szOut
[0] && lpNumberStr
)
1009 WideCharToMultiByte(cp
, 0, szOut
, -1, lpNumberStr
, cchOut
, 0, 0);
1013 /* Number parsing state flags */
1014 #define NF_ISNEGATIVE 0x1 /* '-' found */
1015 #define NF_ISREAL 0x2 /* '.' found */
1016 #define NF_DIGITS 0x4 /* '0'-'9' found */
1017 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */
1018 #define NF_ROUND 0x10 /* Number needs to be rounded */
1020 /* Formatting options for Numbers */
1021 #define NLS_NEG_PARENS 0 /* "(1.1)" */
1022 #define NLS_NEG_LEFT 1 /* "-1.1" */
1023 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */
1024 #define NLS_NEG_RIGHT 3 /* "1.1-" */
1025 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */
1027 /**************************************************************************
1028 * GetNumberFormatW (KERNEL32.@)
1030 * See GetNumberFormatA.
1032 INT WINAPI
GetNumberFormatW(LCID lcid
, DWORD dwFlags
,
1033 LPCWSTR lpszValue
, const NUMBERFMTW
*lpFormat
,
1034 LPWSTR lpNumberStr
, int cchOut
)
1036 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1038 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
;
1039 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0;
1042 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1043 lpFormat
, lpNumberStr
, cchOut
);
1045 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpNumberStr
) ||
1046 !IsValidLocale(lcid
, 0) ||
1047 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
)))
1054 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1058 lpFormat
= &node
->fmt
;
1059 lpszNegStart
= lpszNeg
= GetNegative(node
);
1063 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1064 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1065 lpszNegStart
= lpszNeg
= szNegBuff
;
1067 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1069 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1071 /* Format the number backwards into a temporary buffer */
1076 /* Check the number for validity */
1079 if (*szSrc
>= '0' && *szSrc
<= '9')
1081 dwState
|= NF_DIGITS
;
1082 if (dwState
& NF_ISREAL
)
1085 else if (*szSrc
== '-')
1088 goto error
; /* '-' not first character */
1089 dwState
|= NF_ISNEGATIVE
;
1091 else if (*szSrc
== '.')
1093 if (dwState
& NF_ISREAL
)
1094 goto error
; /* More than one '.' */
1095 dwState
|= NF_ISREAL
;
1098 goto error
; /* Invalid char */
1101 szSrc
--; /* Point to last character */
1103 if (!(dwState
& NF_DIGITS
))
1104 goto error
; /* No digits */
1106 /* Add any trailing negative sign */
1107 if (dwState
& NF_ISNEGATIVE
)
1109 switch (lpFormat
->NegativeOrder
)
1111 case NLS_NEG_PARENS
:
1115 case NLS_NEG_RIGHT_SPACE
:
1116 while (lpszNeg
>= lpszNegStart
)
1117 *szOut
-- = *lpszNeg
--;
1118 if (lpFormat
->NegativeOrder
== NLS_NEG_RIGHT_SPACE
)
1124 /* Copy all digits up to the decimal point */
1125 if (!lpFormat
->NumDigits
)
1127 if (dwState
& NF_ISREAL
)
1129 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1131 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1132 dwState
|= NF_ROUND
;
1134 dwState
&= ~NF_ROUND
;
1142 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1144 if (dwDecimals
<= lpFormat
->NumDigits
)
1146 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1147 while (dwDecimals
--)
1148 *szOut
-- = '0'; /* Pad to correct number of dp */
1152 dwDecimals
-= lpFormat
->NumDigits
;
1153 /* Skip excess decimals, and determine if we have to round the number */
1154 while (dwDecimals
--)
1156 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1157 dwState
|= NF_ROUND
;
1159 dwState
&= ~NF_ROUND
;
1164 if (dwState
& NF_ISREAL
)
1166 while (*szSrc
!= '.')
1168 if (dwState
& NF_ROUND
)
1171 *szOut
-- = '0'; /* continue rounding */
1174 dwState
&= ~NF_ROUND
;
1175 *szOut
-- = (*szSrc
)+1;
1180 *szOut
-- = *szSrc
--; /* Write existing decimals */
1182 szSrc
--; /* Skip '.' */
1185 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1186 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1189 dwGroupCount
= lpFormat
->Grouping
== 32 ? 3 : lpFormat
->Grouping
;
1191 /* Write the remaining whole number digits, including grouping chars */
1192 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1194 if (dwState
& NF_ROUND
)
1197 *szOut
-- = '0'; /* continue rounding */
1200 dwState
&= ~NF_ROUND
;
1201 *szOut
-- = (*szSrc
)+1;
1206 *szOut
-- = *szSrc
--;
1208 dwState
|= NF_DIGITS_OUT
;
1209 dwCurrentGroupCount
++;
1210 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1212 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1214 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1215 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1217 dwCurrentGroupCount
= 0;
1218 if (lpFormat
->Grouping
== 32)
1219 dwGroupCount
= 2; /* Indic grouping: 3 then 2 */
1222 if (dwState
& NF_ROUND
)
1224 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1226 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1227 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1229 /* Add any leading negative sign */
1230 if (dwState
& NF_ISNEGATIVE
)
1232 switch (lpFormat
->NegativeOrder
)
1234 case NLS_NEG_PARENS
:
1237 case NLS_NEG_LEFT_SPACE
:
1241 while (lpszNeg
>= lpszNegStart
)
1242 *szOut
-- = *lpszNeg
--;
1248 iRet
= strlenW(szOut
) + 1;
1252 memcpy(lpNumberStr
, szOut
, iRet
* sizeof(WCHAR
));
1255 memcpy(lpNumberStr
, szOut
, cchOut
* sizeof(WCHAR
));
1256 lpNumberStr
[cchOut
- 1] = '\0';
1257 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1264 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1268 /**************************************************************************
1269 * GetCurrencyFormatA (KERNEL32.@)
1271 * Format a currency string for a given locale.
1274 * lcid [I] Locale to format for
1275 * dwFlags [I] LOCALE_ flags from "winnls.h"
1276 * lpszValue [I] String to format
1277 * lpFormat [I] Formatting overrides
1278 * lpCurrencyStr [O] Destination for formatted string
1279 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size
1282 * - lpszValue can contain only '0' - '9', '-' and '.'.
1283 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will
1284 * be formatted according to the format details returned by GetLocaleInfoA().
1285 * - This function rounds the currency if the number of decimals exceeds the
1286 * locales number of currency decimal places.
1287 * - If cchOut is 0, this function does not write to lpCurrencyStr.
1288 * - The Ascii version of this function fails if lcid is Unicode only.
1291 * Success: The number of character written to lpNumberStr, or that would
1292 * have been written, if cchOut is 0.
1293 * Failure: 0. Use GetLastError() to determine the cause.
1295 INT WINAPI
GetCurrencyFormatA(LCID lcid
, DWORD dwFlags
,
1296 LPCSTR lpszValue
, const CURRENCYFMTA
*lpFormat
,
1297 LPSTR lpCurrencyStr
, int cchOut
)
1300 WCHAR szDec
[8], szGrp
[8], szCy
[8], szIn
[128], szOut
[128];
1302 const CURRENCYFMTW
*pfmt
= NULL
;
1305 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_a(lpszValue
),
1306 lpFormat
, lpCurrencyStr
, cchOut
);
1308 if (NLS_IsUnicodeOnlyLcid(lcid
))
1310 SetLastError(ERROR_INVALID_PARAMETER
);
1314 if (!(dwFlags
& LOCALE_USE_CP_ACP
))
1316 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1319 SetLastError(ERROR_INVALID_PARAMETER
);
1323 cp
= node
->dwCodePage
;
1328 memcpy(&fmt
, lpFormat
, sizeof(fmt
));
1330 if (lpFormat
->lpDecimalSep
)
1332 MultiByteToWideChar(cp
, 0, lpFormat
->lpDecimalSep
, -1, szDec
, sizeof(szDec
)/sizeof(WCHAR
));
1333 fmt
.lpDecimalSep
= szDec
;
1335 if (lpFormat
->lpThousandSep
)
1337 MultiByteToWideChar(cp
, 0, lpFormat
->lpThousandSep
, -1, szGrp
, sizeof(szGrp
)/sizeof(WCHAR
));
1338 fmt
.lpThousandSep
= szGrp
;
1340 if (lpFormat
->lpCurrencySymbol
)
1342 MultiByteToWideChar(cp
, 0, lpFormat
->lpCurrencySymbol
, -1, szCy
, sizeof(szCy
)/sizeof(WCHAR
));
1343 fmt
.lpCurrencySymbol
= szCy
;
1348 MultiByteToWideChar(cp
, 0, lpszValue
, -1, szIn
, sizeof(szIn
)/sizeof(WCHAR
));
1350 if (cchOut
> (int)(sizeof(szOut
)/sizeof(WCHAR
)))
1351 cchOut
= sizeof(szOut
)/sizeof(WCHAR
);
1355 iRet
= GetCurrencyFormatW(lcid
, dwFlags
, lpszValue
? szIn
: NULL
, pfmt
,
1356 lpCurrencyStr
? szOut
: NULL
, cchOut
);
1358 if (szOut
[0] && lpCurrencyStr
)
1359 WideCharToMultiByte(cp
, 0, szOut
, -1, lpCurrencyStr
, cchOut
, 0, 0);
1363 /* Formatting states for Currencies. We use flags to avoid code duplication. */
1364 #define CF_PARENS 0x1 /* Parentheses */
1365 #define CF_MINUS_LEFT 0x2 /* '-' to the left */
1366 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */
1367 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */
1368 #define CF_CY_LEFT 0x10 /* '$' to the left */
1369 #define CF_CY_RIGHT 0x20 /* '$' to the right */
1370 #define CF_CY_SPACE 0x40 /* ' ' by '$' */
1372 /**************************************************************************
1373 * GetCurrencyFormatW (KERNEL32.@)
1375 * See GetCurrencyFormatA.
1377 INT WINAPI
GetCurrencyFormatW(LCID lcid
, DWORD dwFlags
,
1378 LPCWSTR lpszValue
, const CURRENCYFMTW
*lpFormat
,
1379 LPWSTR lpCurrencyStr
, int cchOut
)
1381 static const BYTE NLS_NegCyFormats
[16] =
1383 CF_PARENS
|CF_CY_LEFT
, /* ($1.1) */
1384 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
, /* -$1.1 */
1385 CF_MINUS_LEFT
|CF_CY_LEFT
, /* $-1.1 */
1386 CF_MINUS_RIGHT
|CF_CY_LEFT
, /* $1.1- */
1387 CF_PARENS
|CF_CY_RIGHT
, /* (1.1$) */
1388 CF_MINUS_LEFT
|CF_CY_RIGHT
, /* -1.1$ */
1389 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
, /* 1.1-$ */
1390 CF_MINUS_RIGHT
|CF_CY_RIGHT
, /* 1.1$- */
1391 CF_MINUS_LEFT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* -1.1 $ */
1392 CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
|CF_CY_SPACE
, /* -$ 1.1 */
1393 CF_MINUS_RIGHT
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $- */
1394 CF_MINUS_RIGHT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1- */
1395 CF_MINUS_LEFT
|CF_CY_LEFT
|CF_CY_SPACE
, /* $ -1.1 */
1396 CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1- $ */
1397 CF_PARENS
|CF_CY_LEFT
|CF_CY_SPACE
, /* ($ 1.1) */
1398 CF_PARENS
|CF_CY_RIGHT
|CF_CY_SPACE
, /* (1.1 $) */
1400 static const BYTE NLS_PosCyFormats
[4] =
1402 CF_CY_LEFT
, /* $1.1 */
1403 CF_CY_RIGHT
, /* 1.1$ */
1404 CF_CY_LEFT
|CF_CY_SPACE
, /* $ 1.1 */
1405 CF_CY_RIGHT
|CF_CY_SPACE
, /* 1.1 $ */
1407 WCHAR szBuff
[128], *szOut
= szBuff
+ sizeof(szBuff
) / sizeof(WCHAR
) - 1;
1409 const WCHAR
*lpszNeg
= NULL
, *lpszNegStart
, *szSrc
, *lpszCy
, *lpszCyStart
;
1410 DWORD dwState
= 0, dwDecimals
= 0, dwGroupCount
= 0, dwCurrentGroupCount
= 0, dwFmt
;
1413 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid
, dwFlags
, debugstr_w(lpszValue
),
1414 lpFormat
, lpCurrencyStr
, cchOut
);
1416 if (!lpszValue
|| cchOut
< 0 || (cchOut
> 0 && !lpCurrencyStr
) ||
1417 !IsValidLocale(lcid
, 0) ||
1418 (lpFormat
&& (dwFlags
|| !lpFormat
->lpDecimalSep
|| !lpFormat
->lpThousandSep
||
1419 !lpFormat
->lpCurrencySymbol
|| lpFormat
->NegativeOrder
> 15 ||
1420 lpFormat
->PositiveOrder
> 3)))
1427 const NLS_FORMAT_NODE
*node
= NLS_GetFormats(lcid
, dwFlags
);
1432 lpFormat
= &node
->cyfmt
;
1433 lpszNegStart
= lpszNeg
= GetNegative(node
);
1437 GetLocaleInfoW(lcid
, LOCALE_SNEGATIVESIGN
|(dwFlags
& LOCALE_NOUSEROVERRIDE
),
1438 szNegBuff
, sizeof(szNegBuff
)/sizeof(WCHAR
));
1439 lpszNegStart
= lpszNeg
= szNegBuff
;
1441 dwFlags
&= (LOCALE_NOUSEROVERRIDE
|LOCALE_USE_CP_ACP
);
1443 lpszNeg
= lpszNeg
+ strlenW(lpszNeg
) - 1;
1444 lpszCyStart
= lpFormat
->lpCurrencySymbol
;
1445 lpszCy
= lpszCyStart
+ strlenW(lpszCyStart
) - 1;
1447 /* Format the currency backwards into a temporary buffer */
1452 /* Check the number for validity */
1455 if (*szSrc
>= '0' && *szSrc
<= '9')
1457 dwState
|= NF_DIGITS
;
1458 if (dwState
& NF_ISREAL
)
1461 else if (*szSrc
== '-')
1464 goto error
; /* '-' not first character */
1465 dwState
|= NF_ISNEGATIVE
;
1467 else if (*szSrc
== '.')
1469 if (dwState
& NF_ISREAL
)
1470 goto error
; /* More than one '.' */
1471 dwState
|= NF_ISREAL
;
1474 goto error
; /* Invalid char */
1477 szSrc
--; /* Point to last character */
1479 if (!(dwState
& NF_DIGITS
))
1480 goto error
; /* No digits */
1482 if (dwState
& NF_ISNEGATIVE
)
1483 dwFmt
= NLS_NegCyFormats
[lpFormat
->NegativeOrder
];
1485 dwFmt
= NLS_PosCyFormats
[lpFormat
->PositiveOrder
];
1487 /* Add any trailing negative or currency signs */
1488 if (dwFmt
& CF_PARENS
)
1491 while (dwFmt
& (CF_MINUS_RIGHT
|CF_CY_RIGHT
))
1493 switch (dwFmt
& (CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
))
1495 case CF_MINUS_RIGHT
:
1496 case CF_MINUS_RIGHT
|CF_CY_RIGHT
:
1497 while (lpszNeg
>= lpszNegStart
)
1498 *szOut
-- = *lpszNeg
--;
1499 dwFmt
&= ~CF_MINUS_RIGHT
;
1503 case CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1504 case CF_MINUS_RIGHT
|CF_MINUS_BEFORE
|CF_CY_RIGHT
:
1505 while (lpszCy
>= lpszCyStart
)
1506 *szOut
-- = *lpszCy
--;
1507 if (dwFmt
& CF_CY_SPACE
)
1509 dwFmt
&= ~(CF_CY_RIGHT
|CF_MINUS_BEFORE
);
1514 /* Copy all digits up to the decimal point */
1515 if (!lpFormat
->NumDigits
)
1517 if (dwState
& NF_ISREAL
)
1519 while (*szSrc
!= '.') /* Don't write any decimals or a separator */
1521 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1522 dwState
|= NF_ROUND
;
1524 dwState
&= ~NF_ROUND
;
1532 LPWSTR lpszDec
= lpFormat
->lpDecimalSep
+ strlenW(lpFormat
->lpDecimalSep
) - 1;
1534 if (dwDecimals
<= lpFormat
->NumDigits
)
1536 dwDecimals
= lpFormat
->NumDigits
- dwDecimals
;
1537 while (dwDecimals
--)
1538 *szOut
-- = '0'; /* Pad to correct number of dp */
1542 dwDecimals
-= lpFormat
->NumDigits
;
1543 /* Skip excess decimals, and determine if we have to round the number */
1544 while (dwDecimals
--)
1546 if (*szSrc
>= '5' || (*szSrc
== '4' && (dwState
& NF_ROUND
)))
1547 dwState
|= NF_ROUND
;
1549 dwState
&= ~NF_ROUND
;
1554 if (dwState
& NF_ISREAL
)
1556 while (*szSrc
!= '.')
1558 if (dwState
& NF_ROUND
)
1561 *szOut
-- = '0'; /* continue rounding */
1564 dwState
&= ~NF_ROUND
;
1565 *szOut
-- = (*szSrc
)+1;
1570 *szOut
-- = *szSrc
--; /* Write existing decimals */
1572 szSrc
--; /* Skip '.' */
1574 while (lpszDec
>= lpFormat
->lpDecimalSep
)
1575 *szOut
-- = *lpszDec
--; /* Write decimal separator */
1578 dwGroupCount
= lpFormat
->Grouping
;
1580 /* Write the remaining whole number digits, including grouping chars */
1581 while (szSrc
>= lpszValue
&& *szSrc
>= '0' && *szSrc
<= '9')
1583 if (dwState
& NF_ROUND
)
1586 *szOut
-- = '0'; /* continue rounding */
1589 dwState
&= ~NF_ROUND
;
1590 *szOut
-- = (*szSrc
)+1;
1595 *szOut
-- = *szSrc
--;
1597 dwState
|= NF_DIGITS_OUT
;
1598 dwCurrentGroupCount
++;
1599 if (szSrc
>= lpszValue
&& dwCurrentGroupCount
== dwGroupCount
&& *szSrc
!= '-')
1601 LPWSTR lpszGrp
= lpFormat
->lpThousandSep
+ strlenW(lpFormat
->lpThousandSep
) - 1;
1603 while (lpszGrp
>= lpFormat
->lpThousandSep
)
1604 *szOut
-- = *lpszGrp
--; /* Write grouping char */
1606 dwCurrentGroupCount
= 0;
1609 if (dwState
& NF_ROUND
)
1610 *szOut
-- = '1'; /* e.g. .6 > 1.0 */
1611 else if (!(dwState
& NF_DIGITS_OUT
) && lpFormat
->LeadingZero
)
1612 *szOut
-- = '0'; /* Add leading 0 if we have no digits before the decimal point */
1614 /* Add any leading negative or currency sign */
1615 while (dwFmt
& (CF_MINUS_LEFT
|CF_CY_LEFT
))
1617 switch (dwFmt
& (CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
))
1620 case CF_MINUS_LEFT
|CF_CY_LEFT
:
1621 while (lpszNeg
>= lpszNegStart
)
1622 *szOut
-- = *lpszNeg
--;
1623 dwFmt
&= ~CF_MINUS_LEFT
;
1627 case CF_CY_LEFT
|CF_MINUS_BEFORE
:
1628 case CF_MINUS_LEFT
|CF_MINUS_BEFORE
|CF_CY_LEFT
:
1629 if (dwFmt
& CF_CY_SPACE
)
1631 while (lpszCy
>= lpszCyStart
)
1632 *szOut
-- = *lpszCy
--;
1633 dwFmt
&= ~(CF_CY_LEFT
|CF_MINUS_BEFORE
);
1637 if (dwFmt
& CF_PARENS
)
1641 iRet
= strlenW(szOut
) + 1;
1645 memcpy(lpCurrencyStr
, szOut
, iRet
* sizeof(WCHAR
));
1648 memcpy(lpCurrencyStr
, szOut
, cchOut
* sizeof(WCHAR
));
1649 lpCurrencyStr
[cchOut
- 1] = '\0';
1650 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
1657 SetLastError(lpFormat
&& dwFlags
? ERROR_INVALID_FLAGS
: ERROR_INVALID_PARAMETER
);
1661 /* FIXME: Everything below here needs to move somewhere else along with the
1662 * other EnumXXX functions, when a method for storing resources for
1663 * alternate calendars is determined.
1666 /**************************************************************************
1667 * EnumDateFormatsExA (KERNEL32.@)
1669 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1670 * LOCALE_NOUSEROVERRIDE here as well?
1672 BOOL WINAPI
EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc
, LCID lcid
, DWORD flags
)
1679 SetLastError(ERROR_INVALID_PARAMETER
);
1683 if (!GetLocaleInfoW(lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
)))
1686 switch (flags
& ~LOCALE_USE_CP_ACP
)
1689 case DATE_SHORTDATE
:
1690 if (GetLocaleInfoA(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1695 if (GetLocaleInfoA(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1699 case DATE_YEARMONTH
:
1700 if (GetLocaleInfoA(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1705 FIXME("Unknown date format (%d)\n", flags
);
1706 SetLastError(ERROR_INVALID_PARAMETER
);
1712 /**************************************************************************
1713 * EnumDateFormatsExW (KERNEL32.@)
1715 BOOL WINAPI
EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc
, LCID lcid
, DWORD flags
)
1722 SetLastError(ERROR_INVALID_PARAMETER
);
1726 if (!GetLocaleInfoW(lcid
, LOCALE_ICALENDARTYPE
|LOCALE_RETURN_NUMBER
, (LPWSTR
)&cal_id
, sizeof(cal_id
)/sizeof(WCHAR
)))
1729 switch (flags
& ~LOCALE_USE_CP_ACP
)
1732 case DATE_SHORTDATE
:
1733 if (GetLocaleInfoW(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1738 if (GetLocaleInfoW(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1742 case DATE_YEARMONTH
:
1743 if (GetLocaleInfoW(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1748 FIXME("Unknown date format (%d)\n", flags
);
1749 SetLastError(ERROR_INVALID_PARAMETER
);
1755 /**************************************************************************
1756 * EnumDateFormatsA (KERNEL32.@)
1758 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1759 * LOCALE_NOUSEROVERRIDE here as well?
1761 BOOL WINAPI
EnumDateFormatsA(DATEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
1767 SetLastError(ERROR_INVALID_PARAMETER
);
1771 switch (flags
& ~LOCALE_USE_CP_ACP
)
1774 case DATE_SHORTDATE
:
1775 if (GetLocaleInfoA(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1780 if (GetLocaleInfoA(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1784 case DATE_YEARMONTH
:
1785 if (GetLocaleInfoA(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1790 FIXME("Unknown date format (%d)\n", flags
);
1791 SetLastError(ERROR_INVALID_PARAMETER
);
1797 /**************************************************************************
1798 * EnumDateFormatsW (KERNEL32.@)
1800 BOOL WINAPI
EnumDateFormatsW(DATEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
1806 SetLastError(ERROR_INVALID_PARAMETER
);
1810 switch (flags
& ~LOCALE_USE_CP_ACP
)
1813 case DATE_SHORTDATE
:
1814 if (GetLocaleInfoW(lcid
, LOCALE_SSHORTDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1819 if (GetLocaleInfoW(lcid
, LOCALE_SLONGDATE
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1823 case DATE_YEARMONTH
:
1824 if (GetLocaleInfoW(lcid
, LOCALE_SYEARMONTH
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1829 FIXME("Unknown date format (%d)\n", flags
);
1830 SetLastError(ERROR_INVALID_PARAMETER
);
1836 /**************************************************************************
1837 * EnumTimeFormatsA (KERNEL32.@)
1839 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle
1840 * LOCALE_NOUSEROVERRIDE here as well?
1842 BOOL WINAPI
EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc
, LCID lcid
, DWORD flags
)
1848 SetLastError(ERROR_INVALID_PARAMETER
);
1852 switch (flags
& ~LOCALE_USE_CP_ACP
)
1855 if (GetLocaleInfoA(lcid
, LOCALE_STIMEFORMAT
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1860 FIXME("Unknown time format (%d)\n", flags
);
1861 SetLastError(ERROR_INVALID_PARAMETER
);
1867 /**************************************************************************
1868 * EnumTimeFormatsW (KERNEL32.@)
1870 BOOL WINAPI
EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc
, LCID lcid
, DWORD flags
)
1876 SetLastError(ERROR_INVALID_PARAMETER
);
1880 switch (flags
& ~LOCALE_USE_CP_ACP
)
1883 if (GetLocaleInfoW(lcid
, LOCALE_STIMEFORMAT
| (flags
& LOCALE_USE_CP_ACP
), buf
, 256))
1888 FIXME("Unknown time format (%d)\n", flags
);
1889 SetLastError(ERROR_INVALID_PARAMETER
);
1895 /******************************************************************************
1896 * NLS_EnumCalendarInfoAW <internal>
1897 * Enumerates calendar information for a specified locale.
1900 * calinfoproc [I] Pointer to the callback
1901 * locale [I] The locale for which to retrieve calendar information.
1902 * This parameter can be a locale identifier created by the
1903 * MAKELCID macro, or one of the following values:
1904 * LOCALE_SYSTEM_DEFAULT
1905 * Use the default system locale.
1906 * LOCALE_USER_DEFAULT
1907 * Use the default user locale.
1908 * calendar [I] The calendar for which information is requested, or
1909 * ENUM_ALL_CALENDARS.
1910 * caltype [I] The type of calendar information to be returned. Note
1911 * that only one CALTYPE value can be specified per call
1912 * of this function, except where noted.
1913 * unicode [I] Specifies if the callback expects a unicode string.
1914 * ex [I] Specifies if the callback needs the calendar identifier.
1918 * Failure: FALSE. Use GetLastError() to determine the cause.
1921 * When the ANSI version of this function is used with a Unicode-only LCID,
1922 * the call can succeed because the system uses the system code page.
1923 * However, characters that are undefined in the system code page appear
1924 * in the string as a question mark (?).
1927 * The above note should be respected by GetCalendarInfoA.
1929 static BOOL
NLS_EnumCalendarInfoAW(void *calinfoproc
, LCID locale
,
1930 CALID calendar
, CALTYPE caltype
, BOOL unicode
, BOOL ex
)
1932 WCHAR
*buf
, *opt
= NULL
, *iter
= NULL
;
1934 int bufSz
= 200; /* the size of the buffer */
1936 if (calinfoproc
== NULL
)
1938 SetLastError(ERROR_INVALID_PARAMETER
);
1942 buf
= HeapAlloc(GetProcessHeap(), 0, bufSz
);
1945 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1949 if (calendar
== ENUM_ALL_CALENDARS
)
1951 int optSz
= GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, NULL
, 0);
1954 opt
= HeapAlloc(GetProcessHeap(), 0, optSz
* sizeof(WCHAR
));
1957 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1960 if (GetLocaleInfoW(locale
, LOCALE_IOPTIONALCALENDAR
, opt
, optSz
))
1963 calendar
= NLS_GetLocaleNumber(locale
, LOCALE_ICALENDARTYPE
);
1966 while (TRUE
) /* loop through calendars */
1968 do /* loop until there's no error */
1971 ret
= GetCalendarInfoW(locale
, calendar
, caltype
, buf
, bufSz
/ sizeof(WCHAR
), NULL
);
1972 else ret
= GetCalendarInfoA(locale
, calendar
, caltype
, (CHAR
*)buf
, bufSz
/ sizeof(CHAR
), NULL
);
1976 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
1977 { /* so resize it */
1980 newSz
= GetCalendarInfoW(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(WCHAR
);
1981 else newSz
= GetCalendarInfoA(locale
, calendar
, caltype
, NULL
, 0, NULL
) * sizeof(CHAR
);
1984 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz
, newSz
);
1988 WARN("Buffer too small; resizing to %d bytes.\n", bufSz
);
1989 buf
= HeapReAlloc(GetProcessHeap(), 0, buf
, bufSz
);
1992 } else goto cleanup
;
1996 /* Here we are. We pass the buffer to the correct version of
1997 * the callback. Because it's not the same number of params,
1998 * we must check for Ex, but we don't care about Unicode
1999 * because the buffer is already in the correct format.
2002 ret
= ((CALINFO_ENUMPROCEXW
)calinfoproc
)(buf
, calendar
);
2004 ret
= ((CALINFO_ENUMPROCW
)calinfoproc
)(buf
);
2006 if (!ret
) { /* the callback told to stop */
2011 if ((iter
== NULL
) || (*iter
== 0)) /* no more calendars */
2015 while ((*iter
>= '0') && (*iter
<= '9'))
2016 calendar
= calendar
* 10 + *iter
++ - '0';
2020 SetLastError(ERROR_BADDB
);
2027 HeapFree(GetProcessHeap(), 0, opt
);
2028 HeapFree(GetProcessHeap(), 0, buf
);
2032 /******************************************************************************
2033 * EnumCalendarInfoA [KERNEL32.@]
2035 * See EnumCalendarInfoAW.
2037 BOOL WINAPI
EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc
,LCID locale
,
2038 CALID calendar
,CALTYPE caltype
)
2040 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2041 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, FALSE
);
2044 /******************************************************************************
2045 * EnumCalendarInfoW [KERNEL32.@]
2047 * See EnumCalendarInfoAW.
2049 BOOL WINAPI
EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc
,LCID locale
,
2050 CALID calendar
,CALTYPE caltype
)
2052 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2053 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, FALSE
);
2056 /******************************************************************************
2057 * EnumCalendarInfoExA [KERNEL32.@]
2059 * See EnumCalendarInfoAW.
2061 BOOL WINAPI
EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc
,LCID locale
,
2062 CALID calendar
,CALTYPE caltype
)
2064 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2065 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, FALSE
, TRUE
);
2068 /******************************************************************************
2069 * EnumCalendarInfoExW [KERNEL32.@]
2071 * See EnumCalendarInfoAW.
2073 BOOL WINAPI
EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc
,LCID locale
,
2074 CALID calendar
,CALTYPE caltype
)
2076 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc
, locale
, calendar
, caltype
);
2077 return NLS_EnumCalendarInfoAW(calinfoproc
, locale
, calendar
, caltype
, TRUE
, TRUE
);
2080 /*********************************************************************
2081 * GetCalendarInfoA (KERNEL32.@)
2084 int WINAPI
GetCalendarInfoA(LCID lcid
, CALID Calendar
, CALTYPE CalType
,
2085 LPSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2088 LPWSTR lpCalDataW
= NULL
;
2090 if (NLS_IsUnicodeOnlyLcid(lcid
))
2092 SetLastError(ERROR_INVALID_PARAMETER
);
2097 !(lpCalDataW
= HeapAlloc(GetProcessHeap(), 0, cchData
*sizeof(WCHAR
))))
2100 ret
= GetCalendarInfoW(lcid
, Calendar
, CalType
, lpCalDataW
, cchData
, lpValue
);
2101 if(ret
&& lpCalDataW
&& lpCalData
)
2102 WideCharToMultiByte(CP_ACP
, 0, lpCalDataW
, cchData
, lpCalData
, cchData
, NULL
, NULL
);
2103 else if (CalType
& CAL_RETURN_NUMBER
)
2104 ret
*= sizeof(WCHAR
);
2105 HeapFree(GetProcessHeap(), 0, lpCalDataW
);
2110 /*********************************************************************
2111 * GetCalendarInfoW (KERNEL32.@)
2114 int WINAPI
GetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
,
2115 LPWSTR lpCalData
, int cchData
, LPDWORD lpValue
)
2117 if (CalType
& CAL_NOUSEROVERRIDE
)
2118 FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n");
2119 if (CalType
& CAL_USE_CP_ACP
)
2120 FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n");
2122 if (CalType
& CAL_RETURN_NUMBER
) {
2125 SetLastError( ERROR_INVALID_PARAMETER
);
2128 if (lpCalData
!= NULL
)
2129 WARN("lpCalData not NULL (%p) when it should!\n", lpCalData
);
2131 WARN("cchData not 0 (%d) when it should!\n", cchData
);
2133 if (lpValue
!= NULL
)
2134 WARN("lpValue not NULL (%p) when it should!\n", lpValue
);
2137 /* FIXME: No verification is made yet wrt Locale
2138 * for the CALTYPES not requiring GetLocaleInfoA */
2139 switch (CalType
& ~(CAL_NOUSEROVERRIDE
|CAL_RETURN_NUMBER
|CAL_USE_CP_ACP
)) {
2140 case CAL_ICALINTVALUE
:
2141 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2144 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2146 case CAL_IYEAROFFSETRANGE
:
2147 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2149 case CAL_SERASTRING
:
2150 FIXME("Unimplemented caltype %d\n", CalType
& 0xffff);
2152 case CAL_SSHORTDATE
:
2153 return GetLocaleInfoW(Locale
, LOCALE_SSHORTDATE
, lpCalData
, cchData
);
2155 return GetLocaleInfoW(Locale
, LOCALE_SLONGDATE
, lpCalData
, cchData
);
2157 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME1
, lpCalData
, cchData
);
2159 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME2
, lpCalData
, cchData
);
2161 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME3
, lpCalData
, cchData
);
2163 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME4
, lpCalData
, cchData
);
2165 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME5
, lpCalData
, cchData
);
2167 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME6
, lpCalData
, cchData
);
2169 return GetLocaleInfoW(Locale
, LOCALE_SDAYNAME7
, lpCalData
, cchData
);
2170 case CAL_SABBREVDAYNAME1
:
2171 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME1
, lpCalData
, cchData
);
2172 case CAL_SABBREVDAYNAME2
:
2173 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME2
, lpCalData
, cchData
);
2174 case CAL_SABBREVDAYNAME3
:
2175 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME3
, lpCalData
, cchData
);
2176 case CAL_SABBREVDAYNAME4
:
2177 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME4
, lpCalData
, cchData
);
2178 case CAL_SABBREVDAYNAME5
:
2179 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME5
, lpCalData
, cchData
);
2180 case CAL_SABBREVDAYNAME6
:
2181 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME6
, lpCalData
, cchData
);
2182 case CAL_SABBREVDAYNAME7
:
2183 return GetLocaleInfoW(Locale
, LOCALE_SABBREVDAYNAME7
, lpCalData
, cchData
);
2184 case CAL_SMONTHNAME1
:
2185 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME1
, lpCalData
, cchData
);
2186 case CAL_SMONTHNAME2
:
2187 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME2
, lpCalData
, cchData
);
2188 case CAL_SMONTHNAME3
:
2189 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME3
, lpCalData
, cchData
);
2190 case CAL_SMONTHNAME4
:
2191 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME4
, lpCalData
, cchData
);
2192 case CAL_SMONTHNAME5
:
2193 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME5
, lpCalData
, cchData
);
2194 case CAL_SMONTHNAME6
:
2195 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME6
, lpCalData
, cchData
);
2196 case CAL_SMONTHNAME7
:
2197 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME7
, lpCalData
, cchData
);
2198 case CAL_SMONTHNAME8
:
2199 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME8
, lpCalData
, cchData
);
2200 case CAL_SMONTHNAME9
:
2201 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME9
, lpCalData
, cchData
);
2202 case CAL_SMONTHNAME10
:
2203 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME10
, lpCalData
, cchData
);
2204 case CAL_SMONTHNAME11
:
2205 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME11
, lpCalData
, cchData
);
2206 case CAL_SMONTHNAME12
:
2207 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME12
, lpCalData
, cchData
);
2208 case CAL_SMONTHNAME13
:
2209 return GetLocaleInfoW(Locale
, LOCALE_SMONTHNAME13
, lpCalData
, cchData
);
2210 case CAL_SABBREVMONTHNAME1
:
2211 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME1
, lpCalData
, cchData
);
2212 case CAL_SABBREVMONTHNAME2
:
2213 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME2
, lpCalData
, cchData
);
2214 case CAL_SABBREVMONTHNAME3
:
2215 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME3
, lpCalData
, cchData
);
2216 case CAL_SABBREVMONTHNAME4
:
2217 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME4
, lpCalData
, cchData
);
2218 case CAL_SABBREVMONTHNAME5
:
2219 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME5
, lpCalData
, cchData
);
2220 case CAL_SABBREVMONTHNAME6
:
2221 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME6
, lpCalData
, cchData
);
2222 case CAL_SABBREVMONTHNAME7
:
2223 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME7
, lpCalData
, cchData
);
2224 case CAL_SABBREVMONTHNAME8
:
2225 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME8
, lpCalData
, cchData
);
2226 case CAL_SABBREVMONTHNAME9
:
2227 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME9
, lpCalData
, cchData
);
2228 case CAL_SABBREVMONTHNAME10
:
2229 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME10
, lpCalData
, cchData
);
2230 case CAL_SABBREVMONTHNAME11
:
2231 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME11
, lpCalData
, cchData
);
2232 case CAL_SABBREVMONTHNAME12
:
2233 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME12
, lpCalData
, cchData
);
2234 case CAL_SABBREVMONTHNAME13
:
2235 return GetLocaleInfoW(Locale
, LOCALE_SABBREVMONTHNAME13
, lpCalData
, cchData
);
2236 case CAL_SYEARMONTH
:
2237 return GetLocaleInfoW(Locale
, LOCALE_SYEARMONTH
, lpCalData
, cchData
);
2238 case CAL_ITWODIGITYEARMAX
:
2239 if (CalType
& CAL_RETURN_NUMBER
)
2241 *lpValue
= CALINFO_MAX_YEAR
;
2242 return sizeof(DWORD
) / sizeof(WCHAR
);
2246 static const WCHAR fmtW
[] = {'%','u',0};
2248 int ret
= snprintfW( buffer
, 10, fmtW
, CALINFO_MAX_YEAR
) + 1;
2249 if (!lpCalData
) return ret
;
2252 strcpyW( lpCalData
, buffer
);
2255 SetLastError( ERROR_INSUFFICIENT_BUFFER
);
2260 FIXME("Unknown caltype %d\n",CalType
& 0xffff);
2261 SetLastError(ERROR_INVALID_FLAGS
);
2267 /*********************************************************************
2268 * SetCalendarInfoA (KERNEL32.@)
2271 int WINAPI
SetCalendarInfoA(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCSTR lpCalData
)
2273 FIXME("(%08x,%08x,%08x,%s): stub\n",
2274 Locale
, Calendar
, CalType
, debugstr_a(lpCalData
));
2278 /*********************************************************************
2279 * SetCalendarInfoW (KERNEL32.@)
2283 int WINAPI
SetCalendarInfoW(LCID Locale
, CALID Calendar
, CALTYPE CalType
, LPCWSTR lpCalData
)
2285 FIXME("(%08x,%08x,%08x,%s): stub\n",
2286 Locale
, Calendar
, CalType
, debugstr_w(lpCalData
));