📄 varformat.c
字号:
/*
* Variant formatting functions
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
* Since the formatting functions aren't properly documented, I used the
* Visual Basic documentation as a guide to implementing these functions. This
* means that some named or user-defined formats may work slightly differently.
* Please submit a test case if you find a difference.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wine/unicode.h"
#include "winerror.h"
#include "variant.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(variant);
/* Make sure internal conversions to strings use the '.','+'/'-' and ','
* format chars from the US locale. This enables us to parse the created
* strings to determine the number of decimal places, exponent, etc.
*/
#define LCID_US MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT)
static const WCHAR szPercent_d[] = { '%','d','\0' };
static const WCHAR szPercentZeroTwo_d[] = { '%','0','2','d','\0' };
static const WCHAR szPercentZeroStar_d[] = { '%','0','*','d','\0' };
#if 0
#define dump_tokens(rgb) do { \
int i_; TRACE("Tokens->{\n"); \
for (i_ = 0; i_ < rgb[0]; i_++) \
TRACE("%s0x%02x", i_?",":"",rgb[i_]); \
TRACE(" }\n"); \
} while(0)
#endif
/******************************************************************************
* Variant-Formats {OLEAUT32}
*
* NOTES
* When formatting a variant a variety of format strings may be used to generate
* different kinds of formatted output. A format string consists of either a named
* format, or a user-defined format.
*
* The following named formats are defined:
*| Name Description
*| ---- -----------
*| General Date Display Date, and time for non-integer values
*| Short Date Short date format as defined by locale settings
*| Medium Date Medium date format as defined by locale settings
*| Long Date Long date format as defined by locale settings
*| Short Time Short Time format as defined by locale settings
*| Medium Time Medium time format as defined by locale settings
*| Long Time Long time format as defined by locale settings
*| True/False Localised text of "True" or "False"
*| Yes/No Localised text of "Yes" or "No"
*| On/Off Localised text of "On" or "Off"
*| General Number No thousands separator. No decimal points for integers
*| Currency General currency format using localised characters
*| Fixed At least one whole and two fractional digits
*| Standard Same as 'Fixed', but including decimal separators
*| Percent Multiply by 100 and display a trailing '%' character
*| Scientific Display with exponent
*
* User-defined formats consist of a combination of tokens and literal
* characters. Literal characters are copied unmodified to the formatted
* output at the position they occupy in the format string. Any character
* that is not recognised as a token is treated as a literal. A literal can
* also be specified by preceding it with a backslash character
* (e.g. "\L\i\t\e\r\a\l") or enclosing it in double quotes.
*
* A user-defined format can have up to 4 sections, depending on the type of
* format. The following table lists sections and their meaning:
*| Format Type Sections Meaning
*| ----------- -------- -------
*| Number 1 Use the same format for all numbers
*| Number 2 Use format 1 for positive and 2 for negative numbers
*| Number 3 Use format 1 for positive, 2 for zero, and 3
*| for negative numbers.
*| Number 4 Use format 1 for positive, 2 for zero, 3 for
*| negative, and 4 for null numbers.
*| String 1 Use the same format for all strings
*| String 2 Use format 2 for null and empty strings, otherwise
*| use format 1.
*| Date 1 Use the same format for all dates
*
* The formatting tokens fall into several categories depending on the type
* of formatted output. For more information on each type, see
* VarFormat-Dates(), VarFormat-Strings() and VarFormat-Numbers().
*
* SEE ALSO
* VarTokenizeFormatString(), VarFormatFromTokens(), VarFormat(),
* VarFormatDateTime(), VarFormatNumber(), VarFormatCurrency().
*/
/******************************************************************************
* VarFormat-Strings {OLEAUT32}
*
* NOTES
* When formatting a variant as a string, it is first converted to a VT_BSTR.
* The user-format string defines which characters are copied into which
* positions in the output string. Literals may be inserted in the format
* string. When creating the formatted string, excess characters in the string
* (those not consumed by a token) are appended to the end of the output. If
* there are more tokens than characters in the string to format, spaces will
* be inserted at the start of the string if the '@' token was used.
*
* By default strings are converted to lowercase, or uppercase if the '>' token
* is encountered. This applies to the whole string: it is not possible to
* generate a mixed-case output string.
*
* In user-defined string formats, the following tokens are recognised:
*| Token Description
*| ----- -----------
*| '@' Copy a char from the source, or a space if no chars are left.
*| '&' Copy a char from the source, or write nothing if no chars are left.
*| '<' Output the whole string as lower-case (the default).
*| '>' Output the whole string as upper-case.
*| '!' MSDN indicates that this character should cause right-to-left
*| copying, however tests show that it is tokenised but not processed.
*/
/*
* Common format definitions
*/
/* Fomat types */
#define FMT_TYPE_UNKNOWN 0x0
#define FMT_TYPE_GENERAL 0x1
#define FMT_TYPE_NUMBER 0x2
#define FMT_TYPE_DATE 0x3
#define FMT_TYPE_STRING 0x4
#define FMT_TO_STRING 0x0 /* If header->size == this, act like VB's Str() fn */
typedef struct tagFMT_SHORT_HEADER
{
BYTE size; /* Size of tokenised block (including header), or FMT_TO_STRING */
BYTE type; /* Allowable types (FMT_TYPE_*) */
BYTE offset[1]; /* Offset of the first (and only) format section */
} FMT_SHORT_HEADER;
typedef struct tagFMT_HEADER
{
BYTE size; /* Total size of the whole tokenised block (including header) */
BYTE type; /* Allowable types (FMT_TYPE_*) */
BYTE starts[4]; /* Offset of each of the 4 format sections, or 0 if none */
} FMT_HEADER;
#define FmtGetPositive(x) (x->starts[0])
#define FmtGetNegative(x) (x->starts[1] ? x->starts[1] : x->starts[0])
#define FmtGetZero(x) (x->starts[2] ? x->starts[2] : x->starts[0])
#define FmtGetNull(x) (x->starts[3] ? x->starts[3] : x->starts[0])
/*
* String formats
*/
#define FMT_FLAG_LT 0x1 /* Has '<' (lower case) */
#define FMT_FLAG_GT 0x2 /* Has '>' (upper case) */
#define FMT_FLAG_RTL 0x4 /* Has '!' (Copy right to left) */
typedef struct tagFMT_STRING_HEADER
{
BYTE flags; /* LT, GT, RTL */
BYTE unknown1;
BYTE unknown2;
BYTE copy_chars; /* Number of chars to be copied */
BYTE unknown3;
} FMT_STRING_HEADER;
/*
* Number formats
*/
#define FMT_FLAG_PERCENT 0x1 /* Has '%' (Percentage) */
#define FMT_FLAG_EXPONENT 0x2 /* Has 'e' (Exponent/Scientific notation) */
#define FMT_FLAG_THOUSANDS 0x4 /* Has ',' (Standard use of the thousands separator) */
#define FMT_FLAG_BOOL 0x20 /* Boolean format */
typedef struct tagFMT_NUMBER_HEADER
{
BYTE flags; /* PERCENT, EXPONENT, THOUSANDS, BOOL */
BYTE multiplier; /* Multiplier, 100 for percentages */
BYTE divisor; /* Divisor, 1000 if '%%' was used */
BYTE whole; /* Number of digits before the decimal point */
BYTE fractional; /* Number of digits after the decimal point */
} FMT_NUMBER_HEADER;
/*
* Date Formats
*/
typedef struct tagFMT_DATE_HEADER
{
BYTE flags;
BYTE unknown1;
BYTE unknown2;
BYTE unknown3;
BYTE unknown4;
} FMT_DATE_HEADER;
/*
* Format token values
*/
#define FMT_GEN_COPY 0x00 /* \n, "lit" => 0,pos,len: Copy len chars from input+pos */
#define FMT_GEN_INLINE 0x01 /* => 1,len,[chars]: Copy len chars from token stream */
#define FMT_GEN_END 0x02 /* \0,; => 2: End of the tokenised format */
#define FMT_DATE_TIME_SEP 0x03 /* Time separator char */
#define FMT_DATE_DATE_SEP 0x04 /* Date separator char */
#define FMT_DATE_GENERAL 0x05 /* General format date */
#define FMT_DATE_QUARTER 0x06 /* Quarter of the year from 1-4 */
#define FMT_DATE_TIME_SYS 0x07 /* System long time format */
#define FMT_DATE_DAY 0x08 /* Day with no leading 0 */
#define FMT_DATE_DAY_0 0x09 /* Day with leading 0 */
#define FMT_DATE_DAY_SHORT 0x0A /* Short day name */
#define FMT_DATE_DAY_LONG 0x0B /* Long day name */
#define FMT_DATE_SHORT 0x0C /* Short date format */
#define FMT_DATE_LONG 0x0D /* Long date format */
#define FMT_DATE_MEDIUM 0x0E /* Medium date format */
#define FMT_DATE_DAY_WEEK 0x0F /* First day of the week */
#define FMT_DATE_WEEK_YEAR 0x10 /* First week of the year */
#define FMT_DATE_MON 0x11 /* Month with no leading 0 */
#define FMT_DATE_MON_0 0x12 /* Month with leading 0 */
#define FMT_DATE_MON_SHORT 0x13 /* Short month name */
#define FMT_DATE_MON_LONG 0x14 /* Long month name */
#define FMT_DATE_YEAR_DOY 0x15 /* Day of the year with no leading 0 */
#define FMT_DATE_YEAR_0 0x16 /* 2 digit year with leading 0 */
/* NOTE: token 0x17 is not defined, 'yyy' is not valid */
#define FMT_DATE_YEAR_LONG 0x18 /* 4 digit year */
#define FMT_DATE_MIN 0x1A /* Minutes with no leading 0 */
#define FMT_DATE_MIN_0 0x1B /* Minutes with leading 0 */
#define FMT_DATE_SEC 0x1C /* Seconds with no leading 0 */
#define FMT_DATE_SEC_0 0x1D /* Seconds with leading 0 */
#define FMT_DATE_HOUR 0x1E /* Hours with no leading 0 */
#define FMT_DATE_HOUR_0 0x1F /* Hours with leading 0 */
#define FMT_DATE_HOUR_12 0x20 /* Hours with no leading 0, 12 hour clock */
#define FMT_DATE_HOUR_12_0 0x21 /* Hours with leading 0, 12 hour clock */
#define FMT_DATE_TIME_UNK2 0x23
/* FIXME: probably missing some here */
#define FMT_DATE_AMPM_SYS1 0x2E /* AM/PM as defined by system settings */
#define FMT_DATE_AMPM_UPPER 0x2F /* Upper-case AM or PM */
#define FMT_DATE_A_UPPER 0x30 /* Upper-case A or P */
#define FMT_DATE_AMPM_SYS2 0x31 /* AM/PM as defined by system settings */
#define FMT_DATE_AMPM_LOWER 0x32 /* Lower-case AM or PM */
#define FMT_DATE_A_LOWER 0x33 /* Lower-case A or P */
#define FMT_NUM_COPY_ZERO 0x34 /* Copy 1 digit or 0 if no digit */
#define FMT_NUM_COPY_SKIP 0x35 /* Copy 1 digit or skip if no digit */
#define FMT_NUM_DECIMAL 0x36 /* Decimal separator */
#define FMT_NUM_EXP_POS_U 0x37 /* Scientific notation, uppercase, + sign */
#define FMT_NUM_EXP_NEG_U 0x38 /* Scientific notation, uppercase, - sign */
#define FMT_NUM_EXP_POS_L 0x39 /* Scientific notation, lowercase, + sign */
#define FMT_NUM_EXP_NEG_L 0x3A /* Scientific notation, lowercase, - sign */
#define FMT_NUM_CURRENCY 0x3B /* Currency symbol */
#define FMT_NUM_TRUE_FALSE 0x3D /* Convert to "True" or "False" */
#define FMT_NUM_YES_NO 0x3E /* Convert to "Yes" or "No" */
#define FMT_NUM_ON_OFF 0x3F /* Convert to "On" or "Off" */
#define FMT_STR_COPY_SPACE 0x40 /* Copy len chars with space if no char */
#define FMT_STR_COPY_SKIP 0x41 /* Copy len chars or skip if no char */
/* Wine additions */
#define FMT_WINE_HOURS_12 0x81 /* Hours using 12 hour clockhourCopy len chars or skip if no char */
/* Named Formats and their tokenised values */
static const WCHAR szGeneralDate[] = { 'G','e','n','e','r','a','l',' ','D','a','t','e','\0' };
static const BYTE fmtGeneralDate[0x0a] =
{
0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_GENERAL,FMT_GEN_END
};
static const WCHAR szShortDate[] = { 'S','h','o','r','t',' ','D','a','t','e','\0' };
static const BYTE fmtShortDate[0x0a] =
{
0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_SHORT,FMT_GEN_END
};
static const WCHAR szMediumDate[] = { 'M','e','d','i','u','m',' ','D','a','t','e','\0' };
static const BYTE fmtMediumDate[0x0a] =
{
0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_MEDIUM,FMT_GEN_END
};
static const WCHAR szLongDate[] = { 'L','o','n','g',' ','D','a','t','e','\0' };
static const BYTE fmtLongDate[0x0a] =
{
0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_LONG,FMT_GEN_END
};
static const WCHAR szShortTime[] = { 'S','h','o','r','t',' ','T','i','m','e','\0' };
static const BYTE fmtShortTime[0x0c] =
{
0x0c,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_TIME_UNK2,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,FMT_GEN_END
};
static const WCHAR szMediumTime[] = { 'M','e','d','i','u','m',' ','T','i','m','e','\0' };
static const BYTE fmtMediumTime[0x11] =
{
0x11,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_HOUR_12_0,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,
FMT_GEN_INLINE,0x01,' ','\0',FMT_DATE_AMPM_SYS1,FMT_GEN_END
};
static const WCHAR szLongTime[] = { 'L','o','n','g',' ','T','i','m','e','\0' };
static const BYTE fmtLongTime[0x0d] =
{
0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
0x0,0x0,0x0,0x0,0x0,
FMT_DATE_TIME_SYS,FMT_GEN_END
};
static const WCHAR szTrueFalse[] = { 'T','r','u','e','/','F','a','l','s','e','\0' };
static const BYTE fmtTrueFalse[0x0d] =
{
0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
FMT_NUM_TRUE_FALSE,FMT_GEN_END
};
static const WCHAR szYesNo[] = { 'Y','e','s','/','N','o','\0' };
static const BYTE fmtYesNo[0x0d] =
{
0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
FMT_NUM_YES_NO,FMT_GEN_END
};
static const WCHAR szOnOff[] = { 'O','n','/','O','f','f','\0' };
static const BYTE fmtOnOff[0x0d] =
{
0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
FMT_NUM_ON_OFF,FMT_GEN_END
};
static const WCHAR szGeneralNumber[] = { 'G','e','n','e','r','a','l',' ','N','u','m','b','e','r','\0' };
static const BYTE fmtGeneralNumber[sizeof(FMT_HEADER)] =
{
sizeof(FMT_HEADER),FMT_TYPE_GENERAL,sizeof(FMT_HEADER),0x0,0x0,0x0
};
static const WCHAR szCurrency[] = { 'C','u','r','r','e','n','c','y','\0' };
static const BYTE fmtCurrency[0x26] =
{
0x26,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x12,0x0,0x0,
/* Positive numbers */
FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
FMT_GEN_END,
/* Negative numbers */
FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
FMT_GEN_INLINE,0x1,'(','\0',FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,
FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_INLINE,0x1,')','\0',
FMT_GEN_END
};
static const WCHAR szFixed[] = { 'F','i','x','e','d','\0' };
static const BYTE fmtFixed[0x11] =
{
0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
0x0,0x0,0x0,0x1,0x2,
FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
};
static const WCHAR szStandard[] = { 'S','t','a','n','d','a','r','d','\0' };
static const BYTE fmtStandard[0x11] =
{
0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_THOUSANDS,0x0,0x0,0x1,0x2,
FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
};
static const WCHAR szPercent[] = { 'P','e','r','c','e','n','t','\0' };
static const BYTE fmtPercent[0x15] =
{
0x15,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
FMT_FLAG_PERCENT,0x1,0x0,0x1,0x2,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -