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