📄 varformat.c
字号:
FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
FMT_GEN_INLINE,0x1,'%','\0',FMT_GEN_END
};
static const WCHAR szScientific[] = { 'S','c','i','e','n','t','i','f','i','c','\0' };
static const BYTE fmtScientific[0x13] =
{
0x13,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_EXPONENT,0x0,0x0,0x1,0x2,
FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_NUM_EXP_POS_U,0x2,FMT_GEN_END
};
typedef struct tagNAMED_FORMAT
{
LPCWSTR name;
const BYTE* format;
} NAMED_FORMAT;
/* Format name to tokenised format. Must be kept sorted by name */
static const NAMED_FORMAT VARIANT_NamedFormats[] =
{
{ szCurrency, fmtCurrency },
{ szFixed, fmtFixed },
{ szGeneralDate, fmtGeneralDate },
{ szGeneralNumber, fmtGeneralNumber },
{ szLongDate, fmtLongDate },
{ szLongTime, fmtLongTime },
{ szMediumDate, fmtMediumDate },
{ szMediumTime, fmtMediumTime },
{ szOnOff, fmtOnOff },
{ szPercent, fmtPercent },
{ szScientific, fmtScientific },
{ szShortDate, fmtShortDate },
{ szShortTime, fmtShortTime },
{ szStandard, fmtStandard },
{ szTrueFalse, fmtTrueFalse },
{ szYesNo, fmtYesNo }
};
typedef const NAMED_FORMAT *LPCNAMED_FORMAT;
static int FormatCompareFn(const void *l, const void *r)
{
return strcmpiW(((LPCNAMED_FORMAT)l)->name, ((LPCNAMED_FORMAT)r)->name);
}
static inline const BYTE *VARIANT_GetNamedFormat(LPCWSTR lpszFormat)
{
NAMED_FORMAT key;
LPCNAMED_FORMAT fmt;
key.name = lpszFormat;
fmt = (LPCNAMED_FORMAT)bsearch(&key, VARIANT_NamedFormats,
sizeof(VARIANT_NamedFormats)/sizeof(NAMED_FORMAT),
sizeof(NAMED_FORMAT), FormatCompareFn);
return fmt ? fmt->format : NULL;
}
/* Return an error if the token for the value will not fit in the destination */
#define NEED_SPACE(x) if (cbTok < (int)(x)) return TYPE_E_BUFFERTOOSMALL; cbTok -= (x)
/* Non-zero if the format is unknown or a given type */
#define COULD_BE(typ) ((!fmt_number && header->type==FMT_TYPE_UNKNOWN)||header->type==typ)
/* State during tokenising */
#define FMT_STATE_OPEN_COPY 0x1 /* Last token written was a copy */
#define FMT_STATE_WROTE_DECIMAL 0x2 /* Already wrote a decimal separator */
#define FMT_STATE_SEEN_HOURS 0x4 /* See the hh specifier */
#define FMT_STATE_WROTE_MINUTES 0x8 /* Wrote minutes */
/**********************************************************************
* VarTokenizeFormatString [OLEAUT32.140]
*
* Convert a format string into tokenised form.
*
* PARAMS
* lpszFormat [I] Format string to tokenise
* rgbTok [O] Destination for tokenised format
* cbTok [I] Size of rgbTok in bytes
* nFirstDay [I] First day of the week (1-7, or 0 for current system default)
* nFirstWeek [I] How to treat the first week (see notes)
* lcid [I] Locale Id of the format string
* pcbActual [O] If non-NULL, filled with the first token generated
*
* RETURNS
* Success: S_OK. rgbTok contains the tokenised format.
* Failure: E_INVALIDARG, if any argument is invalid.
* TYPE_E_BUFFERTOOSMALL, if rgbTok is not large enough.
*
* NOTES
* Valid values for the nFirstWeek parameter are:
*| Value Meaning
*| ----- -------
*| 0 Use the current system default
*| 1 The first week is that containing Jan 1
*| 2 Four or more days of the first week are in the current year
*| 3 The first week is 7 days long
* See Variant-Formats(), VarFormatFromTokens().
*/
HRESULT WINAPI VarTokenizeFormatString(LPOLESTR lpszFormat, LPBYTE rgbTok,
int cbTok, int nFirstDay, int nFirstWeek,
LCID lcid, int *pcbActual)
{
/* Note: none of these strings should be NUL terminated */
static const WCHAR szTTTTT[] = { 't','t','t','t','t' };
static const WCHAR szAMPM[] = { 'A','M','P','M' };
static const WCHAR szampm[] = { 'a','m','p','m' };
static const WCHAR szAMSlashPM[] = { 'A','M','/','P','M' };
static const WCHAR szamSlashpm[] = { 'a','m','/','p','m' };
const BYTE *namedFmt;
FMT_HEADER *header = (FMT_HEADER*)rgbTok;
FMT_STRING_HEADER *str_header = (FMT_STRING_HEADER*)(rgbTok + sizeof(FMT_HEADER));
FMT_NUMBER_HEADER *num_header = (FMT_NUMBER_HEADER*)str_header;
FMT_DATE_HEADER *date_header = (FMT_DATE_HEADER*)str_header;
BYTE* pOut = rgbTok + sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER);
BYTE* pLastHours = NULL;
BYTE fmt_number = 0;
DWORD fmt_state = 0;
LPCWSTR pFormat = lpszFormat;
TRACE("(%s,%p,%d,%d,%d,0x%08x,%p)\n", debugstr_w(lpszFormat), rgbTok, cbTok,
nFirstDay, nFirstWeek, lcid, pcbActual);
if (!rgbTok ||
nFirstDay < 0 || nFirstDay > 7 || nFirstWeek < 0 || nFirstWeek > 3)
return E_INVALIDARG;
if (!lpszFormat || !*lpszFormat)
{
/* An empty string means 'general format' */
NEED_SPACE(sizeof(BYTE));
*rgbTok = FMT_TO_STRING;
if (pcbActual)
*pcbActual = FMT_TO_STRING;
return S_OK;
}
if (cbTok > 255)
cbTok = 255; /* Ensure we error instead of wrapping */
/* Named formats */
namedFmt = VARIANT_GetNamedFormat(lpszFormat);
if (namedFmt)
{
NEED_SPACE(namedFmt[0]);
memcpy(rgbTok, namedFmt, namedFmt[0]);
TRACE("Using pre-tokenised named format %s\n", debugstr_w(lpszFormat));
/* FIXME: pcbActual */
return S_OK;
}
/* Insert header */
NEED_SPACE(sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER));
memset(header, 0, sizeof(FMT_HEADER));
memset(str_header, 0, sizeof(FMT_STRING_HEADER));
header->starts[fmt_number] = sizeof(FMT_HEADER);
while (*pFormat)
{
/* --------------
* General tokens
* --------------
*/
if (*pFormat == ';')
{
while (*pFormat == ';')
{
TRACE(";\n");
if (++fmt_number > 3)
return E_INVALIDARG; /* too many formats */
pFormat++;
}
if (*pFormat)
{
TRACE("New header\n");
NEED_SPACE(sizeof(BYTE) + sizeof(FMT_STRING_HEADER));
*pOut++ = FMT_GEN_END;
header->starts[fmt_number] = pOut - rgbTok;
str_header = (FMT_STRING_HEADER*)pOut;
num_header = (FMT_NUMBER_HEADER*)pOut;
date_header = (FMT_DATE_HEADER*)pOut;
memset(str_header, 0, sizeof(FMT_STRING_HEADER));
pOut += sizeof(FMT_STRING_HEADER);
fmt_state = 0;
pLastHours = NULL;
}
}
else if (*pFormat == '\\')
{
/* Escaped character */
if (pFormat[1])
{
NEED_SPACE(3 * sizeof(BYTE));
pFormat++;
*pOut++ = FMT_GEN_COPY;
*pOut++ = pFormat - lpszFormat;
*pOut++ = 0x1;
fmt_state |= FMT_STATE_OPEN_COPY;
TRACE("'\\'\n");
}
else
fmt_state &= ~FMT_STATE_OPEN_COPY;
pFormat++;
}
else if (*pFormat == '"')
{
/* Escaped string
* Note: Native encodes "" as a copy of length zero. That's just dumb, so
* here we avoid encoding anything in this case.
*/
if (!pFormat[1])
pFormat++;
else if (pFormat[1] == '"')
{
pFormat += 2;
}
else
{
LPCWSTR start = ++pFormat;
while (*pFormat && *pFormat != '"')
pFormat++;
NEED_SPACE(3 * sizeof(BYTE));
*pOut++ = FMT_GEN_COPY;
*pOut++ = start - lpszFormat;
*pOut++ = pFormat - start;
if (*pFormat == '"')
pFormat++;
TRACE("Quoted string pos %d, len %d\n", pOut[-2], pOut[-1]);
}
fmt_state &= ~FMT_STATE_OPEN_COPY;
}
/* -------------
* Number tokens
* -------------
*/
else if (*pFormat == '0' && COULD_BE(FMT_TYPE_NUMBER))
{
/* Number formats: Digit from number or '0' if no digits
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_NUMBER;
NEED_SPACE(2 * sizeof(BYTE));
*pOut++ = FMT_NUM_COPY_ZERO;
*pOut = 0x0;
while (*pFormat == '0')
{
*pOut = *pOut + 1;
pFormat++;
}
if (fmt_state & FMT_STATE_WROTE_DECIMAL)
num_header->fractional += *pOut;
else
num_header->whole += *pOut;
TRACE("%d 0's\n", *pOut);
pOut++;
fmt_state &= ~FMT_STATE_OPEN_COPY;
}
else if (*pFormat == '#' && COULD_BE(FMT_TYPE_NUMBER))
{
/* Number formats: Digit from number or blank if no digits
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_NUMBER;
NEED_SPACE(2 * sizeof(BYTE));
*pOut++ = FMT_NUM_COPY_SKIP;
*pOut = 0x0;
while (*pFormat == '#')
{
*pOut = *pOut + 1;
pFormat++;
}
if (fmt_state & FMT_STATE_WROTE_DECIMAL)
num_header->fractional += *pOut;
else
num_header->whole += *pOut;
TRACE("%d #'s\n", *pOut);
pOut++;
fmt_state &= ~FMT_STATE_OPEN_COPY;
}
else if (*pFormat == '.' && COULD_BE(FMT_TYPE_NUMBER) &&
!(fmt_state & FMT_STATE_WROTE_DECIMAL))
{
/* Number formats: Decimal separator when 1st seen, literal thereafter
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_NUMBER;
NEED_SPACE(sizeof(BYTE));
*pOut++ = FMT_NUM_DECIMAL;
fmt_state |= FMT_STATE_WROTE_DECIMAL;
fmt_state &= ~FMT_STATE_OPEN_COPY;
pFormat++;
TRACE("decimal sep\n");
}
else if ((*pFormat == 'e' || *pFormat == 'E') && (pFormat[1] == '-' ||
pFormat[1] == '+') && header->type == FMT_TYPE_NUMBER)
{
/* Number formats: Exponent specifier
* Other formats: Literal
*/
num_header->flags |= FMT_FLAG_EXPONENT;
NEED_SPACE(2 * sizeof(BYTE));
if (*pFormat == 'e') {
if (pFormat[1] == '+')
*pOut = FMT_NUM_EXP_POS_L;
else
*pOut = FMT_NUM_EXP_NEG_L;
} else {
if (pFormat[1] == '+')
*pOut = FMT_NUM_EXP_POS_U;
else
*pOut = FMT_NUM_EXP_NEG_U;
}
pFormat += 2;
*++pOut = 0x0;
while (*pFormat == '0')
{
*pOut = *pOut + 1;
pFormat++;
}
pOut++;
TRACE("exponent\n");
}
/* FIXME: %% => Divide by 1000 */
else if (*pFormat == ',' && header->type == FMT_TYPE_NUMBER)
{
/* Number formats: Use the thousands separator
* Other formats: Literal
*/
num_header->flags |= FMT_FLAG_THOUSANDS;
pFormat++;
fmt_state &= ~FMT_STATE_OPEN_COPY;
TRACE("thousands sep\n");
}
/* -----------
* Date tokens
* -----------
*/
else if (*pFormat == '/' && COULD_BE(FMT_TYPE_DATE))
{
/* Date formats: Date separator
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_DATE;
NEED_SPACE(sizeof(BYTE));
*pOut++ = FMT_DATE_DATE_SEP;
pFormat++;
fmt_state &= ~FMT_STATE_OPEN_COPY;
TRACE("date sep\n");
}
else if (*pFormat == ':' && COULD_BE(FMT_TYPE_DATE))
{
/* Date formats: Time separator
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_DATE;
NEED_SPACE(sizeof(BYTE));
*pOut++ = FMT_DATE_TIME_SEP;
pFormat++;
fmt_state &= ~FMT_STATE_OPEN_COPY;
TRACE("time sep\n");
}
else if ((*pFormat == 'a' || *pFormat == 'A') &&
!strncmpiW(pFormat, szAMPM, sizeof(szAMPM)/sizeof(WCHAR)))
{
/* Date formats: System AM/PM designation
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_DATE;
NEED_SPACE(sizeof(BYTE));
pFormat += sizeof(szAMPM)/sizeof(WCHAR);
if (!strncmpW(pFormat, szampm, sizeof(szampm)/sizeof(WCHAR)))
*pOut++ = FMT_DATE_AMPM_SYS2;
else
*pOut++ = FMT_DATE_AMPM_SYS1;
if (pLastHours)
*pLastHours = *pLastHours + 2;
TRACE("ampm\n");
}
else if (*pFormat == 'a' && pFormat[1] == '/' &&
(pFormat[2] == 'p' || pFormat[2] == 'P'))
{
/* Date formats: lowercase a or p designation
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_DATE;
NEED_SPACE(sizeof(BYTE));
pFormat += 3;
*pOut++ = FMT_DATE_A_LOWER;
if (pLastHours)
*pLastHours = *pLastHours + 2;
TRACE("a/p\n");
}
else if (*pFormat == 'A' && pFormat[1] == '/' &&
(pFormat[2] == 'p' || pFormat[2] == 'P'))
{
/* Date formats: Uppercase a or p designation
* Other formats: Literal
* Types the format if found
*/
header->type = FMT_TYPE_DATE;
NEED_SPACE(sizeof(BYTE));
pFormat += 3;
*pOut++ = FMT_DATE_A_UPPER;
if (pLastHours)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -