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