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