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 == 32 ? 3 : 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 if (lpFormat->Grouping == 32) 1795 dwGroupCount = 2; /* Indic grouping: 3 then 2 */ 1796 } 1797 } 1798 if (dwState & NF_ROUND) 1799 *szOut-- = '1'; /* e.g. .6 > 1.0 */ 1800 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero) 1801 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */ 1802 1803 /* Add any leading negative or currency sign */ 1804 while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT)) 1805 { 1806 switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT)) 1807 { 1808 case CF_MINUS_LEFT: 1809 case CF_MINUS_LEFT|CF_CY_LEFT: 1810 while (lpszNeg >= lpszNegStart) 1811 *szOut-- = *lpszNeg--; 1812 dwFmt &= ~CF_MINUS_LEFT; 1813 break; 1814 1815 case CF_CY_LEFT: 1816 case CF_CY_LEFT|CF_MINUS_BEFORE: 1817 case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT: 1818 if (dwFmt & CF_CY_SPACE) 1819 *szOut-- = ' '; 1820 while (lpszCy >= lpszCyStart) 1821 *szOut-- = *lpszCy--; 1822 dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE); 1823 break; 1824 } 1825 } 1826 if (dwFmt & CF_PARENS) 1827 *szOut-- = '('; 1828 szOut++; 1829 1830 iRet = strlenW(szOut) + 1; 1831 if (cchOut) 1832 { 1833 if (iRet <= cchOut) 1834 memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR)); 1835 else 1836 { 1837 memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR)); 1838 lpCurrencyStr[cchOut - 1] = '\0'; 1839 SetLastError(ERROR_INSUFFICIENT_BUFFER); 1840 iRet = 0; 1841 } 1842 } 1843 return iRet; 1844 1845 error: 1846 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER); 1847 return 0; 1848 } 1849 1850 #if _WIN32_WINNT >= 0x600 1851 /*********************************************************************** 1852 * GetCurrencyFormatEx (KERNEL32.@) 1853 */ 1854 int WINAPI GetCurrencyFormatEx(LPCWSTR localename, DWORD flags, LPCWSTR value, 1855 const CURRENCYFMTW *format, LPWSTR str, int len) 1856 { 1857 TRACE("(%s,0x%08x,%s,%p,%p,%d)\n", debugstr_w(localename), flags, 1858 debugstr_w(value), format, str, len); 1859 1860 return GetCurrencyFormatW( LocaleNameToLCID(localename, 0), flags, value, format, str, len); 1861 } 1862 #endif 1863 1864 1865 /* FIXME: Everything below here needs to move somewhere else along with the 1866 * other EnumXXX functions, when a method for storing resources for 1867 * alternate calendars is determined. 1868 */ 1869 1870 enum enum_callback_type { 1871 CALLBACK_ENUMPROC, 1872 CALLBACK_ENUMPROCEX, 1873 CALLBACK_ENUMPROCEXEX 1874 }; 1875 1876 struct enumdateformats_context { 1877 enum enum_callback_type type; /* callback kind */ 1878 union { 1879 DATEFMT_ENUMPROCW callback; /* user callback pointer */ 1880 DATEFMT_ENUMPROCEXW callbackex; 1881 DATEFMT_ENUMPROCEXEX callbackexex; 1882 } u; 1883 LCID lcid; /* locale of interest */ 1884 DWORD flags; 1885 LPARAM lParam; 1886 BOOL unicode; /* A vs W callback type, only for regular and Ex callbacks */ 1887 }; 1888 1889 /****************************************************************************** 1890 * NLS_EnumDateFormats <internal> 1891 * Enumerates date formats for a specified locale. 1892 * 1893 * PARAMS 1894 * ctxt [I] enumeration context, see 'struct enumdateformats_context' 1895 * 1896 * RETURNS 1897 * Success: TRUE. 1898 * Failure: FALSE. Use GetLastError() to determine the cause. 1899 */ 1900 static BOOL NLS_EnumDateFormats(const struct enumdateformats_context *ctxt) 1901 { 1902 WCHAR bufW[256]; 1903 char bufA[256]; 1904 LCTYPE lctype; 1905 CALID cal_id; 1906 INT ret; 1907 1908 if (!ctxt->u.callback) 1909 { 1910 SetLastError(ERROR_INVALID_PARAMETER); 1911 return FALSE; 1912 } 1913 1914 if (!GetLocaleInfoW(ctxt->lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR))) 1915 return FALSE; 1916 1917 switch (ctxt->flags & ~LOCALE_USE_CP_ACP) 1918 { 1919 case 0: 1920 case DATE_SHORTDATE: 1921 lctype = LOCALE_SSHORTDATE; 1922 break; 1923 case DATE_LONGDATE: 1924 lctype = LOCALE_SLONGDATE; 1925 break; 1926 case DATE_YEARMONTH: 1927 lctype = LOCALE_SYEARMONTH; 1928 break; 1929 default: 1930 FIXME("Unknown date format (0x%08x)\n", ctxt->flags); 1931 SetLastError(ERROR_INVALID_PARAMETER); 1932 return FALSE; 1933 } 1934 1935 lctype |= ctxt->flags & LOCALE_USE_CP_ACP; 1936 if (ctxt->unicode) 1937 ret = GetLocaleInfoW(ctxt->lcid, lctype, bufW, ARRAY_SIZE(bufW)); 1938 else 1939 ret = GetLocaleInfoA(ctxt->lcid, lctype, bufA, ARRAY_SIZE(bufA)); 1940 1941 if (ret) 1942 { 1943 switch (ctxt->type) 1944 { 1945 case CALLBACK_ENUMPROC: 1946 ctxt->u.callback(ctxt->unicode ? bufW : (WCHAR*)bufA); 1947 break; 1948 case CALLBACK_ENUMPROCEX: 1949 ctxt->u.callbackex(ctxt->unicode ? bufW : (WCHAR*)bufA, cal_id); 1950 break; 1951 case CALLBACK_ENUMPROCEXEX: 1952 ctxt->u.callbackexex(bufW, cal_id, ctxt->lParam); 1953 break; 1954 default: 1955 ; 1956 } 1957 } 1958 1959 return TRUE; 1960 } 1961 1962 /************************************************************************** 1963 * EnumDateFormatsExA (KERNEL32.@) 1964 * 1965 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 1966 * LOCALE_NOUSEROVERRIDE here as well? 1967 */ 1968 BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags) 1969 { 1970 struct enumdateformats_context ctxt; 1971 1972 ctxt.type = CALLBACK_ENUMPROCEX; 1973 ctxt.u.callbackex = (DATEFMT_ENUMPROCEXW)proc; 1974 ctxt.lcid = lcid; 1975 ctxt.flags = flags; 1976 ctxt.unicode = FALSE; 1977 1978 return NLS_EnumDateFormats(&ctxt); 1979 } 1980 1981 /************************************************************************** 1982 * EnumDateFormatsExW (KERNEL32.@) 1983 */ 1984 BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags) 1985 { 1986 struct enumdateformats_context ctxt; 1987 1988 ctxt.type = CALLBACK_ENUMPROCEX; 1989 ctxt.u.callbackex = proc; 1990 ctxt.lcid = lcid; 1991 ctxt.flags = flags; 1992 ctxt.unicode = TRUE; 1993 1994 return NLS_EnumDateFormats(&ctxt); 1995 } 1996 1997 /************************************************************************** 1998 * EnumDateFormatsA (KERNEL32.@) 1999 * 2000 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 2001 * LOCALE_NOUSEROVERRIDE here as well? 2002 */ 2003 BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags) 2004 { 2005 struct enumdateformats_context ctxt; 2006 2007 ctxt.type = CALLBACK_ENUMPROC; 2008 ctxt.u.callback = (DATEFMT_ENUMPROCW)proc; 2009 ctxt.lcid = lcid; 2010 ctxt.flags = flags; 2011 ctxt.unicode = FALSE; 2012 2013 return NLS_EnumDateFormats(&ctxt); 2014 } 2015 2016 /************************************************************************** 2017 * EnumDateFormatsW (KERNEL32.@) 2018 */ 2019 BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags) 2020 { 2021 struct enumdateformats_context ctxt; 2022 2023 ctxt.type = CALLBACK_ENUMPROC; 2024 ctxt.u.callback = proc; 2025 ctxt.lcid = lcid; 2026 ctxt.flags = flags; 2027 ctxt.unicode = TRUE; 2028 2029 return NLS_EnumDateFormats(&ctxt); 2030 } 2031 2032 #if _WIN32_WINNT >= 0x600 2033 /************************************************************************** 2034 * EnumDateFormatsExEx (KERNEL32.@) 2035 */ 2036 BOOL WINAPI EnumDateFormatsExEx(DATEFMT_ENUMPROCEXEX proc, const WCHAR *locale, DWORD flags, LPARAM lParam) 2037 { 2038 struct enumdateformats_context ctxt; 2039 2040 ctxt.type = CALLBACK_ENUMPROCEXEX; 2041 ctxt.u.callbackexex = proc; 2042 ctxt.lcid = LocaleNameToLCID(locale, 0); 2043 ctxt.flags = flags; 2044 ctxt.lParam = lParam; 2045 ctxt.unicode = TRUE; 2046 2047 return NLS_EnumDateFormats(&ctxt); 2048 } 2049 #endif /* _WIN32_WINNT >= 0x600 */ 2050 2051 struct enumtimeformats_context { 2052 enum enum_callback_type type; /* callback kind */ 2053 union { 2054 TIMEFMT_ENUMPROCW callback; /* user callback pointer */ 2055 TIMEFMT_ENUMPROCEX callbackex; 2056 } u; 2057 LCID lcid; /* locale of interest */ 2058 DWORD flags; 2059 LPARAM lParam; 2060 BOOL unicode; /* A vs W callback type, only for regular and Ex callbacks */ 2061 }; 2062 2063 static BOOL NLS_EnumTimeFormats(struct enumtimeformats_context *ctxt) 2064 { 2065 WCHAR bufW[256]; 2066 char bufA[256]; 2067 LCTYPE lctype; 2068 INT ret; 2069 2070 if (!ctxt->u.callback) 2071 { 2072 SetLastError(ERROR_INVALID_PARAMETER); 2073 return FALSE; 2074 } 2075 2076 switch (ctxt->flags & ~LOCALE_USE_CP_ACP) 2077 { 2078 case 0: 2079 lctype = LOCALE_STIMEFORMAT; 2080 break; 2081 case TIME_NOSECONDS: 2082 lctype = LOCALE_SSHORTTIME; 2083 break; 2084 default: 2085 FIXME("Unknown time format (%d)\n", ctxt->flags); 2086 SetLastError(ERROR_INVALID_PARAMETER); 2087 return FALSE; 2088 } 2089 2090 lctype |= ctxt->flags & LOCALE_USE_CP_ACP; 2091 if (ctxt->unicode) 2092 ret = GetLocaleInfoW(ctxt->lcid, lctype, bufW, ARRAY_SIZE(bufW)); 2093 else 2094 ret = GetLocaleInfoA(ctxt->lcid, lctype, bufA, ARRAY_SIZE(bufA)); 2095 2096 if (ret) 2097 { 2098 switch (ctxt->type) 2099 { 2100 case CALLBACK_ENUMPROC: 2101 ctxt->u.callback(ctxt->unicode ? bufW : (WCHAR*)bufA); 2102 break; 2103 case CALLBACK_ENUMPROCEX: 2104 ctxt->u.callbackex(bufW, ctxt->lParam); 2105 break; 2106 default: 2107 ; 2108 } 2109 } 2110 2111 return TRUE; 2112 } 2113 2114 /************************************************************************** 2115 * EnumTimeFormatsA (KERNEL32.@) 2116 * 2117 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 2118 * LOCALE_NOUSEROVERRIDE here as well? 2119 */ 2120 BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags) 2121 { 2122 struct enumtimeformats_context ctxt; 2123 2124 /* EnumTimeFormatsA doesn't support flags, EnumTimeFormatsW does. */ 2125 if (flags & ~LOCALE_USE_CP_ACP) 2126 { 2127 SetLastError(ERROR_INVALID_FLAGS); 2128 return FALSE; 2129 } 2130 2131 ctxt.type = CALLBACK_ENUMPROC; 2132 ctxt.u.callback = (TIMEFMT_ENUMPROCW)proc; 2133 ctxt.lcid = lcid; 2134 ctxt.flags = flags; 2135 ctxt.unicode = FALSE; 2136 2137 return NLS_EnumTimeFormats(&ctxt); 2138 } 2139 2140 /************************************************************************** 2141 * EnumTimeFormatsW (KERNEL32.@) 2142 */ 2143 BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags) 2144 { 2145 struct enumtimeformats_context ctxt; 2146 2147 ctxt.type = CALLBACK_ENUMPROC; 2148 ctxt.u.callback = proc; 2149 ctxt.lcid = lcid; 2150 ctxt.flags = flags; 2151 ctxt.unicode = TRUE; 2152 2153 return NLS_EnumTimeFormats(&ctxt); 2154 } 2155 2156 #if _WIN32_WINNT >= 0x600 2157 /************************************************************************** 2158 * EnumTimeFormatsEx (KERNEL32.@) 2159 */ 2160 BOOL WINAPI EnumTimeFormatsEx(TIMEFMT_ENUMPROCEX proc, const WCHAR *locale, DWORD flags, LPARAM lParam) 2161 { 2162 struct enumtimeformats_context ctxt; 2163 2164 ctxt.type = CALLBACK_ENUMPROCEX; 2165 ctxt.u.callbackex = proc; 2166 ctxt.lcid = LocaleNameToLCID(locale, 0); 2167 ctxt.flags = flags; 2168 ctxt.lParam = lParam; 2169 ctxt.unicode = TRUE; 2170 2171 return NLS_EnumTimeFormats(&ctxt); 2172 } 2173 #endif /* _WIN32_WINNT >= 0x600 */ 2174 2175 struct enumcalendar_context { 2176 enum enum_callback_type type; /* callback kind */ 2177 union { 2178 CALINFO_ENUMPROCW callback; /* user callback pointer */ 2179 CALINFO_ENUMPROCEXW callbackex; 2180 CALINFO_ENUMPROCEXEX callbackexex; 2181 } u; 2182 LCID lcid; /* locale of interest */ 2183 CALID calendar; /* specific calendar or ENUM_ALL_CALENDARS */ 2184 CALTYPE caltype; /* calendar information type */ 2185 LPARAM lParam; /* user input parameter passed to callback, for ExEx case only */ 2186 BOOL unicode; /* A vs W callback type, only for regular and Ex callbacks */ 2187 }; 2188 2189 /****************************************************************************** 2190 * NLS_EnumCalendarInfo <internal> 2191 * Enumerates calendar information for a specified locale. 2192 * 2193 * PARAMS 2194 * ctxt [I] enumeration context, see 'struct enumcalendar_context' 2195 * 2196 * RETURNS 2197 * Success: TRUE. 2198 * Failure: FALSE. Use GetLastError() to determine the cause. 2199 * 2200 * NOTES 2201 * When the ANSI version of this function is used with a Unicode-only LCID, 2202 * the call can succeed because the system uses the system code page. 2203 * However, characters that are undefined in the system code page appear 2204 * in the string as a question mark (?). 2205 * 2206 * TODO 2207 * The above note should be respected by GetCalendarInfoA. 2208 */ 2209 static BOOL NLS_EnumCalendarInfo(const struct enumcalendar_context *ctxt) 2210 { 2211 WCHAR *buf, *opt = NULL, *iter = NULL; 2212 CALID calendar = ctxt->calendar; 2213 BOOL ret = FALSE; 2214 int bufSz = 200; /* the size of the buffer */ 2215 2216 if (ctxt->u.callback == NULL) 2217 { 2218 SetLastError(ERROR_INVALID_PARAMETER); 2219 return FALSE; 2220 } 2221 2222 buf = HeapAlloc(GetProcessHeap(), 0, bufSz); 2223 if (buf == NULL) 2224 { 2225 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2226 return FALSE; 2227 } 2228 2229 if (calendar == ENUM_ALL_CALENDARS) 2230 { 2231 int optSz = GetLocaleInfoW(ctxt->lcid, LOCALE_IOPTIONALCALENDAR, NULL, 0); 2232 if (optSz > 1) 2233 { 2234 opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR)); 2235 if (opt == NULL) 2236 { 2237 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2238 goto cleanup; 2239 } 2240 if (GetLocaleInfoW(ctxt->lcid, LOCALE_IOPTIONALCALENDAR, opt, optSz)) 2241 iter = opt; 2242 } 2243 calendar = NLS_GetLocaleNumber(ctxt->lcid, LOCALE_ICALENDARTYPE); 2244 } 2245 2246 while (TRUE) /* loop through calendars */ 2247 { 2248 do /* loop until there's no error */ 2249 { 2250 if (ctxt->caltype & CAL_RETURN_NUMBER) 2251 ret = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, NULL, bufSz / sizeof(WCHAR), (LPDWORD)buf); 2252 else if (ctxt->unicode) 2253 ret = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, buf, bufSz / sizeof(WCHAR), NULL); 2254 else ret = GetCalendarInfoA(ctxt->lcid, calendar, ctxt->caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL); 2255 2256 if (!ret) 2257 { 2258 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 2259 { /* so resize it */ 2260 int newSz; 2261 if (ctxt->unicode) 2262 newSz = GetCalendarInfoW(ctxt->lcid, calendar, ctxt->caltype, NULL, 0, NULL) * sizeof(WCHAR); 2263 else newSz = GetCalendarInfoA(ctxt->lcid, calendar, ctxt->caltype, NULL, 0, NULL) * sizeof(CHAR); 2264 if (bufSz >= newSz) 2265 { 2266 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz); 2267 goto cleanup; 2268 } 2269 bufSz = newSz; 2270 WARN("Buffer too small; resizing to %d bytes.\n", bufSz); 2271 buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz); 2272 if (buf == NULL) 2273 goto cleanup; 2274 } else goto cleanup; 2275 } 2276 } while (!ret); 2277 2278 /* Here we are. We pass the buffer to the correct version of 2279 * the callback. Because it's not the same number of params, 2280 * we must check for Ex, but we don't care about Unicode 2281 * because the buffer is already in the correct format. 2282 */ 2283 switch (ctxt->type) 2284 { 2285 case CALLBACK_ENUMPROC: 2286 ret = ctxt->u.callback(buf); 2287 break; 2288 case CALLBACK_ENUMPROCEX: 2289 ret = ctxt->u.callbackex(buf, calendar); 2290 break; 2291 case CALLBACK_ENUMPROCEXEX: 2292 ret = ctxt->u.callbackexex(buf, calendar, NULL, ctxt->lParam); 2293 break; 2294 default: 2295 ; 2296 } 2297 2298 if (!ret) { /* the callback told to stop */ 2299 ret = TRUE; 2300 break; 2301 } 2302 2303 if ((iter == NULL) || (*iter == 0)) /* no more calendars */ 2304 break; 2305 2306 calendar = 0; 2307 while ((*iter >= '0') && (*iter <= '9')) 2308 calendar = calendar * 10 + *iter++ - '0'; 2309 2310 if (*iter++ != 0) 2311 { 2312 SetLastError(ERROR_BADDB); 2313 ret = FALSE; 2314 break; 2315 } 2316 } 2317 2318 cleanup: 2319 HeapFree(GetProcessHeap(), 0, opt); 2320 HeapFree(GetProcessHeap(), 0, buf); 2321 return ret; 2322 } 2323 2324 /****************************************************************************** 2325 * EnumCalendarInfoA [KERNEL32.@] 2326 */ 2327 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale, 2328 CALID calendar,CALTYPE caltype ) 2329 { 2330 struct enumcalendar_context ctxt; 2331 2332 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 2333 2334 ctxt.type = CALLBACK_ENUMPROC; 2335 ctxt.u.callback = (CALINFO_ENUMPROCW)calinfoproc; 2336 ctxt.lcid = locale; 2337 ctxt.calendar = calendar; 2338 ctxt.caltype = caltype; 2339 ctxt.lParam = 0; 2340 ctxt.unicode = FALSE; 2341 return NLS_EnumCalendarInfo(&ctxt); 2342 } 2343 2344 /****************************************************************************** 2345 * EnumCalendarInfoW [KERNEL32.@] 2346 */ 2347 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale, 2348 CALID calendar,CALTYPE caltype ) 2349 { 2350 struct enumcalendar_context ctxt; 2351 2352 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 2353 2354 ctxt.type = CALLBACK_ENUMPROC; 2355 ctxt.u.callback = calinfoproc; 2356 ctxt.lcid = locale; 2357 ctxt.calendar = calendar; 2358 ctxt.caltype = caltype; 2359 ctxt.lParam = 0; 2360 ctxt.unicode = TRUE; 2361 return NLS_EnumCalendarInfo(&ctxt); 2362 } 2363 2364 /****************************************************************************** 2365 * EnumCalendarInfoExA [KERNEL32.@] 2366 */ 2367 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale, 2368 CALID calendar,CALTYPE caltype ) 2369 { 2370 struct enumcalendar_context ctxt; 2371 2372 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 2373 2374 ctxt.type = CALLBACK_ENUMPROCEX; 2375 ctxt.u.callbackex = (CALINFO_ENUMPROCEXW)calinfoproc; 2376 ctxt.lcid = locale; 2377 ctxt.calendar = calendar; 2378 ctxt.caltype = caltype; 2379 ctxt.lParam = 0; 2380 ctxt.unicode = FALSE; 2381 return NLS_EnumCalendarInfo(&ctxt); 2382 } 2383 2384 /****************************************************************************** 2385 * EnumCalendarInfoExW [KERNEL32.@] 2386 */ 2387 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale, 2388 CALID calendar,CALTYPE caltype ) 2389 { 2390 struct enumcalendar_context ctxt; 2391 2392 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 2393 2394 ctxt.type = CALLBACK_ENUMPROCEX; 2395 ctxt.u.callbackex = calinfoproc; 2396 ctxt.lcid = locale; 2397 ctxt.calendar = calendar; 2398 ctxt.caltype = caltype; 2399 ctxt.lParam = 0; 2400 ctxt.unicode = TRUE; 2401 return NLS_EnumCalendarInfo(&ctxt); 2402 } 2403 2404 #if _WIN32_WINNT >= 0x600 2405 /****************************************************************************** 2406 * EnumCalendarInfoExEx [KERNEL32.@] 2407 */ 2408 BOOL WINAPI EnumCalendarInfoExEx( CALINFO_ENUMPROCEXEX calinfoproc, LPCWSTR locale, CALID calendar, 2409 LPCWSTR reserved, CALTYPE caltype, LPARAM lParam) 2410 { 2411 struct enumcalendar_context ctxt; 2412 2413 TRACE("(%p,%s,0x%08x,%p,0x%08x,0x%ld)\n", calinfoproc, debugstr_w(locale), calendar, reserved, caltype, lParam); 2414 2415 ctxt.type = CALLBACK_ENUMPROCEXEX; 2416 ctxt.u.callbackexex = calinfoproc; 2417 ctxt.lcid = LocaleNameToLCID(locale, 0); 2418 ctxt.calendar = calendar; 2419 ctxt.caltype = caltype; 2420 ctxt.lParam = lParam; 2421 ctxt.unicode = TRUE; 2422 return NLS_EnumCalendarInfo(&ctxt); 2423 } 2424 #endif /* _WIN32_WINNT >= 0x600 */ 2425 2426 /********************************************************************* 2427 * GetCalendarInfoA (KERNEL32.@) 2428 * 2429 */ 2430 int WINAPI GetCalendarInfoA(LCID lcid, CALID Calendar, CALTYPE CalType, 2431 LPSTR lpCalData, int cchData, LPDWORD lpValue) 2432 { 2433 int ret, cchDataW = cchData; 2434 LPWSTR lpCalDataW = NULL; 2435 #ifdef __REACTOS__ 2436 DWORD cp = CP_ACP; 2437 if (!(CalType & CAL_USE_CP_ACP)) 2438 { 2439 DWORD dwFlags = ((CalType & CAL_NOUSEROVERRIDE) ? LOCALE_NOUSEROVERRIDE : 0); 2440 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 2441 if (!node) 2442 { 2443 SetLastError(ERROR_INVALID_PARAMETER); 2444 return 0; 2445 } 2446 cp = node->dwCodePage; 2447 } 2448 if ((CalType & 0xFFFF) == CAL_SABBREVERASTRING) 2449 { 2450 /* NOTE: CAL_SABBREVERASTRING is not supported in GetCalendarInfoA */ 2451 SetLastError(ERROR_INVALID_PARAMETER); 2452 return 0; 2453 } 2454 #endif 2455 2456 if (NLS_IsUnicodeOnlyLcid(lcid)) 2457 { 2458 SetLastError(ERROR_INVALID_PARAMETER); 2459 return 0; 2460 } 2461 2462 if (!cchData && !(CalType & CAL_RETURN_NUMBER)) 2463 cchDataW = GetCalendarInfoW(lcid, Calendar, CalType, NULL, 0, NULL); 2464 if (!(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchDataW*sizeof(WCHAR)))) 2465 return 0; 2466 2467 ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchDataW, lpValue); 2468 if(ret && lpCalDataW && lpCalData) 2469 #ifdef __REACTOS__ 2470 ret = WideCharToMultiByte(cp, 0, lpCalDataW, -1, lpCalData, cchData, NULL, NULL); 2471 #else 2472 ret = WideCharToMultiByte(CP_ACP, 0, lpCalDataW, -1, lpCalData, cchData, NULL, NULL); 2473 #endif 2474 else if (CalType & CAL_RETURN_NUMBER) 2475 ret *= sizeof(WCHAR); 2476 HeapFree(GetProcessHeap(), 0, lpCalDataW); 2477 2478 return ret; 2479 } 2480 2481 /********************************************************************* 2482 * GetCalendarInfoW (KERNEL32.@) 2483 * 2484 */ 2485 int WINAPI GetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, 2486 LPWSTR lpCalData, int cchData, LPDWORD lpValue) 2487 { 2488 static const LCTYPE caltype_lctype_map[] = { 2489 0, /* not used */ 2490 0, /* CAL_ICALINTVALUE */ 2491 0, /* CAL_SCALNAME */ 2492 0, /* CAL_IYEAROFFSETRANGE */ 2493 0, /* CAL_SERASTRING */ 2494 LOCALE_SSHORTDATE, 2495 LOCALE_SLONGDATE, 2496 LOCALE_SDAYNAME1, 2497 LOCALE_SDAYNAME2, 2498 LOCALE_SDAYNAME3, 2499 LOCALE_SDAYNAME4, 2500 LOCALE_SDAYNAME5, 2501 LOCALE_SDAYNAME6, 2502 LOCALE_SDAYNAME7, 2503 LOCALE_SABBREVDAYNAME1, 2504 LOCALE_SABBREVDAYNAME2, 2505 LOCALE_SABBREVDAYNAME3, 2506 LOCALE_SABBREVDAYNAME4, 2507 LOCALE_SABBREVDAYNAME5, 2508 LOCALE_SABBREVDAYNAME6, 2509 LOCALE_SABBREVDAYNAME7, 2510 LOCALE_SMONTHNAME1, 2511 LOCALE_SMONTHNAME2, 2512 LOCALE_SMONTHNAME3, 2513 LOCALE_SMONTHNAME4, 2514 LOCALE_SMONTHNAME5, 2515 LOCALE_SMONTHNAME6, 2516 LOCALE_SMONTHNAME7, 2517 LOCALE_SMONTHNAME8, 2518 LOCALE_SMONTHNAME9, 2519 LOCALE_SMONTHNAME10, 2520 LOCALE_SMONTHNAME11, 2521 LOCALE_SMONTHNAME12, 2522 LOCALE_SMONTHNAME13, 2523 LOCALE_SABBREVMONTHNAME1, 2524 LOCALE_SABBREVMONTHNAME2, 2525 LOCALE_SABBREVMONTHNAME3, 2526 LOCALE_SABBREVMONTHNAME4, 2527 LOCALE_SABBREVMONTHNAME5, 2528 LOCALE_SABBREVMONTHNAME6, 2529 LOCALE_SABBREVMONTHNAME7, 2530 LOCALE_SABBREVMONTHNAME8, 2531 LOCALE_SABBREVMONTHNAME9, 2532 LOCALE_SABBREVMONTHNAME10, 2533 LOCALE_SABBREVMONTHNAME11, 2534 LOCALE_SABBREVMONTHNAME12, 2535 LOCALE_SABBREVMONTHNAME13, 2536 LOCALE_SYEARMONTH, 2537 0, /* CAL_ITWODIGITYEARMAX */ 2538 #if (WINVER >= 0x0600) /* ReactOS */ 2539 LOCALE_SSHORTESTDAYNAME1, 2540 LOCALE_SSHORTESTDAYNAME2, 2541 LOCALE_SSHORTESTDAYNAME3, 2542 LOCALE_SSHORTESTDAYNAME4, 2543 LOCALE_SSHORTESTDAYNAME5, 2544 LOCALE_SSHORTESTDAYNAME6, 2545 LOCALE_SSHORTESTDAYNAME7, 2546 #endif 2547 LOCALE_SMONTHDAY, 2548 0, /* CAL_SABBREVERASTRING */ 2549 }; 2550 DWORD localeflags = 0; 2551 CALTYPE calinfo; 2552 2553 if (CalType & CAL_NOUSEROVERRIDE) 2554 FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n"); 2555 if (CalType & CAL_USE_CP_ACP) 2556 FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n"); 2557 2558 if (CalType & CAL_RETURN_NUMBER) { 2559 if (!lpValue) 2560 { 2561 SetLastError( ERROR_INVALID_PARAMETER ); 2562 return 0; 2563 } 2564 if (lpCalData != NULL) 2565 WARN("lpCalData not NULL (%p) when it should!\n", lpCalData); 2566 if (cchData != 0) 2567 WARN("cchData not 0 (%d) when it should!\n", cchData); 2568 } else { 2569 if (lpValue != NULL) 2570 WARN("lpValue not NULL (%p) when it should!\n", lpValue); 2571 } 2572 2573 /* FIXME: No verification is made yet wrt Locale 2574 * for the CALTYPES not requiring GetLocaleInfoA */ 2575 2576 calinfo = CalType & 0xffff; 2577 2578 #ifdef __REACTOS__ 2579 if (CalType & LOCALE_RETURN_GENITIVE_NAMES) 2580 #else 2581 if (CalType & CAL_RETURN_GENITIVE_NAMES) 2582 #endif 2583 localeflags |= LOCALE_RETURN_GENITIVE_NAMES; 2584 2585 switch (calinfo) { 2586 case CAL_ICALINTVALUE: 2587 #ifdef __REACTOS__ 2588 if (IS_LCID_JAPANESE(Locale)) 2589 { 2590 if (CalType & CAL_RETURN_NUMBER) 2591 { 2592 *lpValue = CAL_JAPAN; 2593 return sizeof(DWORD) / sizeof(WCHAR); 2594 } 2595 else 2596 { 2597 static const WCHAR fmtW[] = {'%','u',0}; 2598 WCHAR buffer[10]; 2599 int ret = snprintfW( buffer, 10, fmtW, CAL_JAPAN ) + 1; 2600 if (!lpCalData) return ret; 2601 if (ret <= cchData) 2602 { 2603 strcpyW( lpCalData, buffer ); 2604 return ret; 2605 } 2606 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 2607 return 0; 2608 } 2609 } 2610 #endif 2611 if (CalType & CAL_RETURN_NUMBER) 2612 return GetLocaleInfoW(Locale, LOCALE_RETURN_NUMBER | LOCALE_ICALENDARTYPE, 2613 (LPWSTR)lpValue, 2); 2614 return GetLocaleInfoW(Locale, LOCALE_ICALENDARTYPE, lpCalData, cchData); 2615 case CAL_SCALNAME: 2616 #ifdef __REACTOS__ 2617 if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN) 2618 { 2619 // Wareki 2620 lpCalData[0] = 0x548C; 2621 lpCalData[1] = 0x66A6; 2622 lpCalData[2] = 0; 2623 return 3; 2624 } 2625 #endif 2626 FIXME("Unimplemented caltype %d\n", calinfo); 2627 if (lpCalData) *lpCalData = 0; 2628 return 1; 2629 case CAL_IYEAROFFSETRANGE: 2630 #ifdef __REACTOS__ 2631 if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN) 2632 { 2633 PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL); 2634 if (pEra) 2635 { 2636 if (CalType & CAL_RETURN_NUMBER) 2637 { 2638 *lpValue = pEra->wYear; 2639 return sizeof(DWORD) / sizeof(WCHAR); 2640 } 2641 else 2642 { 2643 static const WCHAR fmtW[] = {'%','u',0}; 2644 WCHAR buffer[10]; 2645 int ret = snprintfW( buffer, 10, fmtW, pEra->wYear ) + 1; 2646 if (!lpCalData) return ret; 2647 if (ret <= cchData) 2648 { 2649 strcpyW( lpCalData, buffer ); 2650 return ret; 2651 } 2652 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 2653 return 0; 2654 } 2655 } 2656 else 2657 { 2658 SetLastError(ERROR_INVALID_PARAMETER); 2659 return 0; 2660 } 2661 } 2662 #endif 2663 FIXME("Unimplemented caltype %d\n", calinfo); 2664 return 0; 2665 case CAL_SERASTRING: 2666 #ifdef __REACTOS__ 2667 if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN) 2668 { 2669 PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL); 2670 if (pEra) 2671 { 2672 RtlStringCchCopyW(lpCalData, cchData, pEra->szEraName); 2673 return strlenW(lpCalData) + 1; 2674 } 2675 else 2676 { 2677 SetLastError(ERROR_INVALID_PARAMETER); 2678 return 0; 2679 } 2680 } 2681 #endif 2682 FIXME("Unimplemented caltype %d\n", calinfo); 2683 return 0; 2684 case CAL_SSHORTDATE: 2685 case CAL_SLONGDATE: 2686 case CAL_SDAYNAME1: 2687 case CAL_SDAYNAME2: 2688 case CAL_SDAYNAME3: 2689 case CAL_SDAYNAME4: 2690 case CAL_SDAYNAME5: 2691 case CAL_SDAYNAME6: 2692 case CAL_SDAYNAME7: 2693 case CAL_SABBREVDAYNAME1: 2694 case CAL_SABBREVDAYNAME2: 2695 case CAL_SABBREVDAYNAME3: 2696 case CAL_SABBREVDAYNAME4: 2697 case CAL_SABBREVDAYNAME5: 2698 case CAL_SABBREVDAYNAME6: 2699 case CAL_SABBREVDAYNAME7: 2700 case CAL_SMONTHNAME1: 2701 case CAL_SMONTHNAME2: 2702 case CAL_SMONTHNAME3: 2703 case CAL_SMONTHNAME4: 2704 case CAL_SMONTHNAME5: 2705 case CAL_SMONTHNAME6: 2706 case CAL_SMONTHNAME7: 2707 case CAL_SMONTHNAME8: 2708 case CAL_SMONTHNAME9: 2709 case CAL_SMONTHNAME10: 2710 case CAL_SMONTHNAME11: 2711 case CAL_SMONTHNAME12: 2712 case CAL_SMONTHNAME13: 2713 case CAL_SABBREVMONTHNAME1: 2714 case CAL_SABBREVMONTHNAME2: 2715 case CAL_SABBREVMONTHNAME3: 2716 case CAL_SABBREVMONTHNAME4: 2717 case CAL_SABBREVMONTHNAME5: 2718 case CAL_SABBREVMONTHNAME6: 2719 case CAL_SABBREVMONTHNAME7: 2720 case CAL_SABBREVMONTHNAME8: 2721 case CAL_SABBREVMONTHNAME9: 2722 case CAL_SABBREVMONTHNAME10: 2723 case CAL_SABBREVMONTHNAME11: 2724 case CAL_SABBREVMONTHNAME12: 2725 case CAL_SABBREVMONTHNAME13: 2726 case CAL_SYEARMONTH: 2727 return GetLocaleInfoW(Locale, caltype_lctype_map[calinfo] | localeflags, lpCalData, cchData); 2728 case CAL_ITWODIGITYEARMAX: 2729 #ifdef __REACTOS__ 2730 if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN) 2731 { 2732 if (CalType & CAL_RETURN_NUMBER) 2733 { 2734 *lpValue = JAPANESE_MAX_TWODIGITYEAR; 2735 return sizeof(DWORD) / sizeof(WCHAR); 2736 } 2737 else 2738 { 2739 static const WCHAR fmtW[] = {'%','u',0}; 2740 WCHAR buffer[10]; 2741 int ret = snprintfW( buffer, 10, fmtW, JAPANESE_MAX_TWODIGITYEAR ) + 1; 2742 if (!lpCalData) return ret; 2743 if (ret <= cchData) 2744 { 2745 strcpyW( lpCalData, buffer ); 2746 return ret; 2747 } 2748 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 2749 return 0; 2750 } 2751 } 2752 #endif 2753 if (CalType & CAL_RETURN_NUMBER) 2754 { 2755 *lpValue = CALINFO_MAX_YEAR; 2756 return sizeof(DWORD) / sizeof(WCHAR); 2757 } 2758 else 2759 { 2760 static const WCHAR fmtW[] = {'%','u',0}; 2761 WCHAR buffer[10]; 2762 int ret = snprintfW( buffer, 10, fmtW, CALINFO_MAX_YEAR ) + 1; 2763 if (!lpCalData) return ret; 2764 if (ret <= cchData) 2765 { 2766 strcpyW( lpCalData, buffer ); 2767 return ret; 2768 } 2769 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 2770 return 0; 2771 } 2772 break; 2773 #ifdef __REACTOS__ 2774 case CAL_SABBREVERASTRING: 2775 if (IS_LCID_JAPANESE(Locale) && Calendar == CAL_JAPAN) 2776 { 2777 PCJAPANESE_ERA pEra = JapaneseEra_Find(NULL); 2778 if (pEra) 2779 { 2780 RtlStringCchCopyW(lpCalData, cchData, pEra->szEraAbbrev); 2781 return strlenW(lpCalData) + 1; 2782 } 2783 } 2784 SetLastError(ERROR_INVALID_PARAMETER); 2785 return 0; 2786 #endif 2787 default: 2788 FIXME("Unknown caltype %d\n", calinfo); 2789 SetLastError(ERROR_INVALID_FLAGS); 2790 return 0; 2791 } 2792 return 0; 2793 } 2794 2795 #if _WIN32_WINNT >= 0x600 2796 /********************************************************************* 2797 * GetCalendarInfoEx (KERNEL32.@) 2798 */ 2799 int WINAPI GetCalendarInfoEx(LPCWSTR locale, CALID calendar, LPCWSTR lpReserved, CALTYPE caltype, 2800 LPWSTR data, int len, DWORD *value) 2801 { 2802 static int once; 2803 2804 LCID lcid = LocaleNameToLCID(locale, 0); 2805 if (!once++) 2806 FIXME("(%s, %d, %p, 0x%08x, %p, %d, %p): semi-stub\n", debugstr_w(locale), calendar, lpReserved, caltype, 2807 data, len, value); 2808 return GetCalendarInfoW(lcid, calendar, caltype, data, len, value); 2809 } 2810 #endif 2811 2812 /********************************************************************* 2813 * SetCalendarInfoA (KERNEL32.@) 2814 * 2815 */ 2816 int WINAPI SetCalendarInfoA(LCID Locale, CALID Calendar, CALTYPE CalType, LPCSTR lpCalData) 2817 { 2818 FIXME("(%08x,%08x,%08x,%s): stub\n", 2819 Locale, Calendar, CalType, debugstr_a(lpCalData)); 2820 return 0; 2821 } 2822 2823 /********************************************************************* 2824 * SetCalendarInfoW (KERNEL32.@) 2825 * 2826 * 2827 */ 2828 int WINAPI SetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, LPCWSTR lpCalData) 2829 { 2830 FIXME("(%08x,%08x,%08x,%s): stub\n", 2831 Locale, Calendar, CalType, debugstr_w(lpCalData)); 2832 return 0; 2833 } 2834