📄 lcformat.c
字号:
/*
* Locale-dependent format handling
*
* Copyright 1995 Martin von Loewis
* Copyright 1998 David Lee Lambert
* Copyright 2000 Julio César Gázquez
* Copyright 2003 Jon Griffiths
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Whole file ripped from Wine's dlls\kernel\lcformat.c, rev 1.7 and is
* unchanged except that includes are different. I thought about adding
* @implemeted to each exported function, but this might make merging harder?
* -Gunnar
*/
#include <k32.h>
#include "wine/config.h"
#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(nls);
#define DATE_DATEVARSONLY 0x0100 /* only date stuff: yMdg */
#define TIME_TIMEVARSONLY 0x0200 /* only time stuff: hHmst */
/* Since calculating the formatting data for each locale is time-consuming,
* we get the format data for each locale only once and cache it in memory.
* We cache both the system default and user overridden data, after converting
* them into the formats that the functions here expect. Since these functions
* will typically be called with only a small number of the total locales
* installed, the memory overhead is minimal while the speedup is significant.
*
* Our cache takes the form of a singly linked list, whose node is below:
*/
#define NLS_NUM_CACHED_STRINGS 45
typedef struct _NLS_FORMAT_NODE
{
LCID lcid; /* Locale Id */
DWORD dwFlags; /* 0 or LOCALE_NOUSEROVERRIDE */
DWORD dwCodePage; /* Default code page (if LOCALE_USE_ANSI_CP not given) */
NUMBERFMTW fmt; /* Default format for numbers */
CURRENCYFMTW cyfmt; /* Default format for currencies */
LPWSTR lppszStrings[NLS_NUM_CACHED_STRINGS]; /* Default formats,day/month names */
WCHAR szShortAM[2]; /* Short 'AM' marker */
WCHAR szShortPM[2]; /* Short 'PM' marker */
struct _NLS_FORMAT_NODE *next;
} NLS_FORMAT_NODE;
/* Macros to get particular data strings from a format node */
#define GetNegative(fmt) fmt->lppszStrings[0]
#define GetLongDate(fmt) fmt->lppszStrings[1]
#define GetShortDate(fmt) fmt->lppszStrings[2]
#define GetTime(fmt) fmt->lppszStrings[3]
#define GetAM(fmt) fmt->lppszStrings[42]
#define GetPM(fmt) fmt->lppszStrings[43]
#define GetYearMonth(fmt) fmt->lppszStrings[44]
#define GetLongDay(fmt,day) fmt->lppszStrings[4 + day]
#define GetShortDay(fmt,day) fmt->lppszStrings[11 + day]
#define GetLongMonth(fmt,mth) fmt->lppszStrings[18 + mth]
#define GetShortMonth(fmt,mth) fmt->lppszStrings[30 + mth]
/* Write access to the cache is protected by this critical section */
static RTL_CRITICAL_SECTION NLS_FormatsCS;
static RTL_CRITICAL_SECTION_DEBUG NLS_FormatsCS_debug =
{
0, 0, &NLS_FormatsCS,
{ &NLS_FormatsCS_debug.ProcessLocksList,
&NLS_FormatsCS_debug.ProcessLocksList },
0, 0, { 0, (DWORD)(__FILE__ ": NLS_Formats") }
};
static RTL_CRITICAL_SECTION NLS_FormatsCS = { &NLS_FormatsCS_debug, -1, 0, 0, 0, 0 };
/**************************************************************************
* NLS_GetLocaleNumber <internal>
*
* Get a numeric locale format value.
*/
static DWORD NLS_GetLocaleNumber(LCID lcid, DWORD dwFlags)
{
WCHAR szBuff[80];
DWORD dwVal = 0;
szBuff[0] = '\0';
GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
if (szBuff[0] && szBuff[1] == ';' && szBuff[2] != '0')
dwVal = (szBuff[0] - '0') * 10 + (szBuff[2] - '0');
else
{
const WCHAR* iter = szBuff;
dwVal = 0;
while(*iter >= '0' && *iter <= '9')
dwVal = dwVal * 10 + (*iter++ - '0');
}
return dwVal;
}
/**************************************************************************
* NLS_GetLocaleString <internal>
*
* Get a string locale format value.
*/
static WCHAR* NLS_GetLocaleString(LCID lcid, DWORD dwFlags)
{
WCHAR szBuff[80], *str;
DWORD dwLen;
szBuff[0] = '\0';
GetLocaleInfoW(lcid, dwFlags, szBuff, sizeof(szBuff) / sizeof(WCHAR));
dwLen = strlenW(szBuff) + 1;
str = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
if (str)
memcpy(str, szBuff, dwLen * sizeof(WCHAR));
return str;
}
#define GET_LOCALE_NUMBER(num, type) num = NLS_GetLocaleNumber(lcid, type|dwFlags); \
TRACE( #type ": %ld (%08lx)\n", (DWORD)num, (DWORD)num)
#define GET_LOCALE_STRING(str, type) str = NLS_GetLocaleString(lcid, type|dwFlags); \
TRACE( #type ": '%S'\n", (str))
/**************************************************************************
* NLS_GetFormats <internal>
*
* Calculate (and cache) the number formats for a locale.
*/
static const NLS_FORMAT_NODE *NLS_GetFormats(LCID lcid, DWORD dwFlags)
{
/* GetLocaleInfo() identifiers for cached formatting strings */
static const USHORT NLS_LocaleIndices[] = {
LOCALE_SNEGATIVESIGN,
LOCALE_SLONGDATE, LOCALE_SSHORTDATE,
LOCALE_STIMEFORMAT,
LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
LOCALE_SABBREVDAYNAME7,
LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12,
LOCALE_SABBREVMONTHNAME1, LOCALE_SABBREVMONTHNAME2, LOCALE_SABBREVMONTHNAME3,
LOCALE_SABBREVMONTHNAME4, LOCALE_SABBREVMONTHNAME5, LOCALE_SABBREVMONTHNAME6,
LOCALE_SABBREVMONTHNAME7, LOCALE_SABBREVMONTHNAME8, LOCALE_SABBREVMONTHNAME9,
LOCALE_SABBREVMONTHNAME10, LOCALE_SABBREVMONTHNAME11, LOCALE_SABBREVMONTHNAME12,
LOCALE_S1159, LOCALE_S2359,
LOCALE_SYEARMONTH
};
static NLS_FORMAT_NODE *NLS_CachedFormats = NULL;
NLS_FORMAT_NODE *node = NLS_CachedFormats;
dwFlags &= LOCALE_NOUSEROVERRIDE;
TRACE("(0x%04lx,0x%08lx)\n", lcid, dwFlags);
/* See if we have already cached the locales number format */
while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
node = node->next;
if (!node || node->lcid != lcid || node->dwFlags != dwFlags)
{
NLS_FORMAT_NODE *new_node;
DWORD i;
TRACE("Creating new cache entry\n");
if (!(new_node = HeapAlloc(GetProcessHeap(), 0, sizeof(NLS_FORMAT_NODE))))
return NULL;
GET_LOCALE_NUMBER(new_node->dwCodePage, LOCALE_IDEFAULTANSICODEPAGE);
/* Number Format */
new_node->lcid = lcid;
new_node->dwFlags = dwFlags;
new_node->next = NULL;
GET_LOCALE_NUMBER(new_node->fmt.NumDigits, LOCALE_IDIGITS);
GET_LOCALE_NUMBER(new_node->fmt.LeadingZero, LOCALE_ILZERO);
GET_LOCALE_NUMBER(new_node->fmt.NegativeOrder, LOCALE_INEGNUMBER);
GET_LOCALE_NUMBER(new_node->fmt.Grouping, LOCALE_SGROUPING);
if (new_node->fmt.Grouping > 9 && new_node->fmt.Grouping != 32)
{
WARN("LOCALE_SGROUPING (%d) unhandled, please report!\n",
new_node->fmt.Grouping);
new_node->fmt.Grouping = 0;
}
GET_LOCALE_STRING(new_node->fmt.lpDecimalSep, LOCALE_SDECIMAL);
GET_LOCALE_STRING(new_node->fmt.lpThousandSep, LOCALE_STHOUSAND);
/* Currency Format */
new_node->cyfmt.NumDigits = new_node->fmt.NumDigits;
new_node->cyfmt.LeadingZero = new_node->fmt.LeadingZero;
GET_LOCALE_NUMBER(new_node->cyfmt.Grouping, LOCALE_SGROUPING);
if (new_node->cyfmt.Grouping > 9)
{
WARN("LOCALE_SMONGROUPING (%d) unhandled, please report!\n",
new_node->cyfmt.Grouping);
new_node->cyfmt.Grouping = 0;
}
GET_LOCALE_NUMBER(new_node->cyfmt.NegativeOrder, LOCALE_INEGCURR);
if (new_node->cyfmt.NegativeOrder > 15)
{
WARN("LOCALE_INEGCURR (%d) unhandled, please report!\n",
new_node->cyfmt.NegativeOrder);
new_node->cyfmt.NegativeOrder = 0;
}
GET_LOCALE_NUMBER(new_node->cyfmt.PositiveOrder, LOCALE_ICURRENCY);
if (new_node->cyfmt.PositiveOrder > 3)
{
WARN("LOCALE_IPOSCURR (%d) unhandled,please report!\n",
new_node->cyfmt.PositiveOrder);
new_node->cyfmt.PositiveOrder = 0;
}
GET_LOCALE_STRING(new_node->cyfmt.lpDecimalSep, LOCALE_SMONDECIMALSEP);
GET_LOCALE_STRING(new_node->cyfmt.lpThousandSep, LOCALE_SMONTHOUSANDSEP);
GET_LOCALE_STRING(new_node->cyfmt.lpCurrencySymbol, LOCALE_SCURRENCY);
/* Date/Time Format info, negative character, etc */
for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
{
GET_LOCALE_STRING(new_node->lppszStrings[i], NLS_LocaleIndices[i]);
}
new_node->szShortAM[0] = GetAM(new_node)[0]; new_node->szShortAM[1] = '\0';
new_node->szShortPM[0] = GetPM(new_node)[0]; new_node->szShortPM[1] = '\0';
/* Now add the computed format to the cache */
RtlEnterCriticalSection(&NLS_FormatsCS);
/* Search again: We may have raced to add the node */
node = NLS_CachedFormats;
while (node && (node->lcid != lcid || node->dwFlags != dwFlags) && node->next)
node = node->next;
if (!node)
{
node = NLS_CachedFormats = new_node; /* Empty list */
new_node = NULL;
}
else if (node->lcid != lcid || node->dwFlags != dwFlags)
{
node->next = new_node; /* Not in the list, add to end */
node = new_node;
new_node = NULL;
}
RtlLeaveCriticalSection(&NLS_FormatsCS);
if (new_node)
{
/* We raced and lost: The node was already added by another thread.
* node points to the currently cached node, so free new_node.
*/
for (i = 0; i < sizeof(NLS_LocaleIndices)/sizeof(NLS_LocaleIndices[0]); i++)
HeapFree(GetProcessHeap(), 0, new_node->lppszStrings[i]);
HeapFree(GetProcessHeap(), 0, new_node->fmt.lpDecimalSep);
HeapFree(GetProcessHeap(), 0, new_node->fmt.lpThousandSep);
HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpDecimalSep);
HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpThousandSep);
HeapFree(GetProcessHeap(), 0, new_node->cyfmt.lpCurrencySymbol);
HeapFree(GetProcessHeap(), 0, new_node);
}
}
return node;
}
/**************************************************************************
* NLS_IsUnicodeOnlyLcid <internal>
*
* Determine if a locale is Unicode only, and thus invalid in ASCII calls.
*/
BOOL NLS_IsUnicodeOnlyLcid(LCID lcid)
{
switch (PRIMARYLANGID(lcid))
{
case LANG_ARMENIAN:
case LANG_DIVEHI:
case LANG_GEORGIAN:
case LANG_GUJARATI:
case LANG_HINDI:
case LANG_KANNADA:
case LANG_KONKANI:
case LANG_MARATHI:
case LANG_PUNJABI:
case LANG_SANSKRIT:
TRACE("lcid 0x%08lx: langid 0x%4x is Unicode Only\n", lcid, PRIMARYLANGID(lcid));
return TRUE;
default:
return FALSE;
}
}
/*
* Formatting of dates, times, numbers and currencies.
*/
#define IsLiteralMarker(p) (p == '\'')
#define IsDateFmtChar(p) (p == 'd'||p == 'M'||p == 'y'||p == 'g')
#define IsTimeFmtChar(p) (p == 'H'||p == 'h'||p == 'm'||p == 's'||p == 't')
/* Only the following flags can be given if a date/time format is specified */
#define DATE_FORMAT_FLAGS (DATE_DATEVARSONLY|LOCALE_NOUSEROVERRIDE)
#define TIME_FORMAT_FLAGS (TIME_TIMEVARSONLY|TIME_FORCE24HOURFORMAT| \
TIME_NOMINUTESORSECONDS|TIME_NOSECONDS| \
TIME_NOTIMEMARKER|LOCALE_NOUSEROVERRIDE)
/******************************************************************************
* NLS_GetDateTimeFormatW <internal>
*
* Performs the formatting for GetDateFormatW/GetTimeFormatW.
*
* FIXME
* DATE_USE_ALT_CALENDAR - Requires GetCalendarInfo to work first.
* DATE_LTRREADING/DATE_RTLREADING - Not yet implemented.
*/
static INT NLS_GetDateTimeFormatW(LCID lcid, DWORD dwFlags,
const SYSTEMTIME* lpTime, LPCWSTR lpFormat,
LPWSTR lpStr, INT cchOut)
{
const NLS_FORMAT_NODE *node;
SYSTEMTIME st;
INT cchWritten = 0;
INT lastFormatPos = 0;
BOOL bSkipping = FALSE; /* Skipping text around marker? */
/* Verify our arguments */
if ((cchOut && !lpStr) || !(node = NLS_GetFormats(lcid, dwFlags)))
{
NLS_GetDateTimeFormatW_InvalidParameter:
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
if (dwFlags & ~(DATE_DATEVARSONLY|TIME_TIMEVARSONLY))
{
if (lpFormat &&
((dwFlags & DATE_DATEVARSONLY && dwFlags & ~DATE_FORMAT_FLAGS) ||
(dwFlags & TIME_TIMEVARSONLY && dwFlags & ~TIME_FORMAT_FLAGS)))
{
NLS_GetDateTimeFormatW_InvalidFlags:
SetLastError(ERROR_INVALID_FLAGS);
return 0;
}
if (dwFlags & DATE_DATEVARSONLY)
{
if ((dwFlags & (DATE_LTRREADING|DATE_RTLREADING)) == (DATE_LTRREADING|DATE_RTLREADING))
goto NLS_GetDateTimeFormatW_InvalidFlags;
else if (dwFlags & (DATE_LTRREADING|DATE_RTLREADING))
FIXME("Unsupported flags: DATE_LTRREADING/DATE_RTLREADING\n");
switch (dwFlags & (DATE_SHORTDATE|DATE_LONGDATE|DATE_YEARMONTH))
{
case 0:
break;
case DATE_SHORTDATE:
case DATE_LONGDATE:
case DATE_YEARMONTH:
if (lpFormat)
goto NLS_GetDateTimeFormatW_InvalidFlags;
break;
default:
goto NLS_GetDateTimeFormatW_InvalidFlags;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -