Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenlcformat.c
Go to the documentation of this file.
00001 /* 00002 * Locale-dependent format handling 00003 * 00004 * Copyright 1995 Martin von Loewis 00005 * Copyright 1998 David Lee Lambert 00006 * Copyright 2000 Julio César Gázquez 00007 * Copyright 2003 Jon Griffiths 00008 * Copyright 2005 Dmitry Timoshkov 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Lesser General Public 00012 * License as published by the Free Software Foundation; either 00013 * version 2.1 of the License, or (at your option) any later version. 00014 * 00015 * This library is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 * Lesser General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU Lesser General Public 00021 * License along with this library; if not, write to the Free Software 00022 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00023 */ 00024 00025 //#include "config.h" 00026 //#include "wine/port.h" 00027 00028 #include <string.h> 00029 #include <stdarg.h> 00030 #include <stdio.h> 00031 #include <stdlib.h> 00032 00033 #include "windef.h" 00034 #include "winbase.h" 00035 #include "wine/unicode.h" 00036 #include "wine/debug.h" 00037 #include "winternl.h" 00038 00039 #define CRITICAL_SECTION RTL_CRITICAL_SECTION 00040 #define CRITICAL_SECTION_DEBUG RTL_CRITICAL_SECTION_DEBUG 00041 #define CALINFO_MAX_YEAR 2029 00042 00043 #define HeapAlloc RtlAllocateHeap 00044 #define HeapReAlloc RtlReAllocateHeap 00045 #define HeapFree RtlFreeHeap 00046 00047 WINE_DEFAULT_DEBUG_CHANNEL(nls); 00048 00049 #define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */ 00050 #define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */ 00051 00052 /* Since calculating the formatting data for each locale is time-consuming, 00053 * we get the format data for each locale only once and cache it in memory. 00054 * We cache both the system default and user overridden data, after converting 00055 * them into the formats that the functions here expect. Since these functions 00056 * will typically be called with only a small number of the total locales 00057 * installed, the memory overhead is minimal while the speedup is significant. 00058 * 00059 * Our cache takes the form of a singly linked list, whose node is below: 00060 */ 00061 #define NLS_NUM_CACHED_STRINGS 57 00062 00063 typedef struct _NLS_FORMAT_NODE 00064 { 00065 LCID lcid; /* Locale Id */ 00066 DWORD dwFlags; /* 0 or LOCALE_NOUSEROVERRIDE */ 00067 DWORD dwCodePage; /* Default code page (if LOCALE_USE_ANSI_CP not given) */ 00068 NUMBERFMTW fmt; /* Default format for numbers */ 00069 CURRENCYFMTW cyfmt; /* Default format for currencies */ 00070 LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */ 00071 WCHAR szShortAM[2]; /* Short 'AM' marker */ 00072 WCHAR szShortPM[2]; /* Short 'PM' marker */ 00073 struct _NLS_FORMAT_NODE *next; 00074 } NLS_FORMAT_NODE; 00075 00076 /* Macros to get particular data strings from a format node */ 00077 #define GetNegative(fmt) fmt->lppszStrings[0] 00078 #define GetLongDate(fmt) fmt->lppszStrings[1] 00079 #define GetShortDate(fmt) fmt->lppszStrings[2] 00080 #define GetTime(fmt) fmt->lppszStrings[3] 00081 #define GetAM(fmt) fmt->lppszStrings[54] 00082 #define GetPM(fmt) fmt->lppszStrings[55] 00083 #define GetYearMonth(fmt) fmt->lppszStrings[56] 00084 00085 #define GetLongDay(fmt,day) fmt->lppszStrings[4 + day] 00086 #define GetShortDay(fmt,day) fmt->lppszStrings[11 + day] 00087 #define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth] 00088 #define GetGenitiveMonth(fmt,mth) fmt->lppszStrings[30 + mth] 00089 #define GetShortMonth(fmt,mth) fmt->lppszStrings[42 + mth] 00090 00091 /* Write access to the cache is protected by this critical section */ 00092 static CRITICAL_SECTION NLS_FormatsCS; 00093 static CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug = 00094 { 00095 0, 0, &NLS_FormatsCS, 00096 { &NLS_FormatsCS_debug.ProcessLocksList, 00097 &NLS_FormatsCS_debug.ProcessLocksList }, 00098 0, 0, 0 00099 }; 00100 static CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 }; 00101 00102 /************************************************************************** 00103 * NLS_GetLocaleNumber <internal> 00104 * 00105 * Get a numeric locale format value. 00106 */ 00107 static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags) 00108 { 00109 WCHAR szBuff[80]; 00110 DWORD dwVal = 0; 00111 00112 szBuff[0] = '\0'; 00113 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR)); 00114 00115 if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0') 00116 dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0'); 00117 else 00118 { 00119 const WCHAR* iter = szBuff; 00120 dwVal = 0; 00121 while(*iter >= '0' && *iter <= '9') 00122 dwVal = dwVal * 10 + (*iter++ - '0'); 00123 } 00124 return dwVal; 00125 } 00126 00127 /************************************************************************** 00128 * NLS_GetLocaleString <internal> 00129 * 00130 * Get a string locale format value. 00131 */ 00132 static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags) 00133 { 00134 WCHAR szBuff[80], *str; 00135 DWORD dwLen; 00136 00137 szBuff[0] = '\0'; 00138 GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR)); 00139 dwLen = strlenW(szBuff) + 1; 00140 str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR)); 00141 if (str) 00142 memcpy(str, szBuff, dwLen * sizeof(WCHAR)); 00143 return str; 00144 } 00145 00146 #define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \ 00147 TRACE( #type ": %d (%08x)\n", (DWORD)num, (DWORD)num) 00148 00149 #define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \ 00150 TRACE( #type ": %s\n", debugstr_w(str)) 00151 00152 /************************************************************************** 00153 * NLS_GetFormats <internal> 00154 * 00155 * Calculate (and cache) the number formats for a locale. 00156 */ 00157 static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags) 00158 { 00159 /* GetLocaleInfo() identifiers for cached formatting strings */ 00160 static const LCTYPE NLS_LocaleIndices[] = { 00161 LOCALE_SNEGATIVESIGN, 00162 LOCALE_SLONGDATE, LOCALE_SSHORTDATE, 00163 LOCALE_STIMEFORMAT, 00164 LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3, 00165 LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7, 00166 LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3, 00167 LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6, 00168 LOCALE_SABBREVDAYNAME7, 00169 LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3, 00170 LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6, 00171 LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9, 00172 LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12, 00173 LOCALE_SMONTHNAME1 | LOCALE_RETURN_GENITIVE_NAMES, 00174 LOCALE_SMONTHNAME2 | LOCALE_RETURN_GENITIVE_NAMES, 00175 LOCALE_SMONTHNAME3 | LOCALE_RETURN_GENITIVE_NAMES, 00176 LOCALE_SMONTHNAME4 | LOCALE_RETURN_GENITIVE_NAMES, 00177 LOCALE_SMONTHNAME5 | LOCALE_RETURN_GENITIVE_NAMES, 00178 LOCALE_SMONTHNAME6 | LOCALE_RETURN_GENITIVE_NAMES, 00179 LOCALE_SMONTHNAME7 | LOCALE_RETURN_GENITIVE_NAMES, 00180 LOCALE_SMONTHNAME8 | LOCALE_RETURN_GENITIVE_NAMES, 00181 LOCALE_SMONTHNAME9 | LOCALE_RETURN_GENITIVE_NAMES, 00182 LOCALE_SMONTHNAME10 | LOCALE_RETURN_GENITIVE_NAMES, 00183 LOCALE_SMONTHNAME11 | LOCALE_RETURN_GENITIVE_NAMES, 00184 LOCALE_SMONTHNAME12 | LOCALE_RETURN_GENITIVE_NAMES, 00185 LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3, 00186 LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6, 00187 LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9, 00188 LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12, 00189 LOCALE_S1159, LOCALE_S2359, 00190 LOCALE_SYEARMONTH 00191 }; 00192 static NLS_FORMAT_NODE *NLS_CachedFormats = NULL; 00193 NLS_FORMAT_NODE *node = NLS_CachedFormats; 00194 00195 dwFlags &= LOCALE_NOUSEROVERRIDE; 00196 00197 TRACE("(0x%04x,0x%08x)\n", lcid, dwFlags); 00198 00199 /* See if we have already cached the locales number format */ 00200 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next) 00201 node = node->next; 00202 00203 if (!node || node->lcid != lcid || node->dwFlags != dwFlags) 00204 { 00205 NLS_FORMAT_NODE *new_node; 00206 DWORD i; 00207 00208 TRACE("Creating new cache entry\n"); 00209 00210 if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE)))) 00211 return NULL; 00212 00213 GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE); 00214 00215 /* Number Format */ 00216 new_node->lcid = lcid; 00217 new_node->dwFlags = dwFlags; 00218 new_node->next = NULL; 00219 00220 GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS); 00221 GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO); 00222 GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER); 00223 00224 GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING); 00225 if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32) 00226 { 00227 WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n", 00228 new_node->fmt.Grouping); 00229 new_node->fmt.Grouping = 0; 00230 } 00231 00232 GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL); 00233 GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND); 00234 00235 /* Currency Format */ 00236 new_node->cyfmt.NumDigits = new_node->fmt.NumDigits; 00237 new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero; 00238 00239 GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING); 00240 00241 if (new_node->cyfmt.Grouping > 9) 00242 { 00243 WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n", 00244 new_node->cyfmt.Grouping); 00245 new_node->cyfmt.Grouping = 0; 00246 } 00247 00248 GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR); 00249 if (new_node->cyfmt.NegativeOrder > 15) 00250 { 00251 WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n", 00252 new_node->cyfmt.NegativeOrder); 00253 new_node->cyfmt.NegativeOrder = 0; 00254 } 00255 GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY); 00256 if (new_node->cyfmt.PositiveOrder > 3) 00257 { 00258 WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n", 00259 new_node->cyfmt.PositiveOrder); 00260 new_node->cyfmt.PositiveOrder = 0; 00261 } 00262 GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP); 00263 GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP); 00264 GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY); 00265 00266 /* Date/Time Format info, negative character, etc */ 00267 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++) 00268 { 00269 GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]); 00270 } 00271 /* Save some memory if month genitive name is the same or not present */ 00272 for (i = 0; i < 12; i++) 00273 { 00274 if (strcmpW(GetLongMonth(new_node, i), GetGenitiveMonth(new_node, i)) == 0) 00275 { 00276 HeapFree(GetProcessHeap(), 0, GetGenitiveMonth(new_node, i)); 00277 GetGenitiveMonth(new_node, i) = NULL; 00278 } 00279 } 00280 00281 new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0'; 00282 new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0'; 00283 00284 /* Now add the computed format to the cache */ 00285 RtlEnterCriticalSection(&NLS_FormatsCS); 00286 00287 /* Search again: We may have raced to add the node */ 00288 node = NLS_CachedFormats; 00289 while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next) 00290 node = node->next; 00291 00292 if (!node) 00293 { 00294 node = NLS_CachedFormats = new_node; /* Empty list */ 00295 new_node = NULL; 00296 } 00297 else if (node->lcid != lcid || node->dwFlags != dwFlags) 00298 { 00299 node->next = new_node; /* Not in the list, add to end */ 00300 node = new_node; 00301 new_node = NULL; 00302 } 00303 00304 RtlLeaveCriticalSection(&NLS_FormatsCS); 00305 00306 if (new_node) 00307 { 00308 /* We raced and lost: The node was already added by another thread. 00309 * node points to the currently cached node, so free new_node. 00310 */ 00311 for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++) 00312 HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]); 00313 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep); 00314 HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep); 00315 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep); 00316 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep); 00317 HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol); 00318 HeapFree(GetProcessHeap(), 0, new_node); 00319 } 00320 } 00321 return node; 00322 } 00323 00324 /************************************************************************** 00325 * NLS_IsUnicodeOnlyLcid <internal> 00326 * 00327 * Determine if a locale is Unicode only, and thus invalid in ASCII calls. 00328 */ 00329 BOOL NLS_IsUnicodeOnlyLcid(LCID lcid) 00330 { 00331 lcid = ConvertDefaultLocale(lcid); 00332 00333 switch (PRIMARYLANGID(lcid)) 00334 { 00335 case LANG_ARMENIAN: 00336 case LANG_DIVEHI: 00337 case LANG_GEORGIAN: 00338 case LANG_GUJARATI: 00339 case LANG_HINDI: 00340 case LANG_KANNADA: 00341 case LANG_KONKANI: 00342 case LANG_MARATHI: 00343 case LANG_PUNJABI: 00344 case LANG_SANSKRIT: 00345 TRACE("lcid 0x%08x: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid)); 00346 return TRUE; 00347 default: 00348 return FALSE; 00349 } 00350 } 00351 00352 /* 00353 * Formatting of dates, times, numbers and currencies. 00354 */ 00355 00356 #define IsLiteralMarker(p) (p == '\'') 00357 #define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g') 00358 #define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't') 00359 00360 /* Only the following flags can be given if a date/time format is specified */ 00361 #define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY) 00362 #define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \ 00363 TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \ 00364 TIME_NOTIMEMARKER) 00365 00366 /****************************************************************************** 00367 * NLS_GetDateTimeFormatW <internal> 00368 * 00369 * Performs the formatting for GetDateFormatW/GetTimeFormatW. 00370 * 00371 * FIXME 00372 * DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first. 00373 * DATE_LTRREADING/DATE_RTLREADING - Not yet implemented. 00374 */ 00375 static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags, 00376 const SYSTEMTIME* lpTime, LPCWSTR lpFormat, 00377 LPWSTR lpStr, INT cchOut) 00378 { 00379 const NLS_FORMAT_NODE *node; 00380 SYSTEMTIME st; 00381 INT cchWritten = 0; 00382 INT lastFormatPos = 0; 00383 BOOL bSkipping = FALSE; /* Skipping text around marker? */ 00384 BOOL d_dd_formatted = FALSE; /* previous formatted part was for d or dd */ 00385 00386 /* Verify our arguments */ 00387 if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags))) 00388 goto invalid_parameter; 00389 00390 if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY)) 00391 { 00392 if (lpFormat && 00393 ((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) || 00394 (dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS))) 00395 { 00396 goto invalid_flags; 00397 } 00398 00399 if (dwFlags & DATE_DATEVARSONLY) 00400 { 00401 if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING)) 00402 goto invalid_flags; 00403 else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) 00404 FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n"); 00405 00406 switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH)) 00407 { 00408 case 0: 00409 break; 00410 case DATE_SHORTDATE: 00411 case DATE_LONGDATE: 00412 case DATE_YEARMONTH: 00413 if (lpFormat) 00414 goto invalid_flags; 00415 break; 00416 default: 00417 goto invalid_flags; 00418 } 00419 } 00420 } 00421 00422 if (!lpFormat) 00423 { 00424 /* Use the appropriate default format */ 00425 if (dwFlags & DATE_DATEVARSONLY) 00426 { 00427 if (dwFlags & DATE_YEARMONTH) 00428 lpFormat = GetYearMonth(node); 00429 else if (dwFlags & DATE_LONGDATE) 00430 lpFormat = GetLongDate(node); 00431 else 00432 lpFormat = GetShortDate(node); 00433 } 00434 else 00435 lpFormat = GetTime(node); 00436 } 00437 00438 if (!lpTime) 00439 { 00440 GetLocalTime(&st); /* Default to current time */ 00441 lpTime = &st; 00442 } 00443 else 00444 { 00445 if (dwFlags & DATE_DATEVARSONLY) 00446 { 00447 FILETIME ftTmp; 00448 00449 /* Verify the date and correct the D.O.W. if needed */ 00450 memset(&st, 0, sizeof(st)); 00451 st.wYear = lpTime->wYear; 00452 st.wMonth = lpTime->wMonth; 00453 st.wDay = lpTime->wDay; 00454 00455 if (st.wDay > 31 || st.wMonth > 12 || !SystemTimeToFileTime(&st, &ftTmp)) 00456 goto invalid_parameter; 00457 00458 FileTimeToSystemTime(&ftTmp, &st); 00459 lpTime = &st; 00460 } 00461 00462 if (dwFlags & TIME_TIMEVARSONLY) 00463 { 00464 /* Verify the time */ 00465 if (lpTime->wHour > 24 || lpTime->wMinute > 59 || lpTime->wSecond > 59) 00466 goto invalid_parameter; 00467 } 00468 } 00469 00470 /* Format the output */ 00471 while (*lpFormat) 00472 { 00473 if (IsLiteralMarker(*lpFormat)) 00474 { 00475 /* Start of a literal string */ 00476 lpFormat++; 00477 00478 /* Loop until the end of the literal marker or end of the string */ 00479 while (*lpFormat) 00480 { 00481 if (IsLiteralMarker(*lpFormat)) 00482 { 00483 lpFormat++; 00484 if (!IsLiteralMarker(*lpFormat)) 00485 break; /* Terminating literal marker */ 00486 } 00487 00488 if (!cchOut) 00489 cchWritten++; /* Count size only */ 00490 else if (cchWritten >= cchOut) 00491 goto overrun; 00492 else if (!bSkipping) 00493 { 00494 lpStr[cchWritten] = *lpFormat; 00495 cchWritten++; 00496 } 00497 lpFormat++; 00498 } 00499 } 00500 else if ((dwFlags & DATE_DATEVARSONLY && IsDateFmtChar(*lpFormat)) || 00501 (dwFlags & TIME_TIMEVARSONLY && IsTimeFmtChar(*lpFormat))) 00502 { 00503 WCHAR buff[32], fmtChar; 00504 LPCWSTR szAdd = NULL; 00505 DWORD dwVal = 0; 00506 int count = 0, dwLen; 00507 00508 bSkipping = FALSE; 00509 00510 fmtChar = *lpFormat; 00511 while (*lpFormat == fmtChar) 00512 { 00513 count++; 00514 lpFormat++; 00515 } 00516 buff[0] = '\0'; 00517 00518 if (fmtChar != 'M') d_dd_formatted = FALSE; 00519 switch(fmtChar) 00520 { 00521 case 'd': 00522 if (count >= 4) 00523 szAdd = GetLongDay(node, (lpTime->wDayOfWeek + 6) % 7); 00524 else if (count == 3) 00525 szAdd = GetShortDay(node, (lpTime->wDayOfWeek + 6) % 7); 00526 else 00527 { 00528 dwVal = lpTime->wDay; 00529 szAdd = buff; 00530 d_dd_formatted = TRUE; 00531 } 00532 break; 00533 00534 case 'M': 00535 if (count >= 4) 00536 { 00537 LPCWSTR genitive = GetGenitiveMonth(node, lpTime->wMonth - 1); 00538 if (genitive) 00539 { 00540 if (d_dd_formatted) 00541 { 00542 szAdd = genitive; 00543 break; 00544 } 00545 else 00546 { 00547 LPCWSTR format = lpFormat; 00548 /* Look forward now, if next format pattern is for day genitive 00549 name should be used */ 00550 while (*format) 00551 { 00552 /* Skip parts within markers */ 00553 if (IsLiteralMarker(*format)) 00554 { 00555 ++format; 00556 while (*format) 00557 { 00558 if (IsLiteralMarker(*format)) 00559 { 00560 ++format; 00561 if (!IsLiteralMarker(*format)) break; 00562 } 00563 } 00564 } 00565 if (*format != ' ') break; 00566 ++format; 00567 } 00568 /* Only numeric day form matters */ 00569 if (*format && *format == 'd') 00570 { 00571 INT dcount = 1; 00572 while (*++format == 'd') dcount++; 00573 if (dcount < 3) 00574 { 00575 szAdd = genitive; 00576 break; 00577 } 00578 } 00579 } 00580 } 00581 szAdd = GetLongMonth(node, lpTime->wMonth - 1); 00582 } 00583 else if (count == 3) 00584 szAdd = GetShortMonth(node, lpTime->wMonth - 1); 00585 else 00586 { 00587 dwVal = lpTime->wMonth; 00588 szAdd = buff; 00589 } 00590 break; 00591 00592 case 'y': 00593 if (count >= 4) 00594 { 00595 count = 4; 00596 dwVal = lpTime->wYear; 00597 } 00598 else 00599 { 00600 count = count > 2 ? 2 : count; 00601 dwVal = lpTime->wYear % 100; 00602 } 00603 szAdd = buff; 00604 break; 00605 00606 case 'g': 00607 if (count == 2) 00608 { 00609 /* FIXME: Our GetCalendarInfo() does not yet support CAL_SERASTRING. 00610 * When it is fixed, this string should be cached in 'node'. 00611 */ 00612 FIXME("Should be using GetCalendarInfo(CAL_SERASTRING), defaulting to 'AD'\n"); 00613 buff[0] = 'A'; buff[1] = 'D'; buff[2] = '\0'; 00614 } 00615 else 00616 { 00617 buff[0] = 'g'; buff[1] = '\0'; /* Add a literal 'g' */ 00618 } 00619 szAdd = buff; 00620 break; 00621 00622 case 'h': 00623 if (!(dwFlags & TIME_FORCE24HOURFORMAT)) 00624 { 00625 count = count > 2 ? 2 : count; 00626 dwVal = lpTime->wHour == 0 ? 12 : (lpTime->wHour - 1) % 12 + 1; 00627 szAdd = buff; 00628 break; 00629 } 00630 /* .. fall through if we are forced to output in 24 hour format */ 00631 00632 case 'H': 00633 count = count > 2 ? 2 : count; 00634 dwVal = lpTime->wHour; 00635 szAdd = buff; 00636 break; 00637 00638 case 'm': 00639 if (dwFlags & TIME_NOMINUTESORSECONDS) 00640 { 00641 cchWritten = lastFormatPos; /* Skip */ 00642 bSkipping = TRUE; 00643 } 00644 else 00645 { 00646 count = count > 2 ? 2 : count; 00647 dwVal = lpTime->wMinute; 00648 szAdd = buff; 00649 } 00650 break; 00651 00652 case 's': 00653 if (dwFlags & (TIME_NOSECONDS|TIME_NOMINUTESORSECONDS)) 00654 { 00655 cchWritten = lastFormatPos; /* Skip */ 00656 bSkipping = TRUE; 00657 } 00658 else 00659 { 00660 count = count > 2 ? 2 : count; 00661 dwVal = lpTime->wSecond; 00662 szAdd = buff; 00663 } 00664 break; 00665 00666 case 't': 00667 if (dwFlags & TIME_NOTIMEMARKER) 00668 { 00669 cchWritten = lastFormatPos; /* Skip */ 00670 bSkipping = TRUE; 00671 } 00672 else 00673 { 00674 if (count == 1) 00675 szAdd = lpTime->wHour < 12 ? node->szShortAM : node->szShortPM; 00676 else 00677 szAdd = lpTime->wHour < 12 ? GetAM(node) : GetPM(node); 00678 } 00679 break; 00680 } 00681 00682 if (szAdd == buff && buff[0] == '\0') 00683 { 00684 static const WCHAR fmtW[] = {'%','.','*','d',0}; 00685 /* We have a numeric value to add */ 00686 snprintfW(buff, sizeof(buff)/sizeof(WCHAR), fmtW, count, dwVal); 00687 } 00688 00689 dwLen = szAdd ? strlenW(szAdd) : 0; 00690 00691 if (cchOut && dwLen) 00692 { 00693 if (cchWritten + dwLen < cchOut) 00694 memcpy(lpStr + cchWritten, szAdd, dwLen * sizeof(WCHAR)); 00695 else 00696 { 00697 memcpy(lpStr + cchWritten, szAdd, (cchOut - cchWritten) * sizeof(WCHAR)); 00698 goto overrun; 00699 } 00700 } 00701 cchWritten += dwLen; 00702 lastFormatPos = cchWritten; /* Save position of last output format text */ 00703 } 00704 else 00705 { 00706 /* Literal character */ 00707 if (!cchOut) 00708 cchWritten++; /* Count size only */ 00709 else if (cchWritten >= cchOut) 00710 goto overrun; 00711 else if (!bSkipping || *lpFormat == ' ') 00712 { 00713 lpStr[cchWritten] = *lpFormat; 00714 cchWritten++; 00715 } 00716 lpFormat++; 00717 } 00718 } 00719 00720 /* Final string terminator and sanity check */ 00721 if (cchOut) 00722 { 00723 if (cchWritten >= cchOut) 00724 goto overrun; 00725 else 00726 lpStr[cchWritten] = '\0'; 00727 } 00728 cchWritten++; /* Include terminating NUL */ 00729 00730 TRACE("returning length=%d, ouput=%s\n", cchWritten, debugstr_w(lpStr)); 00731 return cchWritten; 00732 00733 overrun: 00734 TRACE("returning 0, (ERROR_INSUFFICIENT_BUFFER)\n"); 00735 SetLastError(ERROR_INSUFFICIENT_BUFFER); 00736 return 0; 00737 00738 invalid_parameter: 00739 SetLastError(ERROR_INVALID_PARAMETER); 00740 return 0; 00741 00742 invalid_flags: 00743 SetLastError(ERROR_INVALID_FLAGS); 00744 return 0; 00745 } 00746 00747 /****************************************************************************** 00748 * NLS_GetDateTimeFormatA <internal> 00749 * 00750 * ASCII wrapper for GetDateFormatA/GetTimeFormatA. 00751 */ 00752 static INT NLS_GetDateTimeFormatA(LCID lcid, DWORD dwFlags, 00753 const SYSTEMTIME* lpTime, 00754 LPCSTR lpFormat, LPSTR lpStr, INT cchOut) 00755 { 00756 DWORD cp = CP_ACP; 00757 WCHAR szFormat[128], szOut[128]; 00758 INT iRet; 00759 00760 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime, 00761 debugstr_a(lpFormat), lpStr, cchOut); 00762 00763 if (NLS_IsUnicodeOnlyLcid(lcid)) 00764 { 00765 SetLastError(ERROR_INVALID_PARAMETER); 00766 return 0; 00767 } 00768 00769 if (!(dwFlags & LOCALE_USE_CP_ACP)) 00770 { 00771 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 00772 if (!node) 00773 { 00774 SetLastError(ERROR_INVALID_PARAMETER); 00775 return 0; 00776 } 00777 00778 cp = node->dwCodePage; 00779 } 00780 00781 if (lpFormat) 00782 MultiByteToWideChar(cp, 0, lpFormat, -1, szFormat, sizeof(szFormat)/sizeof(WCHAR)); 00783 00784 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR))) 00785 cchOut = sizeof(szOut)/sizeof(WCHAR); 00786 00787 szOut[0] = '\0'; 00788 00789 iRet = NLS_GetDateTimeFormatW(lcid, dwFlags, lpTime, lpFormat ? szFormat : NULL, 00790 lpStr ? szOut : NULL, cchOut); 00791 00792 if (lpStr) 00793 { 00794 if (szOut[0]) 00795 WideCharToMultiByte(cp, 0, szOut, iRet ? -1 : cchOut, lpStr, cchOut, 0, 0); 00796 else if (cchOut && iRet) 00797 *lpStr = '\0'; 00798 } 00799 return iRet; 00800 } 00801 00802 /****************************************************************************** 00803 * GetDateFormatA [KERNEL32.@] 00804 * 00805 * Format a date for a given locale. 00806 * 00807 * PARAMS 00808 * lcid [I] Locale to format for 00809 * dwFlags [I] LOCALE_ and DATE_ flags from "winnls.h" 00810 * lpTime [I] Date to format 00811 * lpFormat [I] Format string, or NULL to use the system defaults 00812 * lpDateStr [O] Destination for formatted string 00813 * cchOut [I] Size of lpDateStr, or 0 to calculate the resulting size 00814 * 00815 * NOTES 00816 * - If lpFormat is NULL, lpDateStr will be formatted according to the format 00817 * details returned by GetLocaleInfoA() and modified by dwFlags. 00818 * - lpFormat is a string of characters and formatting tokens. Any characters 00819 * in the string are copied verbatim to lpDateStr, with tokens being replaced 00820 * by the date values they represent. 00821 * - The following tokens have special meanings in a date format string: 00822 *| Token Meaning 00823 *| ----- ------- 00824 *| d Single digit day of the month (no leading 0) 00825 *| dd Double digit day of the month 00826 *| ddd Short name for the day of the week 00827 *| dddd Long name for the day of the week 00828 *| M Single digit month of the year (no leading 0) 00829 *| MM Double digit month of the year 00830 *| MMM Short name for the month of the year 00831 *| MMMM Long name for the month of the year 00832 *| y Double digit year number (no leading 0) 00833 *| yy Double digit year number 00834 *| yyyy Four digit year number 00835 *| gg Era string, for example 'AD'. 00836 * - To output any literal character that could be misidentified as a token, 00837 * enclose it in single quotes. 00838 * - The Ascii version of this function fails if lcid is Unicode only. 00839 * 00840 * RETURNS 00841 * Success: The number of character written to lpDateStr, or that would 00842 * have been written, if cchOut is 0. 00843 * Failure: 0. Use GetLastError() to determine the cause. 00844 */ 00845 INT WINAPI GetDateFormatA( LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, 00846 LPCSTR lpFormat, LPSTR lpDateStr, INT cchOut) 00847 { 00848 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime, 00849 debugstr_a(lpFormat), lpDateStr, cchOut); 00850 00851 return NLS_GetDateTimeFormatA(lcid, dwFlags | DATE_DATEVARSONLY, lpTime, 00852 lpFormat, lpDateStr, cchOut); 00853 } 00854 00855 00856 /****************************************************************************** 00857 * GetDateFormatW [KERNEL32.@] 00858 * 00859 * See GetDateFormatA. 00860 */ 00861 INT WINAPI GetDateFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, 00862 LPCWSTR lpFormat, LPWSTR lpDateStr, INT cchOut) 00863 { 00864 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n", lcid, dwFlags, lpTime, 00865 debugstr_w(lpFormat), lpDateStr, cchOut); 00866 00867 return NLS_GetDateTimeFormatW(lcid, dwFlags|DATE_DATEVARSONLY, lpTime, 00868 lpFormat, lpDateStr, cchOut); 00869 } 00870 00871 /****************************************************************************** 00872 * GetTimeFormatA [KERNEL32.@] 00873 * 00874 * Format a time for a given locale. 00875 * 00876 * PARAMS 00877 * lcid [I] Locale to format for 00878 * dwFlags [I] LOCALE_ and TIME_ flags from "winnls.h" 00879 * lpTime [I] Time to format 00880 * lpFormat [I] Formatting overrides 00881 * lpTimeStr [O] Destination for formatted string 00882 * cchOut [I] Size of lpTimeStr, or 0 to calculate the resulting size 00883 * 00884 * NOTES 00885 * - If lpFormat is NULL, lpszValue will be formatted according to the format 00886 * details returned by GetLocaleInfoA() and modified by dwFlags. 00887 * - lpFormat is a string of characters and formatting tokens. Any characters 00888 * in the string are copied verbatim to lpTimeStr, with tokens being replaced 00889 * by the time values they represent. 00890 * - The following tokens have special meanings in a time format string: 00891 *| Token Meaning 00892 *| ----- ------- 00893 *| h Hours with no leading zero (12-hour clock) 00894 *| hh Hours with full two digits (12-hour clock) 00895 *| H Hours with no leading zero (24-hour clock) 00896 *| HH Hours with full two digits (24-hour clock) 00897 *| m Minutes with no leading zero 00898 *| mm Minutes with full two digits 00899 *| s Seconds with no leading zero 00900 *| ss Seconds with full two digits 00901 *| t Short time marker (e.g. "A" or "P") 00902 *| tt Long time marker (e.g. "AM", "PM") 00903 * - To output any literal character that could be misidentified as a token, 00904 * enclose it in single quotes. 00905 * - The Ascii version of this function fails if lcid is Unicode only. 00906 * 00907 * RETURNS 00908 * Success: The number of character written to lpTimeStr, or that would 00909 * have been written, if cchOut is 0. 00910 * Failure: 0. Use GetLastError() to determine the cause. 00911 */ 00912 INT WINAPI GetTimeFormatA(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, 00913 LPCSTR lpFormat, LPSTR lpTimeStr, INT cchOut) 00914 { 00915 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime, 00916 debugstr_a(lpFormat), lpTimeStr, cchOut); 00917 00918 return NLS_GetDateTimeFormatA(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime, 00919 lpFormat, lpTimeStr, cchOut); 00920 } 00921 00922 /****************************************************************************** 00923 * GetTimeFormatW [KERNEL32.@] 00924 * 00925 * See GetTimeFormatA. 00926 */ 00927 INT WINAPI GetTimeFormatW(LCID lcid, DWORD dwFlags, const SYSTEMTIME* lpTime, 00928 LPCWSTR lpFormat, LPWSTR lpTimeStr, INT cchOut) 00929 { 00930 TRACE("(0x%04x,0x%08x,%p,%s,%p,%d)\n",lcid, dwFlags, lpTime, 00931 debugstr_w(lpFormat), lpTimeStr, cchOut); 00932 00933 return NLS_GetDateTimeFormatW(lcid, dwFlags|TIME_TIMEVARSONLY, lpTime, 00934 lpFormat, lpTimeStr, cchOut); 00935 } 00936 00937 /************************************************************************** 00938 * GetNumberFormatA (KERNEL32.@) 00939 * 00940 * Format a number string for a given locale. 00941 * 00942 * PARAMS 00943 * lcid [I] Locale to format for 00944 * dwFlags [I] LOCALE_ flags from "winnls.h" 00945 * lpszValue [I] String to format 00946 * lpFormat [I] Formatting overrides 00947 * lpNumberStr [O] Destination for formatted string 00948 * cchOut [I] Size of lpNumberStr, or 0 to calculate the resulting size 00949 * 00950 * NOTES 00951 * - lpszValue can contain only '0' - '9', '-' and '.'. 00952 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will 00953 * be formatted according to the format details returned by GetLocaleInfoA(). 00954 * - This function rounds the number string if the number of decimals exceeds the 00955 * locales normal number of decimal places. 00956 * - If cchOut is 0, this function does not write to lpNumberStr. 00957 * - The Ascii version of this function fails if lcid is Unicode only. 00958 * 00959 * RETURNS 00960 * Success: The number of character written to lpNumberStr, or that would 00961 * have been written, if cchOut is 0. 00962 * Failure: 0. Use GetLastError() to determine the cause. 00963 */ 00964 INT WINAPI GetNumberFormatA(LCID lcid, DWORD dwFlags, 00965 LPCSTR lpszValue, const NUMBERFMTA *lpFormat, 00966 LPSTR lpNumberStr, int cchOut) 00967 { 00968 DWORD cp = CP_ACP; 00969 WCHAR szDec[8], szGrp[8], szIn[128], szOut[128]; 00970 NUMBERFMTW fmt; 00971 const NUMBERFMTW *pfmt = NULL; 00972 INT iRet; 00973 00974 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue), 00975 lpFormat, lpNumberStr, cchOut); 00976 00977 if (NLS_IsUnicodeOnlyLcid(lcid)) 00978 { 00979 SetLastError(ERROR_INVALID_PARAMETER); 00980 return 0; 00981 } 00982 00983 if (!(dwFlags & LOCALE_USE_CP_ACP)) 00984 { 00985 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 00986 if (!node) 00987 { 00988 SetLastError(ERROR_INVALID_PARAMETER); 00989 return 0; 00990 } 00991 00992 cp = node->dwCodePage; 00993 } 00994 00995 if (lpFormat) 00996 { 00997 memcpy(&fmt, lpFormat, sizeof(fmt)); 00998 pfmt = &fmt; 00999 if (lpFormat->lpDecimalSep) 01000 { 01001 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR)); 01002 fmt.lpDecimalSep = szDec; 01003 } 01004 if (lpFormat->lpThousandSep) 01005 { 01006 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR)); 01007 fmt.lpThousandSep = szGrp; 01008 } 01009 } 01010 01011 if (lpszValue) 01012 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR)); 01013 01014 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR))) 01015 cchOut = sizeof(szOut)/sizeof(WCHAR); 01016 01017 szOut[0] = '\0'; 01018 01019 iRet = GetNumberFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt, 01020 lpNumberStr ? szOut : NULL, cchOut); 01021 01022 if (szOut[0] && lpNumberStr) 01023 WideCharToMultiByte(cp, 0, szOut, -1, lpNumberStr, cchOut, 0, 0); 01024 return iRet; 01025 } 01026 01027 /* Number parsing state flags */ 01028 #define NF_ISNEGATIVE 0x1 /* '-' found */ 01029 #define NF_ISREAL 0x2 /* '.' found */ 01030 #define NF_DIGITS 0x4 /* '0'-'9' found */ 01031 #define NF_DIGITS_OUT 0x8 /* Digits before the '.' found */ 01032 #define NF_ROUND 0x10 /* Number needs to be rounded */ 01033 01034 /* Formatting options for Numbers */ 01035 #define NLS_NEG_PARENS 0 /* "(1.1)" */ 01036 #define NLS_NEG_LEFT 1 /* "-1.1" */ 01037 #define NLS_NEG_LEFT_SPACE 2 /* "- 1.1" */ 01038 #define NLS_NEG_RIGHT 3 /* "1.1-" */ 01039 #define NLS_NEG_RIGHT_SPACE 4 /* "1.1 -" */ 01040 01041 /************************************************************************** 01042 * GetNumberFormatW (KERNEL32.@) 01043 * 01044 * See GetNumberFormatA. 01045 */ 01046 INT WINAPI GetNumberFormatW(LCID lcid, DWORD dwFlags, 01047 LPCWSTR lpszValue, const NUMBERFMTW *lpFormat, 01048 LPWSTR lpNumberStr, int cchOut) 01049 { 01050 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1; 01051 WCHAR szNegBuff[8]; 01052 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc; 01053 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0; 01054 INT iRet; 01055 01056 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue), 01057 lpFormat, lpNumberStr, cchOut); 01058 01059 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpNumberStr) || 01060 !IsValidLocale(lcid, 0) || 01061 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep))) 01062 { 01063 goto error; 01064 } 01065 01066 if (!lpFormat) 01067 { 01068 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 01069 01070 if (!node) 01071 goto error; 01072 lpFormat = &node->fmt; 01073 lpszNegStart = lpszNeg = GetNegative(node); 01074 } 01075 else 01076 { 01077 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE), 01078 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR)); 01079 lpszNegStart = lpszNeg = szNegBuff; 01080 } 01081 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1; 01082 01083 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP); 01084 01085 /* Format the number backwards into a temporary buffer */ 01086 01087 szSrc = lpszValue; 01088 *szOut-- = '\0'; 01089 01090 /* Check the number for validity */ 01091 while (*szSrc) 01092 { 01093 if (*szSrc >= '0' && *szSrc <= '9') 01094 { 01095 dwState |= NF_DIGITS; 01096 if (dwState & NF_ISREAL) 01097 dwDecimals++; 01098 } 01099 else if (*szSrc == '-') 01100 { 01101 if (dwState) 01102 goto error; /* '-' not first character */ 01103 dwState |= NF_ISNEGATIVE; 01104 } 01105 else if (*szSrc == '.') 01106 { 01107 if (dwState & NF_ISREAL) 01108 goto error; /* More than one '.' */ 01109 dwState |= NF_ISREAL; 01110 } 01111 else 01112 goto error; /* Invalid char */ 01113 szSrc++; 01114 } 01115 szSrc--; /* Point to last character */ 01116 01117 if (!(dwState & NF_DIGITS)) 01118 goto error; /* No digits */ 01119 01120 /* Add any trailing negative sign */ 01121 if (dwState & NF_ISNEGATIVE) 01122 { 01123 switch (lpFormat->NegativeOrder) 01124 { 01125 case NLS_NEG_PARENS: 01126 *szOut-- = ')'; 01127 break; 01128 case NLS_NEG_RIGHT: 01129 case NLS_NEG_RIGHT_SPACE: 01130 while (lpszNeg >= lpszNegStart) 01131 *szOut-- = *lpszNeg--; 01132 if (lpFormat->NegativeOrder == NLS_NEG_RIGHT_SPACE) 01133 *szOut-- = ' '; 01134 break; 01135 } 01136 } 01137 01138 /* Copy all digits up to the decimal point */ 01139 if (!lpFormat->NumDigits) 01140 { 01141 if (dwState & NF_ISREAL) 01142 { 01143 while (*szSrc != '.') /* Don't write any decimals or a separator */ 01144 { 01145 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND))) 01146 dwState |= NF_ROUND; 01147 else 01148 dwState &= ~NF_ROUND; 01149 szSrc--; 01150 } 01151 szSrc--; 01152 } 01153 } 01154 else 01155 { 01156 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1; 01157 01158 if (dwDecimals <= lpFormat->NumDigits) 01159 { 01160 dwDecimals = lpFormat->NumDigits - dwDecimals; 01161 while (dwDecimals--) 01162 *szOut-- = '0'; /* Pad to correct number of dp */ 01163 } 01164 else 01165 { 01166 dwDecimals -= lpFormat->NumDigits; 01167 /* Skip excess decimals, and determine if we have to round the number */ 01168 while (dwDecimals--) 01169 { 01170 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND))) 01171 dwState |= NF_ROUND; 01172 else 01173 dwState &= ~NF_ROUND; 01174 szSrc--; 01175 } 01176 } 01177 01178 if (dwState & NF_ISREAL) 01179 { 01180 while (*szSrc != '.') 01181 { 01182 if (dwState & NF_ROUND) 01183 { 01184 if (*szSrc == '9') 01185 *szOut-- = '0'; /* continue rounding */ 01186 else 01187 { 01188 dwState &= ~NF_ROUND; 01189 *szOut-- = (*szSrc)+1; 01190 } 01191 szSrc--; 01192 } 01193 else 01194 *szOut-- = *szSrc--; /* Write existing decimals */ 01195 } 01196 szSrc--; /* Skip '.' */ 01197 } 01198 01199 while (lpszDec >= lpFormat->lpDecimalSep) 01200 *szOut-- = *lpszDec--; /* Write decimal separator */ 01201 } 01202 01203 dwGroupCount = lpFormat->Grouping == 32 ? 3 : lpFormat->Grouping; 01204 01205 /* Write the remaining whole number digits, including grouping chars */ 01206 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9') 01207 { 01208 if (dwState & NF_ROUND) 01209 { 01210 if (*szSrc == '9') 01211 *szOut-- = '0'; /* continue rounding */ 01212 else 01213 { 01214 dwState &= ~NF_ROUND; 01215 *szOut-- = (*szSrc)+1; 01216 } 01217 szSrc--; 01218 } 01219 else 01220 *szOut-- = *szSrc--; 01221 01222 dwState |= NF_DIGITS_OUT; 01223 dwCurrentGroupCount++; 01224 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-') 01225 { 01226 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1; 01227 01228 while (lpszGrp >= lpFormat->lpThousandSep) 01229 *szOut-- = *lpszGrp--; /* Write grouping char */ 01230 01231 dwCurrentGroupCount = 0; 01232 if (lpFormat->Grouping == 32) 01233 dwGroupCount = 2; /* Indic grouping: 3 then 2 */ 01234 } 01235 } 01236 if (dwState & NF_ROUND) 01237 { 01238 *szOut-- = '1'; /* e.g. .6 > 1.0 */ 01239 } 01240 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero) 01241 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */ 01242 01243 /* Add any leading negative sign */ 01244 if (dwState & NF_ISNEGATIVE) 01245 { 01246 switch (lpFormat->NegativeOrder) 01247 { 01248 case NLS_NEG_PARENS: 01249 *szOut-- = '('; 01250 break; 01251 case NLS_NEG_LEFT_SPACE: 01252 *szOut-- = ' '; 01253 /* Fall through */ 01254 case NLS_NEG_LEFT: 01255 while (lpszNeg >= lpszNegStart) 01256 *szOut-- = *lpszNeg--; 01257 break; 01258 } 01259 } 01260 szOut++; 01261 01262 iRet = strlenW(szOut) + 1; 01263 if (cchOut) 01264 { 01265 if (iRet <= cchOut) 01266 memcpy(lpNumberStr, szOut, iRet * sizeof(WCHAR)); 01267 else 01268 { 01269 memcpy(lpNumberStr, szOut, cchOut * sizeof(WCHAR)); 01270 lpNumberStr[cchOut - 1] = '\0'; 01271 SetLastError(ERROR_INSUFFICIENT_BUFFER); 01272 iRet = 0; 01273 } 01274 } 01275 return iRet; 01276 01277 error: 01278 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER); 01279 return 0; 01280 } 01281 01282 /************************************************************************** 01283 * GetCurrencyFormatA (KERNEL32.@) 01284 * 01285 * Format a currency string for a given locale. 01286 * 01287 * PARAMS 01288 * lcid [I] Locale to format for 01289 * dwFlags [I] LOCALE_ flags from "winnls.h" 01290 * lpszValue [I] String to format 01291 * lpFormat [I] Formatting overrides 01292 * lpCurrencyStr [O] Destination for formatted string 01293 * cchOut [I] Size of lpCurrencyStr, or 0 to calculate the resulting size 01294 * 01295 * NOTES 01296 * - lpszValue can contain only '0' - '9', '-' and '.'. 01297 * - If lpFormat is non-NULL, dwFlags must be 0. In this case lpszValue will 01298 * be formatted according to the format details returned by GetLocaleInfoA(). 01299 * - This function rounds the currency if the number of decimals exceeds the 01300 * locales number of currency decimal places. 01301 * - If cchOut is 0, this function does not write to lpCurrencyStr. 01302 * - The Ascii version of this function fails if lcid is Unicode only. 01303 * 01304 * RETURNS 01305 * Success: The number of character written to lpNumberStr, or that would 01306 * have been written, if cchOut is 0. 01307 * Failure: 0. Use GetLastError() to determine the cause. 01308 */ 01309 INT WINAPI GetCurrencyFormatA(LCID lcid, DWORD dwFlags, 01310 LPCSTR lpszValue, const CURRENCYFMTA *lpFormat, 01311 LPSTR lpCurrencyStr, int cchOut) 01312 { 01313 DWORD cp = CP_ACP; 01314 WCHAR szDec[8], szGrp[8], szCy[8], szIn[128], szOut[128]; 01315 CURRENCYFMTW fmt; 01316 const CURRENCYFMTW *pfmt = NULL; 01317 INT iRet; 01318 01319 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_a(lpszValue), 01320 lpFormat, lpCurrencyStr, cchOut); 01321 01322 if (NLS_IsUnicodeOnlyLcid(lcid)) 01323 { 01324 SetLastError(ERROR_INVALID_PARAMETER); 01325 return 0; 01326 } 01327 01328 if (!(dwFlags & LOCALE_USE_CP_ACP)) 01329 { 01330 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 01331 if (!node) 01332 { 01333 SetLastError(ERROR_INVALID_PARAMETER); 01334 return 0; 01335 } 01336 01337 cp = node->dwCodePage; 01338 } 01339 01340 if (lpFormat) 01341 { 01342 memcpy(&fmt, lpFormat, sizeof(fmt)); 01343 pfmt = &fmt; 01344 if (lpFormat->lpDecimalSep) 01345 { 01346 MultiByteToWideChar(cp, 0, lpFormat->lpDecimalSep, -1, szDec, sizeof(szDec)/sizeof(WCHAR)); 01347 fmt.lpDecimalSep = szDec; 01348 } 01349 if (lpFormat->lpThousandSep) 01350 { 01351 MultiByteToWideChar(cp, 0, lpFormat->lpThousandSep, -1, szGrp, sizeof(szGrp)/sizeof(WCHAR)); 01352 fmt.lpThousandSep = szGrp; 01353 } 01354 if (lpFormat->lpCurrencySymbol) 01355 { 01356 MultiByteToWideChar(cp, 0, lpFormat->lpCurrencySymbol, -1, szCy, sizeof(szCy)/sizeof(WCHAR)); 01357 fmt.lpCurrencySymbol = szCy; 01358 } 01359 } 01360 01361 if (lpszValue) 01362 MultiByteToWideChar(cp, 0, lpszValue, -1, szIn, sizeof(szIn)/sizeof(WCHAR)); 01363 01364 if (cchOut > (int)(sizeof(szOut)/sizeof(WCHAR))) 01365 cchOut = sizeof(szOut)/sizeof(WCHAR); 01366 01367 szOut[0] = '\0'; 01368 01369 iRet = GetCurrencyFormatW(lcid, dwFlags, lpszValue ? szIn : NULL, pfmt, 01370 lpCurrencyStr ? szOut : NULL, cchOut); 01371 01372 if (szOut[0] && lpCurrencyStr) 01373 WideCharToMultiByte(cp, 0, szOut, -1, lpCurrencyStr, cchOut, 0, 0); 01374 return iRet; 01375 } 01376 01377 /* Formatting states for Currencies. We use flags to avoid code duplication. */ 01378 #define CF_PARENS 0x1 /* Parentheses */ 01379 #define CF_MINUS_LEFT 0x2 /* '-' to the left */ 01380 #define CF_MINUS_RIGHT 0x4 /* '-' to the right */ 01381 #define CF_MINUS_BEFORE 0x8 /* '-' before '$' */ 01382 #define CF_CY_LEFT 0x10 /* '$' to the left */ 01383 #define CF_CY_RIGHT 0x20 /* '$' to the right */ 01384 #define CF_CY_SPACE 0x40 /* ' ' by '$' */ 01385 01386 /************************************************************************** 01387 * GetCurrencyFormatW (KERNEL32.@) 01388 * 01389 * See GetCurrencyFormatA. 01390 */ 01391 INT WINAPI GetCurrencyFormatW(LCID lcid, DWORD dwFlags, 01392 LPCWSTR lpszValue, const CURRENCYFMTW *lpFormat, 01393 LPWSTR lpCurrencyStr, int cchOut) 01394 { 01395 static const BYTE NLS_NegCyFormats[16] = 01396 { 01397 CF_PARENS|CF_CY_LEFT, /* ($1.1) */ 01398 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT, /* -$1.1 */ 01399 CF_MINUS_LEFT|CF_CY_LEFT, /* $-1.1 */ 01400 CF_MINUS_RIGHT|CF_CY_LEFT, /* $1.1- */ 01401 CF_PARENS|CF_CY_RIGHT, /* (1.1$) */ 01402 CF_MINUS_LEFT|CF_CY_RIGHT, /* -1.1$ */ 01403 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT, /* 1.1-$ */ 01404 CF_MINUS_RIGHT|CF_CY_RIGHT, /* 1.1$- */ 01405 CF_MINUS_LEFT|CF_CY_RIGHT|CF_CY_SPACE, /* -1.1 $ */ 01406 CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT|CF_CY_SPACE, /* -$ 1.1 */ 01407 CF_MINUS_RIGHT|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $- */ 01408 CF_MINUS_RIGHT|CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1- */ 01409 CF_MINUS_LEFT|CF_CY_LEFT|CF_CY_SPACE, /* $ -1.1 */ 01410 CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT|CF_CY_SPACE, /* 1.1- $ */ 01411 CF_PARENS|CF_CY_LEFT|CF_CY_SPACE, /* ($ 1.1) */ 01412 CF_PARENS|CF_CY_RIGHT|CF_CY_SPACE, /* (1.1 $) */ 01413 }; 01414 static const BYTE NLS_PosCyFormats[4] = 01415 { 01416 CF_CY_LEFT, /* $1.1 */ 01417 CF_CY_RIGHT, /* 1.1$ */ 01418 CF_CY_LEFT|CF_CY_SPACE, /* $ 1.1 */ 01419 CF_CY_RIGHT|CF_CY_SPACE, /* 1.1 $ */ 01420 }; 01421 WCHAR szBuff[128], *szOut = szBuff + sizeof(szBuff) / sizeof(WCHAR) - 1; 01422 WCHAR szNegBuff[8]; 01423 const WCHAR *lpszNeg = NULL, *lpszNegStart, *szSrc, *lpszCy, *lpszCyStart; 01424 DWORD dwState = 0, dwDecimals = 0, dwGroupCount = 0, dwCurrentGroupCount = 0, dwFmt; 01425 INT iRet; 01426 01427 TRACE("(0x%04x,0x%08x,%s,%p,%p,%d)\n", lcid, dwFlags, debugstr_w(lpszValue), 01428 lpFormat, lpCurrencyStr, cchOut); 01429 01430 if (!lpszValue || cchOut < 0 || (cchOut > 0 && !lpCurrencyStr) || 01431 !IsValidLocale(lcid, 0) || 01432 (lpFormat && (dwFlags || !lpFormat->lpDecimalSep || !lpFormat->lpThousandSep || 01433 !lpFormat->lpCurrencySymbol || lpFormat->NegativeOrder > 15 || 01434 lpFormat->PositiveOrder > 3))) 01435 { 01436 goto error; 01437 } 01438 01439 if (!lpFormat) 01440 { 01441 const NLS_FORMAT_NODE *node = NLS_GetFormats(lcid, dwFlags); 01442 01443 if (!node) 01444 goto error; 01445 01446 lpFormat = &node->cyfmt; 01447 lpszNegStart = lpszNeg = GetNegative(node); 01448 } 01449 else 01450 { 01451 GetLocaleInfoW(lcid, LOCALE_SNEGATIVESIGN|(dwFlags & LOCALE_NOUSEROVERRIDE), 01452 szNegBuff, sizeof(szNegBuff)/sizeof(WCHAR)); 01453 lpszNegStart = lpszNeg = szNegBuff; 01454 } 01455 dwFlags &= (LOCALE_NOUSEROVERRIDE|LOCALE_USE_CP_ACP); 01456 01457 lpszNeg = lpszNeg + strlenW(lpszNeg) - 1; 01458 lpszCyStart = lpFormat->lpCurrencySymbol; 01459 lpszCy = lpszCyStart + strlenW(lpszCyStart) - 1; 01460 01461 /* Format the currency backwards into a temporary buffer */ 01462 01463 szSrc = lpszValue; 01464 *szOut-- = '\0'; 01465 01466 /* Check the number for validity */ 01467 while (*szSrc) 01468 { 01469 if (*szSrc >= '0' && *szSrc <= '9') 01470 { 01471 dwState |= NF_DIGITS; 01472 if (dwState & NF_ISREAL) 01473 dwDecimals++; 01474 } 01475 else if (*szSrc == '-') 01476 { 01477 if (dwState) 01478 goto error; /* '-' not first character */ 01479 dwState |= NF_ISNEGATIVE; 01480 } 01481 else if (*szSrc == '.') 01482 { 01483 if (dwState & NF_ISREAL) 01484 goto error; /* More than one '.' */ 01485 dwState |= NF_ISREAL; 01486 } 01487 else 01488 goto error; /* Invalid char */ 01489 szSrc++; 01490 } 01491 szSrc--; /* Point to last character */ 01492 01493 if (!(dwState & NF_DIGITS)) 01494 goto error; /* No digits */ 01495 01496 if (dwState & NF_ISNEGATIVE) 01497 dwFmt = NLS_NegCyFormats[lpFormat->NegativeOrder]; 01498 else 01499 dwFmt = NLS_PosCyFormats[lpFormat->PositiveOrder]; 01500 01501 /* Add any trailing negative or currency signs */ 01502 if (dwFmt & CF_PARENS) 01503 *szOut-- = ')'; 01504 01505 while (dwFmt & (CF_MINUS_RIGHT|CF_CY_RIGHT)) 01506 { 01507 switch (dwFmt & (CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT)) 01508 { 01509 case CF_MINUS_RIGHT: 01510 case CF_MINUS_RIGHT|CF_CY_RIGHT: 01511 while (lpszNeg >= lpszNegStart) 01512 *szOut-- = *lpszNeg--; 01513 dwFmt &= ~CF_MINUS_RIGHT; 01514 break; 01515 01516 case CF_CY_RIGHT: 01517 case CF_MINUS_BEFORE|CF_CY_RIGHT: 01518 case CF_MINUS_RIGHT|CF_MINUS_BEFORE|CF_CY_RIGHT: 01519 while (lpszCy >= lpszCyStart) 01520 *szOut-- = *lpszCy--; 01521 if (dwFmt & CF_CY_SPACE) 01522 *szOut-- = ' '; 01523 dwFmt &= ~(CF_CY_RIGHT|CF_MINUS_BEFORE); 01524 break; 01525 } 01526 } 01527 01528 /* Copy all digits up to the decimal point */ 01529 if (!lpFormat->NumDigits) 01530 { 01531 if (dwState & NF_ISREAL) 01532 { 01533 while (*szSrc != '.') /* Don't write any decimals or a separator */ 01534 { 01535 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND))) 01536 dwState |= NF_ROUND; 01537 else 01538 dwState &= ~NF_ROUND; 01539 szSrc--; 01540 } 01541 szSrc--; 01542 } 01543 } 01544 else 01545 { 01546 LPWSTR lpszDec = lpFormat->lpDecimalSep + strlenW(lpFormat->lpDecimalSep) - 1; 01547 01548 if (dwDecimals <= lpFormat->NumDigits) 01549 { 01550 dwDecimals = lpFormat->NumDigits - dwDecimals; 01551 while (dwDecimals--) 01552 *szOut-- = '0'; /* Pad to correct number of dp */ 01553 } 01554 else 01555 { 01556 dwDecimals -= lpFormat->NumDigits; 01557 /* Skip excess decimals, and determine if we have to round the number */ 01558 while (dwDecimals--) 01559 { 01560 if (*szSrc >= '5' || (*szSrc == '4' && (dwState & NF_ROUND))) 01561 dwState |= NF_ROUND; 01562 else 01563 dwState &= ~NF_ROUND; 01564 szSrc--; 01565 } 01566 } 01567 01568 if (dwState & NF_ISREAL) 01569 { 01570 while (*szSrc != '.') 01571 { 01572 if (dwState & NF_ROUND) 01573 { 01574 if (*szSrc == '9') 01575 *szOut-- = '0'; /* continue rounding */ 01576 else 01577 { 01578 dwState &= ~NF_ROUND; 01579 *szOut-- = (*szSrc)+1; 01580 } 01581 szSrc--; 01582 } 01583 else 01584 *szOut-- = *szSrc--; /* Write existing decimals */ 01585 } 01586 szSrc--; /* Skip '.' */ 01587 } 01588 while (lpszDec >= lpFormat->lpDecimalSep) 01589 *szOut-- = *lpszDec--; /* Write decimal separator */ 01590 } 01591 01592 dwGroupCount = lpFormat->Grouping; 01593 01594 /* Write the remaining whole number digits, including grouping chars */ 01595 while (szSrc >= lpszValue && *szSrc >= '0' && *szSrc <= '9') 01596 { 01597 if (dwState & NF_ROUND) 01598 { 01599 if (*szSrc == '9') 01600 *szOut-- = '0'; /* continue rounding */ 01601 else 01602 { 01603 dwState &= ~NF_ROUND; 01604 *szOut-- = (*szSrc)+1; 01605 } 01606 szSrc--; 01607 } 01608 else 01609 *szOut-- = *szSrc--; 01610 01611 dwState |= NF_DIGITS_OUT; 01612 dwCurrentGroupCount++; 01613 if (szSrc >= lpszValue && dwCurrentGroupCount == dwGroupCount && *szSrc != '-') 01614 { 01615 LPWSTR lpszGrp = lpFormat->lpThousandSep + strlenW(lpFormat->lpThousandSep) - 1; 01616 01617 while (lpszGrp >= lpFormat->lpThousandSep) 01618 *szOut-- = *lpszGrp--; /* Write grouping char */ 01619 01620 dwCurrentGroupCount = 0; 01621 } 01622 } 01623 if (dwState & NF_ROUND) 01624 *szOut-- = '1'; /* e.g. .6 > 1.0 */ 01625 else if (!(dwState & NF_DIGITS_OUT) && lpFormat->LeadingZero) 01626 *szOut-- = '0'; /* Add leading 0 if we have no digits before the decimal point */ 01627 01628 /* Add any leading negative or currency sign */ 01629 while (dwFmt & (CF_MINUS_LEFT|CF_CY_LEFT)) 01630 { 01631 switch (dwFmt & (CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT)) 01632 { 01633 case CF_MINUS_LEFT: 01634 case CF_MINUS_LEFT|CF_CY_LEFT: 01635 while (lpszNeg >= lpszNegStart) 01636 *szOut-- = *lpszNeg--; 01637 dwFmt &= ~CF_MINUS_LEFT; 01638 break; 01639 01640 case CF_CY_LEFT: 01641 case CF_CY_LEFT|CF_MINUS_BEFORE: 01642 case CF_MINUS_LEFT|CF_MINUS_BEFORE|CF_CY_LEFT: 01643 if (dwFmt & CF_CY_SPACE) 01644 *szOut-- = ' '; 01645 while (lpszCy >= lpszCyStart) 01646 *szOut-- = *lpszCy--; 01647 dwFmt &= ~(CF_CY_LEFT|CF_MINUS_BEFORE); 01648 break; 01649 } 01650 } 01651 if (dwFmt & CF_PARENS) 01652 *szOut-- = '('; 01653 szOut++; 01654 01655 iRet = strlenW(szOut) + 1; 01656 if (cchOut) 01657 { 01658 if (iRet <= cchOut) 01659 memcpy(lpCurrencyStr, szOut, iRet * sizeof(WCHAR)); 01660 else 01661 { 01662 memcpy(lpCurrencyStr, szOut, cchOut * sizeof(WCHAR)); 01663 lpCurrencyStr[cchOut - 1] = '\0'; 01664 SetLastError(ERROR_INSUFFICIENT_BUFFER); 01665 iRet = 0; 01666 } 01667 } 01668 return iRet; 01669 01670 error: 01671 SetLastError(lpFormat && dwFlags ? ERROR_INVALID_FLAGS : ERROR_INVALID_PARAMETER); 01672 return 0; 01673 } 01674 01675 /* FIXME: Everything below here needs to move somewhere else along with the 01676 * other EnumXXX functions, when a method for storing resources for 01677 * alternate calendars is determined. 01678 */ 01679 01680 /************************************************************************** 01681 * EnumDateFormatsExA (KERNEL32.@) 01682 * 01683 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 01684 * LOCALE_NOUSEROVERRIDE here as well? 01685 */ 01686 BOOL WINAPI EnumDateFormatsExA(DATEFMT_ENUMPROCEXA proc, LCID lcid, DWORD flags) 01687 { 01688 CALID cal_id; 01689 char buf[256]; 01690 01691 if (!proc) 01692 { 01693 SetLastError(ERROR_INVALID_PARAMETER); 01694 return FALSE; 01695 } 01696 01697 if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR))) 01698 return FALSE; 01699 01700 switch (flags & ~LOCALE_USE_CP_ACP) 01701 { 01702 case 0: 01703 case DATE_SHORTDATE: 01704 if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01705 proc(buf, cal_id); 01706 break; 01707 01708 case DATE_LONGDATE: 01709 if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01710 proc(buf, cal_id); 01711 break; 01712 01713 case DATE_YEARMONTH: 01714 if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01715 proc(buf, cal_id); 01716 break; 01717 01718 default: 01719 FIXME("Unknown date format (%d)\n", flags); 01720 SetLastError(ERROR_INVALID_PARAMETER); 01721 return FALSE; 01722 } 01723 return TRUE; 01724 } 01725 01726 /************************************************************************** 01727 * EnumDateFormatsExW (KERNEL32.@) 01728 */ 01729 BOOL WINAPI EnumDateFormatsExW(DATEFMT_ENUMPROCEXW proc, LCID lcid, DWORD flags) 01730 { 01731 CALID cal_id; 01732 WCHAR buf[256]; 01733 01734 if (!proc) 01735 { 01736 SetLastError(ERROR_INVALID_PARAMETER); 01737 return FALSE; 01738 } 01739 01740 if (!GetLocaleInfoW(lcid, LOCALE_ICALENDARTYPE|LOCALE_RETURN_NUMBER, (LPWSTR)&cal_id, sizeof(cal_id)/sizeof(WCHAR))) 01741 return FALSE; 01742 01743 switch (flags & ~LOCALE_USE_CP_ACP) 01744 { 01745 case 0: 01746 case DATE_SHORTDATE: 01747 if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01748 proc(buf, cal_id); 01749 break; 01750 01751 case DATE_LONGDATE: 01752 if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01753 proc(buf, cal_id); 01754 break; 01755 01756 case DATE_YEARMONTH: 01757 if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01758 proc(buf, cal_id); 01759 break; 01760 01761 default: 01762 FIXME("Unknown date format (%d)\n", flags); 01763 SetLastError(ERROR_INVALID_PARAMETER); 01764 return FALSE; 01765 } 01766 return TRUE; 01767 } 01768 01769 /************************************************************************** 01770 * EnumDateFormatsA (KERNEL32.@) 01771 * 01772 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 01773 * LOCALE_NOUSEROVERRIDE here as well? 01774 */ 01775 BOOL WINAPI EnumDateFormatsA(DATEFMT_ENUMPROCA proc, LCID lcid, DWORD flags) 01776 { 01777 char buf[256]; 01778 01779 if (!proc) 01780 { 01781 SetLastError(ERROR_INVALID_PARAMETER); 01782 return FALSE; 01783 } 01784 01785 switch (flags & ~LOCALE_USE_CP_ACP) 01786 { 01787 case 0: 01788 case DATE_SHORTDATE: 01789 if (GetLocaleInfoA(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01790 proc(buf); 01791 break; 01792 01793 case DATE_LONGDATE: 01794 if (GetLocaleInfoA(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01795 proc(buf); 01796 break; 01797 01798 case DATE_YEARMONTH: 01799 if (GetLocaleInfoA(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01800 proc(buf); 01801 break; 01802 01803 default: 01804 FIXME("Unknown date format (%d)\n", flags); 01805 SetLastError(ERROR_INVALID_PARAMETER); 01806 return FALSE; 01807 } 01808 return TRUE; 01809 } 01810 01811 /************************************************************************** 01812 * EnumDateFormatsW (KERNEL32.@) 01813 */ 01814 BOOL WINAPI EnumDateFormatsW(DATEFMT_ENUMPROCW proc, LCID lcid, DWORD flags) 01815 { 01816 WCHAR buf[256]; 01817 01818 if (!proc) 01819 { 01820 SetLastError(ERROR_INVALID_PARAMETER); 01821 return FALSE; 01822 } 01823 01824 switch (flags & ~LOCALE_USE_CP_ACP) 01825 { 01826 case 0: 01827 case DATE_SHORTDATE: 01828 if (GetLocaleInfoW(lcid, LOCALE_SSHORTDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01829 proc(buf); 01830 break; 01831 01832 case DATE_LONGDATE: 01833 if (GetLocaleInfoW(lcid, LOCALE_SLONGDATE | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01834 proc(buf); 01835 break; 01836 01837 case DATE_YEARMONTH: 01838 if (GetLocaleInfoW(lcid, LOCALE_SYEARMONTH | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01839 proc(buf); 01840 break; 01841 01842 default: 01843 FIXME("Unknown date format (%d)\n", flags); 01844 SetLastError(ERROR_INVALID_PARAMETER); 01845 return FALSE; 01846 } 01847 return TRUE; 01848 } 01849 01850 /************************************************************************** 01851 * EnumTimeFormatsA (KERNEL32.@) 01852 * 01853 * FIXME: MSDN mentions only LOCALE_USE_CP_ACP, should we handle 01854 * LOCALE_NOUSEROVERRIDE here as well? 01855 */ 01856 BOOL WINAPI EnumTimeFormatsA(TIMEFMT_ENUMPROCA proc, LCID lcid, DWORD flags) 01857 { 01858 char buf[256]; 01859 01860 if (!proc) 01861 { 01862 SetLastError(ERROR_INVALID_PARAMETER); 01863 return FALSE; 01864 } 01865 01866 switch (flags & ~LOCALE_USE_CP_ACP) 01867 { 01868 case 0: 01869 if (GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01870 proc(buf); 01871 break; 01872 01873 default: 01874 FIXME("Unknown time format (%d)\n", flags); 01875 SetLastError(ERROR_INVALID_PARAMETER); 01876 return FALSE; 01877 } 01878 return TRUE; 01879 } 01880 01881 /************************************************************************** 01882 * EnumTimeFormatsW (KERNEL32.@) 01883 */ 01884 BOOL WINAPI EnumTimeFormatsW(TIMEFMT_ENUMPROCW proc, LCID lcid, DWORD flags) 01885 { 01886 WCHAR buf[256]; 01887 01888 if (!proc) 01889 { 01890 SetLastError(ERROR_INVALID_PARAMETER); 01891 return FALSE; 01892 } 01893 01894 switch (flags & ~LOCALE_USE_CP_ACP) 01895 { 01896 case 0: 01897 if (GetLocaleInfoW(lcid, LOCALE_STIMEFORMAT | (flags & LOCALE_USE_CP_ACP), buf, 256)) 01898 proc(buf); 01899 break; 01900 01901 default: 01902 FIXME("Unknown time format (%d)\n", flags); 01903 SetLastError(ERROR_INVALID_PARAMETER); 01904 return FALSE; 01905 } 01906 return TRUE; 01907 } 01908 01909 /****************************************************************************** 01910 * NLS_EnumCalendarInfoAW <internal> 01911 * Enumerates calendar information for a specified locale. 01912 * 01913 * PARAMS 01914 * calinfoproc [I] Pointer to the callback 01915 * locale [I] The locale for which to retrieve calendar information. 01916 * This parameter can be a locale identifier created by the 01917 * MAKELCID macro, or one of the following values: 01918 * LOCALE_SYSTEM_DEFAULT 01919 * Use the default system locale. 01920 * LOCALE_USER_DEFAULT 01921 * Use the default user locale. 01922 * calendar [I] The calendar for which information is requested, or 01923 * ENUM_ALL_CALENDARS. 01924 * caltype [I] The type of calendar information to be returned. Note 01925 * that only one CALTYPE value can be specified per call 01926 * of this function, except where noted. 01927 * unicode [I] Specifies if the callback expects a unicode string. 01928 * ex [I] Specifies if the callback needs the calendar identifier. 01929 * 01930 * RETURNS 01931 * Success: TRUE. 01932 * Failure: FALSE. Use GetLastError() to determine the cause. 01933 * 01934 * NOTES 01935 * When the ANSI version of this function is used with a Unicode-only LCID, 01936 * the call can succeed because the system uses the system code page. 01937 * However, characters that are undefined in the system code page appear 01938 * in the string as a question mark (?). 01939 * 01940 * TODO 01941 * The above note should be respected by GetCalendarInfoA. 01942 */ 01943 static BOOL NLS_EnumCalendarInfoAW(void *calinfoproc, LCID locale, 01944 CALID calendar, CALTYPE caltype, BOOL unicode, BOOL ex ) 01945 { 01946 WCHAR *buf, *opt = NULL, *iter = NULL; 01947 BOOL ret = FALSE; 01948 int bufSz = 200; /* the size of the buffer */ 01949 01950 if (calinfoproc == NULL) 01951 { 01952 SetLastError(ERROR_INVALID_PARAMETER); 01953 return FALSE; 01954 } 01955 01956 buf = HeapAlloc(GetProcessHeap(), 0, bufSz); 01957 if (buf == NULL) 01958 { 01959 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 01960 return FALSE; 01961 } 01962 01963 if (calendar == ENUM_ALL_CALENDARS) 01964 { 01965 int optSz = GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, NULL, 0); 01966 if (optSz > 1) 01967 { 01968 opt = HeapAlloc(GetProcessHeap(), 0, optSz * sizeof(WCHAR)); 01969 if (opt == NULL) 01970 { 01971 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 01972 goto cleanup; 01973 } 01974 if (GetLocaleInfoW(locale, LOCALE_IOPTIONALCALENDAR, opt, optSz)) 01975 iter = opt; 01976 } 01977 calendar = NLS_GetLocaleNumber(locale, LOCALE_ICALENDARTYPE); 01978 } 01979 01980 while (TRUE) /* loop through calendars */ 01981 { 01982 do /* loop until there's no error */ 01983 { 01984 if (unicode) 01985 ret = GetCalendarInfoW(locale, calendar, caltype, buf, bufSz / sizeof(WCHAR), NULL); 01986 else ret = GetCalendarInfoA(locale, calendar, caltype, (CHAR*)buf, bufSz / sizeof(CHAR), NULL); 01987 01988 if (!ret) 01989 { 01990 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) 01991 { /* so resize it */ 01992 int newSz; 01993 if (unicode) 01994 newSz = GetCalendarInfoW(locale, calendar, caltype, NULL, 0, NULL) * sizeof(WCHAR); 01995 else newSz = GetCalendarInfoA(locale, calendar, caltype, NULL, 0, NULL) * sizeof(CHAR); 01996 if (bufSz >= newSz) 01997 { 01998 ERR("Buffer resizing disorder: was %d, requested %d.\n", bufSz, newSz); 01999 goto cleanup; 02000 } 02001 bufSz = newSz; 02002 WARN("Buffer too small; resizing to %d bytes.\n", bufSz); 02003 buf = HeapReAlloc(GetProcessHeap(), 0, buf, bufSz); 02004 if (buf == NULL) 02005 goto cleanup; 02006 } else goto cleanup; 02007 } 02008 } while (!ret); 02009 02010 /* Here we are. We pass the buffer to the correct version of 02011 * the callback. Because it's not the same number of params, 02012 * we must check for Ex, but we don't care about Unicode 02013 * because the buffer is already in the correct format. 02014 */ 02015 if (ex) { 02016 ret = ((CALINFO_ENUMPROCEXW)calinfoproc)(buf, calendar); 02017 } else 02018 ret = ((CALINFO_ENUMPROCW)calinfoproc)(buf); 02019 02020 if (!ret) { /* the callback told to stop */ 02021 ret = TRUE; 02022 break; 02023 } 02024 02025 if ((iter == NULL) || (*iter == 0)) /* no more calendars */ 02026 break; 02027 02028 calendar = 0; 02029 while ((*iter >= '0') && (*iter <= '9')) 02030 calendar = calendar * 10 + *iter++ - '0'; 02031 02032 if (*iter++ != 0) 02033 { 02034 SetLastError(ERROR_BADDB); 02035 ret = FALSE; 02036 break; 02037 } 02038 } 02039 02040 cleanup: 02041 HeapFree(GetProcessHeap(), 0, opt); 02042 HeapFree(GetProcessHeap(), 0, buf); 02043 return ret; 02044 } 02045 02046 /****************************************************************************** 02047 * EnumCalendarInfoA [KERNEL32.@] 02048 * 02049 * See EnumCalendarInfoAW. 02050 */ 02051 BOOL WINAPI EnumCalendarInfoA( CALINFO_ENUMPROCA calinfoproc,LCID locale, 02052 CALID calendar,CALTYPE caltype ) 02053 { 02054 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 02055 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, FALSE); 02056 } 02057 02058 /****************************************************************************** 02059 * EnumCalendarInfoW [KERNEL32.@] 02060 * 02061 * See EnumCalendarInfoAW. 02062 */ 02063 BOOL WINAPI EnumCalendarInfoW( CALINFO_ENUMPROCW calinfoproc,LCID locale, 02064 CALID calendar,CALTYPE caltype ) 02065 { 02066 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 02067 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, FALSE); 02068 } 02069 02070 /****************************************************************************** 02071 * EnumCalendarInfoExA [KERNEL32.@] 02072 * 02073 * See EnumCalendarInfoAW. 02074 */ 02075 BOOL WINAPI EnumCalendarInfoExA( CALINFO_ENUMPROCEXA calinfoproc,LCID locale, 02076 CALID calendar,CALTYPE caltype ) 02077 { 02078 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 02079 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, FALSE, TRUE); 02080 } 02081 02082 /****************************************************************************** 02083 * EnumCalendarInfoExW [KERNEL32.@] 02084 * 02085 * See EnumCalendarInfoAW. 02086 */ 02087 BOOL WINAPI EnumCalendarInfoExW( CALINFO_ENUMPROCEXW calinfoproc,LCID locale, 02088 CALID calendar,CALTYPE caltype ) 02089 { 02090 TRACE("(%p,0x%08x,0x%08x,0x%08x)\n", calinfoproc, locale, calendar, caltype); 02091 return NLS_EnumCalendarInfoAW(calinfoproc, locale, calendar, caltype, TRUE, TRUE); 02092 } 02093 02094 /********************************************************************* 02095 * GetCalendarInfoA (KERNEL32.@) 02096 * 02097 */ 02098 int WINAPI GetCalendarInfoA(LCID lcid, CALID Calendar, CALTYPE CalType, 02099 LPSTR lpCalData, int cchData, LPDWORD lpValue) 02100 { 02101 int ret; 02102 LPWSTR lpCalDataW = NULL; 02103 02104 if (NLS_IsUnicodeOnlyLcid(lcid)) 02105 { 02106 SetLastError(ERROR_INVALID_PARAMETER); 02107 return 0; 02108 } 02109 02110 if (cchData && 02111 !(lpCalDataW = HeapAlloc(GetProcessHeap(), 0, cchData*sizeof(WCHAR)))) 02112 return 0; 02113 02114 ret = GetCalendarInfoW(lcid, Calendar, CalType, lpCalDataW, cchData, lpValue); 02115 if(ret && lpCalDataW && lpCalData) 02116 WideCharToMultiByte(CP_ACP, 0, lpCalDataW, cchData, lpCalData, cchData, NULL, NULL); 02117 else if (CalType & CAL_RETURN_NUMBER) 02118 ret *= sizeof(WCHAR); 02119 HeapFree(GetProcessHeap(), 0, lpCalDataW); 02120 02121 return ret; 02122 } 02123 02124 /********************************************************************* 02125 * GetCalendarInfoW (KERNEL32.@) 02126 * 02127 */ 02128 int WINAPI GetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, 02129 LPWSTR lpCalData, int cchData, LPDWORD lpValue) 02130 { 02131 if (CalType & CAL_NOUSEROVERRIDE) 02132 FIXME("flag CAL_NOUSEROVERRIDE used, not fully implemented\n"); 02133 if (CalType & CAL_USE_CP_ACP) 02134 FIXME("flag CAL_USE_CP_ACP used, not fully implemented\n"); 02135 02136 if (CalType & CAL_RETURN_NUMBER) { 02137 if (!lpValue) 02138 { 02139 SetLastError( ERROR_INVALID_PARAMETER ); 02140 return 0; 02141 } 02142 if (lpCalData != NULL) 02143 WARN("lpCalData not NULL (%p) when it should!\n", lpCalData); 02144 if (cchData != 0) 02145 WARN("cchData not 0 (%d) when it should!\n", cchData); 02146 } else { 02147 if (lpValue != NULL) 02148 WARN("lpValue not NULL (%p) when it should!\n", lpValue); 02149 } 02150 02151 /* FIXME: No verification is made yet wrt Locale 02152 * for the CALTYPES not requiring GetLocaleInfoA */ 02153 switch (CalType & ~(CAL_NOUSEROVERRIDE|CAL_RETURN_NUMBER|CAL_USE_CP_ACP)) { 02154 case CAL_ICALINTVALUE: 02155 FIXME("Unimplemented caltype %d\n", CalType & 0xffff); 02156 return 0; 02157 case CAL_SCALNAME: 02158 FIXME("Unimplemented caltype %d\n", CalType & 0xffff); 02159 return 0; 02160 case CAL_IYEAROFFSETRANGE: 02161 FIXME("Unimplemented caltype %d\n", CalType & 0xffff); 02162 return 0; 02163 case CAL_SERASTRING: 02164 FIXME("Unimplemented caltype %d\n", CalType & 0xffff); 02165 return 0; 02166 case CAL_SSHORTDATE: 02167 return GetLocaleInfoW(Locale, LOCALE_SSHORTDATE, lpCalData, cchData); 02168 case CAL_SLONGDATE: 02169 return GetLocaleInfoW(Locale, LOCALE_SLONGDATE, lpCalData, cchData); 02170 case CAL_SDAYNAME1: 02171 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME1, lpCalData, cchData); 02172 case CAL_SDAYNAME2: 02173 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME2, lpCalData, cchData); 02174 case CAL_SDAYNAME3: 02175 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME3, lpCalData, cchData); 02176 case CAL_SDAYNAME4: 02177 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME4, lpCalData, cchData); 02178 case CAL_SDAYNAME5: 02179 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME5, lpCalData, cchData); 02180 case CAL_SDAYNAME6: 02181 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME6, lpCalData, cchData); 02182 case CAL_SDAYNAME7: 02183 return GetLocaleInfoW(Locale, LOCALE_SDAYNAME7, lpCalData, cchData); 02184 case CAL_SABBREVDAYNAME1: 02185 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME1, lpCalData, cchData); 02186 case CAL_SABBREVDAYNAME2: 02187 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME2, lpCalData, cchData); 02188 case CAL_SABBREVDAYNAME3: 02189 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME3, lpCalData, cchData); 02190 case CAL_SABBREVDAYNAME4: 02191 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME4, lpCalData, cchData); 02192 case CAL_SABBREVDAYNAME5: 02193 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME5, lpCalData, cchData); 02194 case CAL_SABBREVDAYNAME6: 02195 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME6, lpCalData, cchData); 02196 case CAL_SABBREVDAYNAME7: 02197 return GetLocaleInfoW(Locale, LOCALE_SABBREVDAYNAME7, lpCalData, cchData); 02198 case CAL_SMONTHNAME1: 02199 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME1, lpCalData, cchData); 02200 case CAL_SMONTHNAME2: 02201 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME2, lpCalData, cchData); 02202 case CAL_SMONTHNAME3: 02203 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME3, lpCalData, cchData); 02204 case CAL_SMONTHNAME4: 02205 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME4, lpCalData, cchData); 02206 case CAL_SMONTHNAME5: 02207 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME5, lpCalData, cchData); 02208 case CAL_SMONTHNAME6: 02209 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME6, lpCalData, cchData); 02210 case CAL_SMONTHNAME7: 02211 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME7, lpCalData, cchData); 02212 case CAL_SMONTHNAME8: 02213 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME8, lpCalData, cchData); 02214 case CAL_SMONTHNAME9: 02215 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME9, lpCalData, cchData); 02216 case CAL_SMONTHNAME10: 02217 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME10, lpCalData, cchData); 02218 case CAL_SMONTHNAME11: 02219 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME11, lpCalData, cchData); 02220 case CAL_SMONTHNAME12: 02221 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME12, lpCalData, cchData); 02222 case CAL_SMONTHNAME13: 02223 return GetLocaleInfoW(Locale, LOCALE_SMONTHNAME13, lpCalData, cchData); 02224 case CAL_SABBREVMONTHNAME1: 02225 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME1, lpCalData, cchData); 02226 case CAL_SABBREVMONTHNAME2: 02227 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME2, lpCalData, cchData); 02228 case CAL_SABBREVMONTHNAME3: 02229 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME3, lpCalData, cchData); 02230 case CAL_SABBREVMONTHNAME4: 02231 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME4, lpCalData, cchData); 02232 case CAL_SABBREVMONTHNAME5: 02233 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME5, lpCalData, cchData); 02234 case CAL_SABBREVMONTHNAME6: 02235 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME6, lpCalData, cchData); 02236 case CAL_SABBREVMONTHNAME7: 02237 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME7, lpCalData, cchData); 02238 case CAL_SABBREVMONTHNAME8: 02239 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME8, lpCalData, cchData); 02240 case CAL_SABBREVMONTHNAME9: 02241 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME9, lpCalData, cchData); 02242 case CAL_SABBREVMONTHNAME10: 02243 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME10, lpCalData, cchData); 02244 case CAL_SABBREVMONTHNAME11: 02245 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME11, lpCalData, cchData); 02246 case CAL_SABBREVMONTHNAME12: 02247 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME12, lpCalData, cchData); 02248 case CAL_SABBREVMONTHNAME13: 02249 return GetLocaleInfoW(Locale, LOCALE_SABBREVMONTHNAME13, lpCalData, cchData); 02250 case CAL_SYEARMONTH: 02251 return GetLocaleInfoW(Locale, LOCALE_SYEARMONTH, lpCalData, cchData); 02252 case CAL_ITWODIGITYEARMAX: 02253 if (CalType & CAL_RETURN_NUMBER) 02254 { 02255 *lpValue = CALINFO_MAX_YEAR; 02256 return sizeof(DWORD) / sizeof(WCHAR); 02257 } 02258 else 02259 { 02260 static const WCHAR fmtW[] = {'%','u',0}; 02261 WCHAR buffer[10]; 02262 int ret = snprintfW( buffer, 10, fmtW, CALINFO_MAX_YEAR ) + 1; 02263 if (!lpCalData) return ret; 02264 if (ret <= cchData) 02265 { 02266 strcpyW( lpCalData, buffer ); 02267 return ret; 02268 } 02269 SetLastError( ERROR_INSUFFICIENT_BUFFER ); 02270 return 0; 02271 } 02272 break; 02273 default: 02274 FIXME("Unknown caltype %d\n",CalType & 0xffff); 02275 SetLastError(ERROR_INVALID_FLAGS); 02276 return 0; 02277 } 02278 return 0; 02279 } 02280 02281 /********************************************************************* 02282 * SetCalendarInfoA (KERNEL32.@) 02283 * 02284 */ 02285 int WINAPI SetCalendarInfoA(LCID Locale, CALID Calendar, CALTYPE CalType, LPCSTR lpCalData) 02286 { 02287 FIXME("(%08x,%08x,%08x,%s): stub\n", 02288 Locale, Calendar, CalType, debugstr_a(lpCalData)); 02289 return 0; 02290 } 02291 02292 /********************************************************************* 02293 * SetCalendarInfoW (KERNEL32.@) 02294 * 02295 * 02296 */ 02297 int WINAPI SetCalendarInfoW(LCID Locale, CALID Calendar, CALTYPE CalType, LPCWSTR lpCalData) 02298 { 02299 FIXME("(%08x,%08x,%08x,%s): stub\n", 02300 Locale, Calendar, CalType, debugstr_w(lpCalData)); 02301 return 0; 02302 } Generated on Sat May 26 2012 04:23:08 for ReactOS by
1.7.6.1
|