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