Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-cw
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-cw
Commits
d537c955
Commit
d537c955
authored
Nov 25, 2003
by
Jon Griffiths
Committed by
Alexandre Julliard
Nov 25, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented the variant formatting functions.
Bool->bstr makes a number str unless [ALPHA|LOCAL]BOOL is passed.
parent
f9bca5ea
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2461 additions
and
554 deletions
+2461
-554
Makefile.in
dlls/oleaut32/Makefile.in
+1
-0
oleaut32.spec
dlls/oleaut32/oleaut32.spec
+2
-2
varformat.c
dlls/oleaut32/varformat.c
+2417
-0
variant.c
dlls/oleaut32/variant.c
+41
-552
No files found.
dlls/oleaut32/Makefile.in
View file @
d537c955
...
...
@@ -26,6 +26,7 @@ C_SRCS = \
tmarshal.c
\
typelib.c
\
usrmarshal.c
\
varformat.c
\
variant.c
C_SRCS16
=
\
...
...
dlls/oleaut32/oleaut32.spec
View file @
d537c955
...
...
@@ -104,7 +104,7 @@
104 stdcall VarCyFromStr(ptr long long ptr)
105 stub VarCyFromDisp
106 stdcall VarCyFromBool(long ptr)
107 st
ub VarFormatNumber # stdcall
(ptr long long long long long ptr)
107 st
dcall VarFormatNumber
(ptr long long long long long ptr)
108 stdcall VarBstrFromUI1(long long long ptr)
109 stdcall VarBstrFromI2(long long long ptr)
110 stdcall VarBstrFromI4(long long long ptr)
...
...
@@ -114,7 +114,7 @@
114 stdcall VarBstrFromDate(double long long ptr)
115 stub VarBstrFromDisp
116 stdcall VarBstrFromBool(long long long ptr)
117 st
ub VarFormatPercent # stdcall
(ptr long long long long long ptr)
117 st
dcall VarFormatPercent
(ptr long long long long long ptr)
118 stdcall VarBoolFromUI1(long ptr)
119 stdcall VarBoolFromI2(long ptr)
120 stdcall VarBoolFromI4(long ptr)
...
...
dlls/oleaut32/varformat.c
0 → 100644
View file @
d537c955
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "oleauto.h"
#include "wine/debug.h"
#include "wine/unicode.h"
#include "winerror.h"
#include "variant.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
ole
);
/* 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
szPercentZeroFour_d
[]
=
{
'%'
,
'0'
,
'4'
,
'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 seperator. No decimal points for integers
*| Currency General currency format using localised characters
*| Fixed At least one whole and two fractional digits
*| Standard As for 'Fixed', but including decimal seperators
*| Percent Multiply by 100 and dispaly a traling '%' 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 preceeding 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 seperator) */
#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 seperator char */
#define FMT_DATE_DATE_SEP 0x04
/* Date seperator 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 seperator */
#define FMT_NUM_EXP_POS_U 0x37
/* Scientific notation, uppercase, + sign */
#define FMT_NUM_EXP_NEG_U 0x38
/* Scientific notation, lowercase, - sign */
#define FMT_NUM_EXP_POS_L 0x39
/* Scientific notation, uppercase, + 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
,
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 seperator */
#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%08lx,%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. Thats 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 seperator 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
"
);
}
/* FIXME: E+ E- e+ e- => Exponent */
/* FIXME: %% => Divide by 1000 */
else
if
(
*
pFormat
==
','
&&
header
->
type
==
FMT_TYPE_NUMBER
)
{
/* Number formats: Use the thousands seperator
* 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 seperator
* 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 seperator
* 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
)
*
pLastHours
=
*
pLastHours
+
2
;
TRACE
(
"A/P
\n
"
);
}
else
if
(
*
pFormat
==
'a'
&&
!
strncmpW
(
pFormat
,
szamSlashpm
,
sizeof
(
szamSlashpm
)
/
sizeof
(
WCHAR
)))
{
/* Date formats: lowercase AM or PM designation
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
+=
sizeof
(
szamSlashpm
)
/
sizeof
(
WCHAR
);
*
pOut
++
=
FMT_DATE_AMPM_LOWER
;
if
(
pLastHours
)
*
pLastHours
=
*
pLastHours
+
2
;
TRACE
(
"AM/PM
\n
"
);
}
else
if
(
*
pFormat
==
'A'
&&
!
strncmpW
(
pFormat
,
szAMSlashPM
,
sizeof
(
szAMSlashPM
)
/
sizeof
(
WCHAR
)))
{
/* Date formats: Uppercase AM or PM designation
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
+=
sizeof
(
szAMSlashPM
)
/
sizeof
(
WCHAR
);
*
pOut
++
=
FMT_DATE_AMPM_UPPER
;
TRACE
(
"AM/PM
\n
"
);
}
else
if
(
*
pFormat
==
'c'
||
*
pFormat
==
'C'
)
{
/* Date formats: General date format
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
+=
sizeof
(
szAMSlashPM
)
/
sizeof
(
WCHAR
);
*
pOut
++
=
FMT_DATE_GENERAL
;
TRACE
(
"gen date
\n
"
);
}
else
if
((
*
pFormat
==
'd'
||
*
pFormat
==
'D'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Day specifier
* Other formats: Literal
* Types the format if found
*/
int
count
=
-
1
;
header
->
type
=
FMT_TYPE_DATE
;
while
((
*
pFormat
==
'd'
||
*
pFormat
==
'D'
)
&&
count
<
6
)
{
pFormat
++
;
count
++
;
}
NEED_SPACE
(
sizeof
(
BYTE
));
*
pOut
++
=
FMT_DATE_DAY
+
count
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
/* When we find the days token, reset the seen hours state so that
* 'mm' is again written as month when encountered.
*/
fmt_state
&=
~
FMT_STATE_SEEN_HOURS
;
TRACE
(
"%d d's
\n
"
,
count
+
1
);
}
else
if
((
*
pFormat
==
'h'
||
*
pFormat
==
'H'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Hour specifier
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
++
;
/* Record the position of the hours specifier - if we encounter
* an am/pm specifier we will change the hours from 24 to 12.
*/
pLastHours
=
pOut
;
if
(
*
pFormat
==
'h'
||
*
pFormat
==
'H'
)
{
pFormat
++
;
*
pOut
++
=
FMT_DATE_HOUR_0
;
TRACE
(
"hh
\n
"
);
}
else
{
*
pOut
++
=
FMT_DATE_HOUR
;
TRACE
(
"h
\n
"
);
}
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
/* Note that now we have seen an hours token, the next occurence of
* 'mm' indicates minutes, not months.
*/
fmt_state
|=
FMT_STATE_SEEN_HOURS
;
}
else
if
((
*
pFormat
==
'm'
||
*
pFormat
==
'M'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Month specifier (or Minute specifier, after hour specifier)
* Other formats: Literal
* Types the format if found
*/
int
count
=
-
1
;
header
->
type
=
FMT_TYPE_DATE
;
while
((
*
pFormat
==
'm'
||
*
pFormat
==
'M'
)
&&
count
<
4
)
{
pFormat
++
;
count
++
;
}
NEED_SPACE
(
sizeof
(
BYTE
));
if
(
count
<=
1
&&
fmt_state
&
FMT_STATE_SEEN_HOURS
&&
!
(
fmt_state
&
FMT_STATE_WROTE_MINUTES
))
{
/* We have seen an hours specifier and not yet written a minutes
* specifier. Write this as minutes and thereafter as months.
*/
*
pOut
++
=
count
==
1
?
FMT_DATE_MIN_0
:
FMT_DATE_MIN
;
fmt_state
|=
FMT_STATE_WROTE_MINUTES
;
/* Hereafter write months */
}
else
*
pOut
++
=
FMT_DATE_MON
+
count
;
/* Months */
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
TRACE
(
"%d m's
\n
"
,
count
+
1
);
}
else
if
((
*
pFormat
==
'n'
||
*
pFormat
==
'N'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Minute specifier
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
++
;
if
(
*
pFormat
==
'n'
||
*
pFormat
==
'N'
)
{
pFormat
++
;
*
pOut
++
=
FMT_DATE_MIN_0
;
TRACE
(
"nn
\n
"
);
}
else
{
*
pOut
++
=
FMT_DATE_MIN
;
TRACE
(
"n
\n
"
);
}
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
((
*
pFormat
==
'q'
||
*
pFormat
==
'q'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Quarter specifier
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
*
pOut
++
=
FMT_DATE_QUARTER
;
pFormat
++
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
TRACE
(
"quarter
\n
"
);
}
else
if
((
*
pFormat
==
's'
||
*
pFormat
==
'S'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Second specifier
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
NEED_SPACE
(
sizeof
(
BYTE
));
pFormat
++
;
if
(
*
pFormat
==
's'
||
*
pFormat
==
'S'
)
{
pFormat
++
;
*
pOut
++
=
FMT_DATE_SEC_0
;
TRACE
(
"ss
\n
"
);
}
else
{
*
pOut
++
=
FMT_DATE_SEC
;
TRACE
(
"s
\n
"
);
}
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
((
*
pFormat
==
't'
||
*
pFormat
==
'T'
)
&&
!
strncmpiW
(
pFormat
,
szTTTTT
,
sizeof
(
szTTTTT
)
/
sizeof
(
WCHAR
)))
{
/* Date formats: System time specifier
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
pFormat
+=
sizeof
(
szTTTTT
)
/
sizeof
(
WCHAR
);
NEED_SPACE
(
sizeof
(
BYTE
));
*
pOut
++
=
FMT_DATE_TIME_SYS
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
((
*
pFormat
==
'w'
||
*
pFormat
==
'W'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Week of the year/Day of the week
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_DATE
;
pFormat
++
;
if
(
*
pFormat
==
'w'
||
*
pFormat
==
'W'
)
{
NEED_SPACE
(
3
*
sizeof
(
BYTE
));
pFormat
++
;
*
pOut
++
=
FMT_DATE_WEEK_YEAR
;
*
pOut
++
=
nFirstDay
;
*
pOut
++
=
nFirstWeek
;
TRACE
(
"ww
\n
"
);
}
else
{
NEED_SPACE
(
2
*
sizeof
(
BYTE
));
*
pOut
++
=
FMT_DATE_DAY_WEEK
;
*
pOut
++
=
nFirstDay
;
TRACE
(
"w
\n
"
);
}
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
((
*
pFormat
==
'y'
||
*
pFormat
==
'Y'
)
&&
COULD_BE
(
FMT_TYPE_DATE
))
{
/* Date formats: Day of year/Year specifier
* Other formats: Literal
* Types the format if found
*/
int
count
=
-
1
;
header
->
type
=
FMT_TYPE_DATE
;
while
((
*
pFormat
==
'y'
||
*
pFormat
==
'Y'
)
&&
count
<
4
)
{
pFormat
++
;
count
++
;
}
if
(
count
==
2
)
{
count
--
;
/* 'yyy' has no meaning, despite what MSDN says */
pFormat
--
;
}
NEED_SPACE
(
sizeof
(
BYTE
));
*
pOut
++
=
FMT_DATE_YEAR_DOY
+
count
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
TRACE
(
"%d y's
\n
"
,
count
+
1
);
}
/* -------------
* String tokens
* -------------
*/
else
if
(
*
pFormat
==
'@'
&&
COULD_BE
(
FMT_TYPE_STRING
))
{
/* String formats: Character from string or space if no char
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_STRING
;
NEED_SPACE
(
2
*
sizeof
(
BYTE
));
*
pOut
++
=
FMT_STR_COPY_SPACE
;
*
pOut
=
0x0
;
while
(
*
pFormat
==
'@'
)
{
*
pOut
=
*
pOut
+
1
;
str_header
->
copy_chars
++
;
pFormat
++
;
}
TRACE
(
"%d @'s
\n
"
,
*
pOut
);
pOut
++
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
(
*
pFormat
==
'&'
&&
COULD_BE
(
FMT_TYPE_STRING
))
{
/* String formats: Character from string or skip if no char
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_STRING
;
NEED_SPACE
(
2
*
sizeof
(
BYTE
));
*
pOut
++
=
FMT_STR_COPY_SKIP
;
*
pOut
=
0x0
;
while
(
*
pFormat
==
'&'
)
{
*
pOut
=
*
pOut
+
1
;
str_header
->
copy_chars
++
;
pFormat
++
;
}
TRACE
(
"%d &'s
\n
"
,
*
pOut
);
pOut
++
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
((
*
pFormat
==
'<'
||
*
pFormat
==
'>'
)
&&
COULD_BE
(
FMT_TYPE_STRING
))
{
/* String formats: Use upper/lower case
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_STRING
;
if
(
*
pFormat
==
'<'
)
str_header
->
flags
|=
FMT_FLAG_LT
;
else
str_header
->
flags
|=
FMT_FLAG_GT
;
TRACE
(
"to %s case
\n
"
,
*
pFormat
==
'<'
?
"lower"
:
"upper"
);
pFormat
++
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
}
else
if
(
*
pFormat
==
'!'
&&
COULD_BE
(
FMT_TYPE_STRING
))
{
/* String formats: Copy right to left
* Other formats: Literal
* Types the format if found
*/
header
->
type
=
FMT_TYPE_STRING
;
str_header
->
flags
|=
FMT_FLAG_RTL
;
pFormat
++
;
fmt_state
&=
~
FMT_STATE_OPEN_COPY
;
TRACE
(
"copy right-to-left
\n
"
);
}
/* --------
* Literals
* --------
*/
/* FIXME: [ seems to be ignored */
else
{
if
(
*
pFormat
==
'%'
&&
header
->
type
==
FMT_TYPE_NUMBER
)
{
/* Number formats: Percentage indicator, also a literal
* Other formats: Literal
* Doesn't type the format
*/
num_header
->
flags
|=
FMT_FLAG_PERCENT
;
}
if
(
fmt_state
&
FMT_STATE_OPEN_COPY
)
{
pOut
[
-
1
]
=
pOut
[
-
1
]
+
1
;
/* Increase the length of the open copy */
TRACE
(
"extend copy (char '%c'), length now %d
\n
"
,
*
pFormat
,
pOut
[
-
1
]);
}
else
{
/* Create a new open copy */
TRACE
(
"New copy (char '%c')
\n
"
,
*
pFormat
);
NEED_SPACE
(
3
*
sizeof
(
BYTE
));
*
pOut
++
=
FMT_GEN_COPY
;
*
pOut
++
=
pFormat
-
lpszFormat
;
*
pOut
++
=
0x1
;
fmt_state
|=
FMT_STATE_OPEN_COPY
;
}
pFormat
++
;
}
}
*
pOut
++
=
FMT_GEN_END
;
header
->
size
=
pOut
-
rgbTok
;
if
(
pcbActual
)
*
pcbActual
=
header
->
size
;
return
S_OK
;
}
/* Number formatting state flags */
#define NUM_WROTE_DEC 0x01
/* Written the decimal seperator */
/* Format a variant using a number format */
static
HRESULT
VARIANT_FormatNumber
(
LPVARIANT
pVarIn
,
LPOLESTR
lpszFormat
,
LPBYTE
rgbTok
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
,
LCID
lcid
)
{
BYTE
rgbDig
[
256
];
NUMPARSE
np
;
int
wholeNumberDigits
,
fractionalDigits
,
divisor10
=
0
,
multiplier10
=
0
;
WCHAR
buff
[
256
],
*
pBuff
=
buff
;
VARIANT
vString
,
vBool
;
DWORD
dwState
=
0
;
FMT_HEADER
*
header
=
(
FMT_HEADER
*
)
rgbTok
;
FMT_NUMBER_HEADER
*
numHeader
;
const
BYTE
*
pToken
=
NULL
;
HRESULT
hRes
=
S_OK
;
TRACE
(
"(%p->(%s%s),%s,%p,0x%08lx,%p,0x%08lx)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
debugstr_w
(
lpszFormat
),
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
V_VT
(
&
vString
)
=
VT_EMPTY
;
V_VT
(
&
vBool
)
=
VT_BOOL
;
if
(
V_TYPE
(
pVarIn
)
==
VT_EMPTY
||
V_TYPE
(
pVarIn
)
==
VT_NULL
)
{
wholeNumberDigits
=
fractionalDigits
=
0
;
numHeader
=
(
FMT_NUMBER_HEADER
*
)(
rgbTok
+
FmtGetNull
(
header
));
V_BOOL
(
&
vBool
)
=
VARIANT_FALSE
;
}
else
{
/* Get a number string from pVarIn, and parse it */
hRes
=
VariantChangeTypeEx
(
&
vString
,
pVarIn
,
LCID_US
,
VARIANT_NOUSEROVERRIDE
,
VT_BSTR
);
if
(
FAILED
(
hRes
))
return
hRes
;
np
.
cDig
=
sizeof
(
rgbDig
);
np
.
dwInFlags
=
NUMPRS_STD
;
hRes
=
VarParseNumFromStr
(
V_BSTR
(
&
vString
),
LCID_US
,
0
,
&
np
,
rgbDig
);
if
(
FAILED
(
hRes
))
return
hRes
;
if
(
np
.
nPwr10
<
0
)
{
if
(
-
np
.
nPwr10
>=
np
.
cDig
)
{
/* A real number < +/- 1.0 e.g. 0.1024 or 0.01024 */
wholeNumberDigits
=
0
;
fractionalDigits
=
np
.
cDig
;
divisor10
=
-
np
.
nPwr10
;
}
else
{
/* An exactly represented real number e.g. 1.024 */
wholeNumberDigits
=
np
.
cDig
+
np
.
nPwr10
;
fractionalDigits
=
np
.
cDig
-
wholeNumberDigits
;
divisor10
=
np
.
cDig
-
wholeNumberDigits
;
}
}
else
if
(
np
.
nPwr10
==
0
)
{
/* An exactly represented whole number e.g. 1024 */
wholeNumberDigits
=
np
.
cDig
;
fractionalDigits
=
0
;
}
else
/* np.nPwr10 > 0 */
{
/* A whole number followed by nPwr10 0's e.g. 102400 */
wholeNumberDigits
=
np
.
cDig
;
fractionalDigits
=
0
;
multiplier10
=
np
.
nPwr10
;
}
/* Figure out which format to use */
if
(
np
.
dwOutFlags
&
NUMPRS_NEG
)
{
numHeader
=
(
FMT_NUMBER_HEADER
*
)(
rgbTok
+
FmtGetNegative
(
header
));
V_BOOL
(
&
vBool
)
=
VARIANT_TRUE
;
}
else
if
(
wholeNumberDigits
==
1
&&
!
fractionalDigits
&&
!
multiplier10
&&
!
divisor10
&&
rgbDig
[
0
]
==
0
)
{
numHeader
=
(
FMT_NUMBER_HEADER
*
)(
rgbTok
+
FmtGetZero
(
header
));
V_BOOL
(
&
vBool
)
=
VARIANT_FALSE
;
}
else
{
numHeader
=
(
FMT_NUMBER_HEADER
*
)(
rgbTok
+
FmtGetPositive
(
header
));
V_BOOL
(
&
vBool
)
=
VARIANT_TRUE
;
}
TRACE
(
"num header: flags = 0x%x, mult=%d, div=%d, whole=%d, fract=%d
\n
"
,
numHeader
->
flags
,
numHeader
->
multiplier
,
numHeader
->
divisor
,
numHeader
->
whole
,
numHeader
->
fractional
);
if
(
numHeader
->
flags
&
FMT_FLAG_PERCENT
&&
!
(
wholeNumberDigits
==
1
&&
!
fractionalDigits
&&
!
multiplier10
&&
!
divisor10
&&
rgbDig
[
0
]
==
0
))
{
/* *100 for %'s. Try to 'steal' fractional digits if we can */
TRACE
(
"Fraction - multiply by 100
\n
"
);
if
(
!
fractionalDigits
)
multiplier10
+=
2
;
else
{
fractionalDigits
--
;
wholeNumberDigits
++
;
if
(
!
fractionalDigits
)
multiplier10
++
;
else
{
fractionalDigits
--
;
wholeNumberDigits
++
;
}
}
}
TRACE
(
"cDig %d; nPwr10 %d, whole %d, frac %d "
,
np
.
cDig
,
np
.
nPwr10
,
wholeNumberDigits
,
fractionalDigits
);
TRACE
(
"mult %d; div %d
\n
"
,
multiplier10
,
divisor10
);
}
pToken
=
(
const
BYTE
*
)
numHeader
+
sizeof
(
FMT_NUMBER_HEADER
);
while
(
SUCCEEDED
(
hRes
)
&&
*
pToken
!=
FMT_GEN_END
)
{
WCHAR
defaultChar
=
'?'
;
DWORD
boolFlag
,
localeValue
=
0
;
if
(
pToken
-
rgbTok
>
header
->
size
)
{
ERR
(
"Ran off the end of the format!
\n
"
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatNumber_Exit
;
}
switch
(
*
pToken
)
{
case
FMT_GEN_COPY
:
TRACE
(
"copy %s
\n
"
,
debugstr_wn
(
lpszFormat
+
pToken
[
1
],
pToken
[
2
]));
memcpy
(
pBuff
,
lpszFormat
+
pToken
[
1
],
pToken
[
2
]
*
sizeof
(
WCHAR
));
pBuff
+=
pToken
[
2
];
pToken
+=
2
;
break
;
case
FMT_GEN_INLINE
:
pToken
+=
2
;
TRACE
(
"copy %s
\n
"
,
debugstr_a
(
pToken
));
while
(
*
pToken
)
*
pBuff
++
=
*
pToken
++
;
break
;
case
FMT_NUM_YES_NO
:
boolFlag
=
VAR_BOOLYESNO
;
goto
VARIANT_FormatNumber_Bool
;
break
;
case
FMT_NUM_ON_OFF
:
boolFlag
=
VAR_BOOLONOFF
;
goto
VARIANT_FormatNumber_Bool
;
break
;
case
FMT_NUM_TRUE_FALSE
:
boolFlag
=
VAR_LOCALBOOL
;
VARIANT_FormatNumber_Bool:
{
BSTR
boolStr
=
NULL
;
if
(
pToken
[
1
]
!=
FMT_GEN_END
)
{
ERR
(
"Boolean token not at end of format!
\n
"
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatNumber_Exit
;
}
hRes
=
VarBstrFromBool
(
V_BOOL
(
&
vBool
),
lcid
,
boolFlag
,
&
boolStr
);
if
(
SUCCEEDED
(
hRes
))
{
strcpyW
(
pBuff
,
boolStr
);
SysFreeString
(
boolStr
);
while
(
*
pBuff
)
pBuff
++
;
}
}
break
;
case
FMT_NUM_DECIMAL
:
TRACE
(
"write decimal seperator
\n
"
);
localeValue
=
LOCALE_SDECIMAL
;
defaultChar
=
'.'
;
dwState
|=
NUM_WROTE_DEC
;
break
;
case
FMT_NUM_CURRENCY
:
TRACE
(
"write currency symbol
\n
"
);
localeValue
=
LOCALE_SCURRENCY
;
defaultChar
=
'$'
;
break
;
case
FMT_NUM_EXP_POS_U
:
case
FMT_NUM_EXP_POS_L
:
case
FMT_NUM_EXP_NEG_U
:
case
FMT_NUM_EXP_NEG_L
:
if
(
*
pToken
==
FMT_NUM_EXP_POS_L
||
*
pToken
==
FMT_NUM_EXP_NEG_L
)
*
pBuff
++
=
'e'
;
else
*
pBuff
++
=
'E'
;
if
(
divisor10
)
{
*
pBuff
++
=
'-'
;
sprintfW
(
pBuff
,
szPercentZeroStar_d
,
pToken
[
1
],
divisor10
);
}
else
{
if
(
*
pToken
==
FMT_NUM_EXP_POS_L
||
*
pToken
==
FMT_NUM_EXP_POS_U
)
*
pBuff
++
=
'+'
;
sprintfW
(
pBuff
,
szPercentZeroStar_d
,
pToken
[
1
],
multiplier10
);
}
while
(
*
pBuff
)
pBuff
++
;
pToken
++
;
break
;
case
FMT_NUM_COPY_SKIP
:
if
(
dwState
&
NUM_WROTE_DEC
)
{
int
count
;
TRACE
(
"write %d fractional digits or skip
\n
"
,
pToken
[
1
]);
for
(
count
=
0
;
count
<
fractionalDigits
;
count
++
)
pBuff
[
count
]
=
rgbDig
[
wholeNumberDigits
+
count
];
pBuff
+=
fractionalDigits
;
}
else
{
int
count
;
TRACE
(
"write %d digits or skip
\n
"
,
pToken
[
1
]);
if
(
wholeNumberDigits
>
1
||
rgbDig
[
0
]
>
0
)
{
TRACE
(
"write %d whole number digits
\n
"
,
wholeNumberDigits
);
for
(
count
=
0
;
count
<
wholeNumberDigits
;
count
++
)
*
pBuff
++
=
'0'
+
rgbDig
[
count
];
TRACE
(
"write %d whole trailing 0's
\n
"
,
multiplier10
);
for
(
count
=
0
;
count
<
multiplier10
;
count
++
)
*
pBuff
++
=
'0'
;
/* Write trailing zeros for multiplied values */
}
}
pToken
++
;
break
;
case
FMT_NUM_COPY_ZERO
:
if
(
dwState
&
NUM_WROTE_DEC
)
{
int
count
;
TRACE
(
"write %d fractional digits or 0's
\n
"
,
pToken
[
1
]);
for
(
count
=
0
;
count
<
fractionalDigits
;
count
++
)
pBuff
[
count
]
=
rgbDig
[
wholeNumberDigits
+
count
];
pBuff
+=
fractionalDigits
;
if
(
pToken
[
1
]
>
fractionalDigits
)
{
count
=
pToken
[
1
]
-
fractionalDigits
;
while
(
count
--
)
*
pBuff
++
=
'0'
;
/* Write trailing zeros for missing digits */
}
}
else
{
int
count
;
TRACE
(
"write %d digits or 0's
\n
"
,
pToken
[
1
]);
if
(
pToken
[
1
]
>
(
wholeNumberDigits
+
multiplier10
))
{
count
=
pToken
[
1
]
-
(
wholeNumberDigits
+
multiplier10
);
TRACE
(
"write %d leading zeros
\n
"
,
count
);
while
(
count
--
)
*
pBuff
++
=
'0'
;
/* Write leading zeros for missing digits */
}
TRACE
(
"write %d whole number digits
\n
"
,
wholeNumberDigits
);
for
(
count
=
0
;
count
<
wholeNumberDigits
;
count
++
)
*
pBuff
++
=
'0'
+
rgbDig
[
count
];
TRACE
(
"write %d whole trailing 0's
\n
"
,
multiplier10
);
for
(
count
=
0
;
count
<
multiplier10
;
count
++
)
*
pBuff
++
=
'0'
;
/* Write trailing zeros for multiplied values */
}
pToken
++
;
break
;
default:
ERR
(
"Unknown token 0x%02x!
\n
"
,
*
pToken
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatNumber_Exit
;
break
;
}
if
(
localeValue
)
{
if
(
GetLocaleInfoW
(
lcid
,
localeValue
,
pBuff
,
sizeof
(
buff
)
/
sizeof
(
WCHAR
)
-
(
pBuff
-
buff
)))
{
TRACE
(
"added %s
\n
"
,
debugstr_w
(
pBuff
));
while
(
*
pBuff
)
pBuff
++
;
}
else
{
TRACE
(
"added %d '%c'
\n
"
,
defaultChar
,
defaultChar
);
*
pBuff
++
=
defaultChar
;
}
}
pToken
++
;
}
VARIANT_FormatNumber_Exit:
VariantClear
(
&
vString
);
*
pBuff
=
'\0'
;
TRACE
(
"buff is %s
\n
"
,
debugstr_w
(
buff
));
if
(
SUCCEEDED
(
hRes
))
{
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRes
=
E_OUTOFMEMORY
;
}
return
hRes
;
}
/* Format a variant using a date format */
static
HRESULT
VARIANT_FormatDate
(
LPVARIANT
pVarIn
,
LPOLESTR
lpszFormat
,
LPBYTE
rgbTok
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
,
LCID
lcid
)
{
WCHAR
buff
[
256
],
*
pBuff
=
buff
;
VARIANT
vDate
;
UDATE
udate
;
FMT_HEADER
*
header
=
(
FMT_HEADER
*
)
rgbTok
;
FMT_DATE_HEADER
*
dateHeader
;
const
BYTE
*
pToken
=
NULL
;
HRESULT
hRes
;
TRACE
(
"(%p->(%s%s),%s,%p,0x%08lx,%p,0x%08lx)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
debugstr_w
(
lpszFormat
),
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
V_VT
(
&
vDate
)
=
VT_EMPTY
;
if
(
V_TYPE
(
pVarIn
)
==
VT_EMPTY
||
V_TYPE
(
pVarIn
)
==
VT_NULL
)
{
dateHeader
=
(
FMT_DATE_HEADER
*
)(
rgbTok
+
FmtGetNegative
(
header
));
V_DATE
(
&
vDate
)
=
0
;
}
else
{
USHORT
usFlags
=
dwFlags
&
VARIANT_CALENDAR_HIJRI
?
VAR_CALENDAR_HIJRI
:
0
;
hRes
=
VariantChangeTypeEx
(
&
vDate
,
pVarIn
,
LCID_US
,
usFlags
,
VT_DATE
);
if
(
FAILED
(
hRes
))
return
hRes
;
dateHeader
=
(
FMT_DATE_HEADER
*
)(
rgbTok
+
FmtGetPositive
(
header
));
}
hRes
=
VarUdateFromDate
(
V_DATE
(
&
vDate
),
0
/* FIXME: flags? */
,
&
udate
);
if
(
FAILED
(
hRes
))
return
hRes
;
pToken
=
(
const
BYTE
*
)
dateHeader
+
sizeof
(
FMT_DATE_HEADER
);
while
(
*
pToken
!=
FMT_GEN_END
)
{
DWORD
dwVal
=
0
,
localeValue
=
0
,
dwFmt
=
0
;
LPCWSTR
szPrintFmt
=
NULL
;
WCHAR
defaultChar
=
'?'
;
if
(
pToken
-
rgbTok
>
header
->
size
)
{
ERR
(
"Ran off the end of the format!
\n
"
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatDate_Exit
;
}
switch
(
*
pToken
)
{
case
FMT_GEN_COPY
:
TRACE
(
"copy %s
\n
"
,
debugstr_wn
(
lpszFormat
+
pToken
[
1
],
pToken
[
2
]));
memcpy
(
pBuff
,
lpszFormat
+
pToken
[
1
],
pToken
[
2
]
*
sizeof
(
WCHAR
));
pBuff
+=
pToken
[
2
];
pToken
+=
2
;
break
;
case
FMT_DATE_TIME_SEP
:
TRACE
(
"time seperator
\n
"
);
localeValue
=
LOCALE_STIME
;
defaultChar
=
':'
;
break
;
case
FMT_DATE_DATE_SEP
:
TRACE
(
"date seperator
\n
"
);
localeValue
=
LOCALE_SDATE
;
defaultChar
=
'/'
;
break
;
case
FMT_DATE_GENERAL
:
{
BSTR
date
=
NULL
;
WCHAR
*
pDate
=
date
;
hRes
=
VarBstrFromDate
(
V_DATE
(
&
vDate
),
lcid
,
0
,
pbstrOut
);
if
(
FAILED
(
hRes
))
goto
VARIANT_FormatDate_Exit
;
while
(
*
pDate
)
*
pBuff
++
=
*
pDate
++
;
SysFreeString
(
date
);
}
break
;
case
FMT_DATE_QUARTER
:
if
(
udate
.
st
.
wMonth
<=
3
)
*
pBuff
++
=
'1'
;
else
if
(
udate
.
st
.
wMonth
<=
6
)
*
pBuff
++
=
'2'
;
else
if
(
udate
.
st
.
wMonth
<=
9
)
*
pBuff
++
=
'3'
;
else
*
pBuff
++
=
'4'
;
break
;
case
FMT_DATE_TIME_SYS
:
{
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
BSTR
date
=
NULL
;
WCHAR
*
pDate
=
date
;
hRes
=
VarBstrFromDate
(
V_DATE
(
&
vDate
),
lcid
,
VAR_TIMEVALUEONLY
,
pbstrOut
);
if
(
FAILED
(
hRes
))
goto
VARIANT_FormatDate_Exit
;
while
(
*
pDate
)
*
pBuff
++
=
*
pDate
++
;
SysFreeString
(
date
);
}
break
;
case
FMT_DATE_DAY
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wDay
;
break
;
case
FMT_DATE_DAY_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wDay
;
break
;
case
FMT_DATE_DAY_SHORT
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
TRACE
(
"short day
\n
"
);
localeValue
=
LOCALE_SABBREVDAYNAME1
+
udate
.
st
.
wMonth
-
1
;
defaultChar
=
'?'
;
break
;
case
FMT_DATE_DAY_LONG
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
TRACE
(
"long day
\n
"
);
localeValue
=
LOCALE_SDAYNAME1
+
udate
.
st
.
wMonth
-
1
;
defaultChar
=
'?'
;
break
;
case
FMT_DATE_SHORT
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
dwFmt
=
LOCALE_SSHORTDATE
;
break
;
case
FMT_DATE_LONG
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
dwFmt
=
LOCALE_SLONGDATE
;
break
;
case
FMT_DATE_MEDIUM
:
FIXME
(
"Medium date treated as long date
\n
"
);
dwFmt
=
LOCALE_SLONGDATE
;
break
;
case
FMT_DATE_DAY_WEEK
:
szPrintFmt
=
szPercent_d
;
if
(
pToken
[
1
])
dwVal
=
udate
.
st
.
wDayOfWeek
+
2
-
pToken
[
1
];
else
{
GetLocaleInfoW
(
lcid
,
LOCALE_RETURN_NUMBER
|
LOCALE_IFIRSTDAYOFWEEK
,
(
LPWSTR
)
&
dwVal
,
sizeof
(
dwVal
)
/
sizeof
(
WCHAR
));
dwVal
=
udate
.
st
.
wDayOfWeek
+
1
-
dwVal
;
}
pToken
++
;
break
;
case
FMT_DATE_WEEK_YEAR
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
wDayOfYear
/
7
+
1
;
pToken
+=
2
;
FIXME
(
"Ignoring nFirstDay of %d, nFirstWeek of %d
\n
"
,
pToken
[
0
],
pToken
[
1
]);
break
;
case
FMT_DATE_MON
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wMonth
;
break
;
case
FMT_DATE_MON_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wMonth
;
break
;
case
FMT_DATE_MON_SHORT
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
TRACE
(
"short month
\n
"
);
localeValue
=
LOCALE_SABBREVMONTHNAME1
+
udate
.
st
.
wMonth
-
1
;
defaultChar
=
'?'
;
break
;
case
FMT_DATE_MON_LONG
:
/* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
TRACE
(
"long month
\n
"
);
localeValue
=
LOCALE_SMONTHNAME1
+
udate
.
st
.
wMonth
-
1
;
defaultChar
=
'?'
;
break
;
case
FMT_DATE_YEAR_DOY
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
wDayOfYear
;
break
;
case
FMT_DATE_YEAR_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wYear
%
100
;
break
;
case
FMT_DATE_YEAR_LONG
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wYear
;
break
;
case
FMT_DATE_MIN
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wMinute
;
break
;
case
FMT_DATE_MIN_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wMinute
;
break
;
case
FMT_DATE_SEC
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wSecond
;
break
;
case
FMT_DATE_SEC_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wSecond
;
break
;
case
FMT_DATE_HOUR
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wHour
;
break
;
case
FMT_DATE_HOUR_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wHour
;
break
;
case
FMT_DATE_HOUR_12
:
szPrintFmt
=
szPercent_d
;
dwVal
=
udate
.
st
.
wHour
?
udate
.
st
.
wHour
>
12
?
udate
.
st
.
wHour
-
12
:
udate
.
st
.
wHour
:
12
;
break
;
case
FMT_DATE_HOUR_12_0
:
szPrintFmt
=
szPercentZeroTwo_d
;
dwVal
=
udate
.
st
.
wHour
?
udate
.
st
.
wHour
>
12
?
udate
.
st
.
wHour
-
12
:
udate
.
st
.
wHour
:
12
;
break
;
case
FMT_DATE_AMPM_SYS1
:
case
FMT_DATE_AMPM_SYS2
:
localeValue
=
udate
.
st
.
wHour
<
12
?
LOCALE_S1159
:
LOCALE_S2359
;
defaultChar
=
'?'
;
break
;
case
FMT_DATE_AMPM_UPPER
:
*
pBuff
++
=
udate
.
st
.
wHour
<
12
?
'A'
:
'P'
;
*
pBuff
++
=
'M'
;
break
;
case
FMT_DATE_A_UPPER
:
*
pBuff
++
=
udate
.
st
.
wHour
<
12
?
'A'
:
'P'
;
break
;
case
FMT_DATE_AMPM_LOWER
:
*
pBuff
++
=
udate
.
st
.
wHour
<
12
?
'a'
:
'p'
;
*
pBuff
++
=
'm'
;
break
;
case
FMT_DATE_A_LOWER
:
*
pBuff
++
=
udate
.
st
.
wHour
<
12
?
'a'
:
'p'
;
break
;
default:
ERR
(
"Unknown token 0x%02x!
\n
"
,
*
pToken
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatDate_Exit
;
break
;
}
if
(
localeValue
)
{
*
pBuff
=
'\0'
;
if
(
GetLocaleInfoW
(
lcid
,
localeValue
,
pBuff
,
sizeof
(
buff
)
/
sizeof
(
WCHAR
)
-
(
pBuff
-
buff
)))
{
TRACE
(
"added %s
\n
"
,
debugstr_w
(
pBuff
));
while
(
*
pBuff
)
pBuff
++
;
}
else
{
TRACE
(
"added %d %c
\n
"
,
defaultChar
,
defaultChar
);
*
pBuff
++
=
defaultChar
;
}
}
else
if
(
dwFmt
)
{
WCHAR
fmt_buff
[
80
];
if
(
!
GetLocaleInfoW
(
lcid
,
dwFmt
,
fmt_buff
,
sizeof
(
fmt_buff
)
/
sizeof
(
WCHAR
))
||
!
GetDateFormatW
(
lcid
,
0
,
&
udate
.
st
,
fmt_buff
,
pBuff
,
sizeof
(
buff
)
/
sizeof
(
WCHAR
)
-
(
pBuff
-
buff
)))
{
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatDate_Exit
;
}
while
(
*
pBuff
)
pBuff
++
;
}
else
if
(
szPrintFmt
)
{
sprintfW
(
pBuff
,
szPrintFmt
,
dwVal
);
while
(
*
pBuff
)
pBuff
++
;
}
pToken
++
;
}
VARIANT_FormatDate_Exit:
*
pBuff
=
'\0'
;
TRACE
(
"buff is %s
\n
"
,
debugstr_w
(
buff
));
if
(
SUCCEEDED
(
hRes
))
{
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRes
=
E_OUTOFMEMORY
;
}
return
hRes
;
}
/* Format a variant using a string format */
static
HRESULT
VARIANT_FormatString
(
LPVARIANT
pVarIn
,
LPOLESTR
lpszFormat
,
LPBYTE
rgbTok
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
,
LCID
lcid
)
{
static
const
WCHAR
szEmpty
[]
=
{
'\0'
};
WCHAR
buff
[
256
],
*
pBuff
=
buff
;
WCHAR
*
pSrc
;
FMT_HEADER
*
header
=
(
FMT_HEADER
*
)
rgbTok
;
FMT_STRING_HEADER
*
strHeader
;
const
BYTE
*
pToken
=
NULL
;
VARIANT
vStr
;
int
blanks_first
;
BOOL
bUpper
=
FALSE
;
HRESULT
hRes
=
S_OK
;
TRACE
(
"(%p->(%s%s),%s,%p,0x%08lx,%p,0x%08lx)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
debugstr_w
(
lpszFormat
),
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
V_VT
(
&
vStr
)
=
VT_EMPTY
;
if
(
V_TYPE
(
pVarIn
)
==
VT_EMPTY
||
V_TYPE
(
pVarIn
)
==
VT_NULL
)
{
strHeader
=
(
FMT_STRING_HEADER
*
)(
rgbTok
+
FmtGetNegative
(
header
));
V_BSTR
(
&
vStr
)
=
(
WCHAR
*
)
szEmpty
;
}
else
{
hRes
=
VariantChangeTypeEx
(
&
vStr
,
pVarIn
,
LCID_US
,
VARIANT_NOUSEROVERRIDE
,
VT_BSTR
);
if
(
FAILED
(
hRes
))
return
hRes
;
if
(
V_BSTR
(
pVarIn
)[
0
]
==
'\0'
)
strHeader
=
(
FMT_STRING_HEADER
*
)(
rgbTok
+
FmtGetNegative
(
header
));
else
strHeader
=
(
FMT_STRING_HEADER
*
)(
rgbTok
+
FmtGetPositive
(
header
));
}
pSrc
=
V_BSTR
(
&
vStr
);
if
((
strHeader
->
flags
&
(
FMT_FLAG_LT
|
FMT_FLAG_GT
))
==
FMT_FLAG_GT
)
bUpper
=
TRUE
;
blanks_first
=
strHeader
->
copy_chars
-
strlenW
(
pSrc
);
pToken
=
(
const
BYTE
*
)
strHeader
+
sizeof
(
FMT_DATE_HEADER
);
while
(
*
pToken
!=
FMT_GEN_END
)
{
int
dwCount
=
0
;
if
(
pToken
-
rgbTok
>
header
->
size
)
{
ERR
(
"Ran off the end of the format!
\n
"
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatString_Exit
;
}
switch
(
*
pToken
)
{
case
FMT_GEN_COPY
:
TRACE
(
"copy %s
\n
"
,
debugstr_wn
(
lpszFormat
+
pToken
[
1
],
pToken
[
2
]));
memcpy
(
pBuff
,
lpszFormat
+
pToken
[
1
],
pToken
[
2
]
*
sizeof
(
WCHAR
));
pBuff
+=
pToken
[
2
];
pToken
+=
2
;
break
;
case
FMT_STR_COPY_SPACE
:
case
FMT_STR_COPY_SKIP
:
dwCount
=
pToken
[
1
];
if
(
*
pToken
==
FMT_STR_COPY_SPACE
&&
blanks_first
>
0
)
{
TRACE
(
"insert %d initial spaces
\n
"
,
blanks_first
);
while
(
dwCount
>
0
&&
blanks_first
>
0
)
{
*
pBuff
++
=
' '
;
dwCount
--
;
blanks_first
--
;
}
}
TRACE
(
"copy %d chars%s
\n
"
,
dwCount
,
*
pToken
==
FMT_STR_COPY_SPACE
?
" with space"
:
""
);
while
(
dwCount
>
0
&&
*
pSrc
)
{
if
(
bUpper
)
*
pBuff
++
=
toupperW
(
*
pSrc
);
else
*
pBuff
++
=
tolowerW
(
*
pSrc
);
dwCount
--
;
pSrc
++
;
}
if
(
*
pToken
==
FMT_STR_COPY_SPACE
&&
dwCount
>
0
)
{
TRACE
(
"insert %d spaces
\n
"
,
dwCount
);
while
(
dwCount
--
>
0
)
*
pBuff
++
=
' '
;
}
pToken
++
;
break
;
default:
ERR
(
"Unknown token 0x%02x!
\n
"
,
*
pToken
);
hRes
=
E_INVALIDARG
;
goto
VARIANT_FormatString_Exit
;
break
;
}
pToken
++
;
}
VARIANT_FormatString_Exit:
/* Copy out any remaining chars */
while
(
*
pSrc
)
{
if
(
bUpper
)
*
pBuff
++
=
toupperW
(
*
pSrc
);
else
*
pBuff
++
=
tolowerW
(
*
pSrc
);
pSrc
++
;
}
VariantClear
(
&
vStr
);
*
pBuff
=
'\0'
;
TRACE
(
"buff is %s
\n
"
,
debugstr_w
(
buff
));
if
(
SUCCEEDED
(
hRes
))
{
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRes
=
E_OUTOFMEMORY
;
}
return
hRes
;
}
#define NUMBER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2| \
VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8| \
VTBIT_R4|VTBIT_R8|VTBIT_CY|VTBIT_DECIMAL| \
(1<<VT_BOOL)|(1<<VT_INT)|(1<<VT_UINT))
/**********************************************************************
* VarFormatFromTokens [OLEAUT32.139]
*/
HRESULT
WINAPI
VarFormatFromTokens
(
LPVARIANT
pVarIn
,
LPOLESTR
lpszFormat
,
LPBYTE
rgbTok
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
,
LCID
lcid
)
{
FMT_SHORT_HEADER
*
header
=
(
FMT_SHORT_HEADER
*
)
rgbTok
;
VARIANT
vTmp
;
HRESULT
hres
;
TRACE
(
"(%p,%s,%p,%lx,%p,0x%08lx)
\n
"
,
pVarIn
,
debugstr_w
(
lpszFormat
),
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
if
(
!
pbstrOut
)
return
E_INVALIDARG
;
*
pbstrOut
=
NULL
;
if
(
!
pVarIn
||
!
rgbTok
)
return
E_INVALIDARG
;
if
(
*
rgbTok
==
FMT_TO_STRING
||
header
->
type
==
FMT_TYPE_GENERAL
)
{
/* According to MSDN, general format acts somewhat like the 'Str'
* function in Visual Basic.
*/
VarFormatFromTokens_AsStr:
V_VT
(
&
vTmp
)
=
VT_EMPTY
;
hres
=
VariantChangeTypeEx
(
&
vTmp
,
pVarIn
,
lcid
,
dwFlags
,
VT_BSTR
);
*
pbstrOut
=
V_BSTR
(
&
vTmp
);
}
else
{
if
(
header
->
type
==
FMT_TYPE_NUMBER
||
(
header
->
type
==
FMT_TYPE_UNKNOWN
&&
((
1
<<
V_TYPE
(
pVarIn
))
&
NUMBER_VTBITS
)))
{
hres
=
VARIANT_FormatNumber
(
pVarIn
,
lpszFormat
,
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
}
else
if
(
header
->
type
==
FMT_TYPE_DATE
||
(
header
->
type
==
FMT_TYPE_UNKNOWN
&&
V_TYPE
(
pVarIn
)
==
VT_DATE
))
{
hres
=
VARIANT_FormatDate
(
pVarIn
,
lpszFormat
,
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
}
else
if
(
header
->
type
==
FMT_TYPE_STRING
||
V_TYPE
(
pVarIn
)
==
VT_BSTR
)
{
hres
=
VARIANT_FormatString
(
pVarIn
,
lpszFormat
,
rgbTok
,
dwFlags
,
pbstrOut
,
lcid
);
}
else
{
ERR
(
"unrecognised format type 0x%02x
\n
"
,
header
->
type
);
return
E_INVALIDARG
;
}
/* If the coercion failed, still try to create output, unless the
* VAR_FORMAT_NOSUBSTITUTE flag is set.
*/
if
((
hres
==
DISP_E_OVERFLOW
||
hres
==
DISP_E_TYPEMISMATCH
)
&&
!
(
dwFlags
&
VAR_FORMAT_NOSUBSTITUTE
))
goto
VarFormatFromTokens_AsStr
;
}
return
hres
;
}
/**********************************************************************
* VarFormat [OLEAUT32.87]
*
* Format a variant from a format string.
*
* PARAMS
* pVarIn [I] Variant to format
* lpszFormat [I] Format string (see notes)
* nFirstDay [I] First day of the week, (See VarTokenizeFormatString() for details)
* nFirstWeek [I] First week of the year (See VarTokenizeFormatString() for details)
* dwFlags [I] Flags for the format (VAR_ flags from "oleauto.h")
* pbstrOut [O] Destination for formatted string.
*
* RETURNS
* Success: S_OK. pbstrOut contains the formatted value.
* Failure: E_INVALIDARG, if any parameter is invalid.
* E_OUTOFMEMORY, if enough memory cannot be allocated.
* DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
*
* NOTES
* - See Variant-Formats for details concerning creating format strings.
* - This function uses LOCALE_USER_DEFAULT when calling VarTokenizeFormatString()
* and VarFormatFromTokens().
*/
HRESULT
WINAPI
VarFormat
(
LPVARIANT
pVarIn
,
LPOLESTR
lpszFormat
,
int
nFirstDay
,
int
nFirstWeek
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
BYTE
buff
[
256
];
HRESULT
hres
;
TRACE
(
"(%p->(%s%s),%s,%d,%d,0x%08lx,%p)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
debugstr_w
(
lpszFormat
),
nFirstDay
,
nFirstWeek
,
dwFlags
,
pbstrOut
);
if
(
!
pbstrOut
)
return
E_INVALIDARG
;
*
pbstrOut
=
NULL
;
hres
=
VarTokenizeFormatString
(
lpszFormat
,
buff
,
sizeof
(
buff
),
nFirstDay
,
nFirstWeek
,
LOCALE_USER_DEFAULT
,
NULL
);
if
(
SUCCEEDED
(
hres
))
hres
=
VarFormatFromTokens
(
pVarIn
,
lpszFormat
,
buff
,
dwFlags
,
pbstrOut
,
LOCALE_USER_DEFAULT
);
TRACE
(
"returning 0x%08lx, %s
\n
"
,
hres
,
debugstr_w
(
*
pbstrOut
));
return
hres
;
}
/**********************************************************************
* VarFormatDateTime [OLEAUT32.97]
*
* Format a variant value as a date and/or time.
*
* PARAMS
* pVarIn [I] Variant to format
* nFormat [I] Format type (see notes)
* dwFlags [I] Flags for the format (VAR_ flags from "oleauto.h")
* pbstrOut [O] Destination for formatted string.
*
* RETURNS
* Success: S_OK. pbstrOut contains the formatted value.
* Failure: E_INVALIDARG, if any parameter is invalid.
* E_OUTOFMEMORY, if enough memory cannot be allocated.
* DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
*
* NOTES
* This function uses LOCALE_USER_DEFAULT when determining the date format
* characters to use.
* Possible values for the nFormat parameter are:
*| Value Meaning
*| ----- -------
*| 0 General date format
*| 1 Long date format
*| 2 Short date format
*| 3 Long time format
*| 4 Short time format
*/
HRESULT
WINAPI
VarFormatDateTime
(
LPVARIANT
pVarIn
,
INT
nFormat
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
static
const
WCHAR
szEmpty
[]
=
{
'\0'
};
const
BYTE
*
lpFmt
=
NULL
;
TRACE
(
"(%p->(%s%s),%d,0x%08lx,%p)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
nFormat
,
dwFlags
,
pbstrOut
);
if
(
!
pVarIn
||
!
pbstrOut
||
nFormat
<
0
||
nFormat
>
4
)
return
E_INVALIDARG
;
switch
(
nFormat
)
{
case
0
:
lpFmt
=
fmtGeneralDate
;
break
;
case
1
:
lpFmt
=
fmtLongDate
;
break
;
case
2
:
lpFmt
=
fmtShortDate
;
break
;
case
3
:
lpFmt
=
fmtLongTime
;
break
;
case
4
:
lpFmt
=
fmtShortTime
;
break
;
}
return
VarFormatFromTokens
(
pVarIn
,
(
LPWSTR
)
szEmpty
,
(
BYTE
*
)
lpFmt
,
dwFlags
,
pbstrOut
,
LOCALE_USER_DEFAULT
);
}
#define GETLOCALENUMBER(type,field) GetLocaleInfoW(LOCALE_USER_DEFAULT, \
type|LOCALE_RETURN_NUMBER, \
(LPWSTR)&numfmt.field, \
sizeof(numfmt.field)/sizeof(WCHAR))
/**********************************************************************
* VarFormatNumber [OLEAUT32.107]
*
* Format a variant value as a number.
*
* PARAMS
* pVarIn [I] Variant to format
* nDigits [I] Number of digits following the decimal point (-1 = user default)
* nLeading [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
* nParens [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
* nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
* dwFlags [I] Currently unused, set to zero
* pbstrOut [O] Destination for formatted string.
*
* RETURNS
* Success: S_OK. pbstrOut contains the formatted value.
* Failure: E_INVALIDARG, if any parameter is invalid.
* E_OUTOFMEMORY, if enough memory cannot be allocated.
* DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
*
* NOTES
* This function uses LOCALE_USER_DEFAULT when determining the number format
* characters to use.
*/
HRESULT
WINAPI
VarFormatNumber
(
LPVARIANT
pVarIn
,
INT
nDigits
,
INT
nLeading
,
INT
nParens
,
INT
nGrouping
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
HRESULT
hRet
;
VARIANT
vStr
;
TRACE
(
"(%p->(%s%s),%d,%d,%d,%d,0x%08lx,%p)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
nDigits
,
nLeading
,
nParens
,
nGrouping
,
dwFlags
,
pbstrOut
);
if
(
!
pVarIn
||
!
pbstrOut
||
nDigits
>
9
)
return
E_INVALIDARG
;
*
pbstrOut
=
NULL
;
V_VT
(
&
vStr
)
=
VT_EMPTY
;
hRet
=
VariantCopyInd
(
&
vStr
,
pVarIn
);
if
(
SUCCEEDED
(
hRet
))
hRet
=
VariantChangeTypeEx
(
&
vStr
,
&
vStr
,
LOCALE_USER_DEFAULT
,
0
,
VT_BSTR
);
if
(
SUCCEEDED
(
hRet
))
{
WCHAR
buff
[
256
],
decimal
[
8
],
thousands
[
8
];
NUMBERFMTW
numfmt
;
/* Although MSDN makes it clear that the native versions of these functions
* are implemented using VarTokenizeFormatString()/VarFormatFromTokens(),
* using NLS gives us the same result.
*/
if
(
nDigits
<
0
)
GETLOCALENUMBER
(
LOCALE_IDIGITS
,
NumDigits
);
else
numfmt
.
NumDigits
=
nDigits
;
if
(
nLeading
==
-
2
)
GETLOCALENUMBER
(
LOCALE_ILZERO
,
LeadingZero
);
else
if
(
nLeading
==
-
1
)
numfmt
.
LeadingZero
=
1
;
else
numfmt
.
LeadingZero
=
0
;
if
(
nGrouping
==
-
2
)
{
WCHAR
grouping
[
16
];
grouping
[
2
]
=
'\0'
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
grouping
,
sizeof
(
grouping
)
/
sizeof
(
WCHAR
));
numfmt
.
Grouping
=
grouping
[
2
]
==
'2'
?
32
:
grouping
[
0
]
-
'0'
;
}
else
if
(
nGrouping
==
-
1
)
numfmt
.
Grouping
=
3
;
/* 3 = "n,nnn.nn" */
else
numfmt
.
Grouping
=
0
;
/* 0 = No grouping */
if
(
nParens
==
-
2
)
GETLOCALENUMBER
(
LOCALE_INEGNUMBER
,
NegativeOrder
);
else
if
(
nParens
==
-
1
)
numfmt
.
NegativeOrder
=
0
;
/* 0 = "(xxx)" */
else
numfmt
.
NegativeOrder
=
1
;
/* 1 = "-xxx" */
numfmt
.
lpDecimalSep
=
decimal
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
decimal
,
sizeof
(
decimal
)
/
sizeof
(
WCHAR
));
numfmt
.
lpThousandSep
=
thousands
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
thousands
,
sizeof
(
thousands
)
/
sizeof
(
WCHAR
));
if
(
GetNumberFormatW
(
LOCALE_USER_DEFAULT
,
0
,
V_BSTR
(
&
vStr
),
&
numfmt
,
buff
,
sizeof
(
buff
)
/
sizeof
(
WCHAR
)))
{
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRet
=
E_OUTOFMEMORY
;
}
else
hRet
=
DISP_E_TYPEMISMATCH
;
SysFreeString
(
V_BSTR
(
&
vStr
));
}
return
hRet
;
}
/**********************************************************************
* VarFormatPercent [OLEAUT32.117]
*
* Format a variant value as a percentage.
*
* PARAMS
* pVarIn [I] Variant to format
* nDigits [I] Number of digits following the decimal point (-1 = user default)
* nLeading [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
* nParens [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
* nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
* dwFlags [I] Currently unused, set to zero
* pbstrOut [O] Destination for formatted string.
*
* RETURNS
* Success: S_OK. pbstrOut contains the formatted value.
* Failure: E_INVALIDARG, if any parameter is invalid.
* E_OUTOFMEMORY, if enough memory cannot be allocated.
* DISP_E_OVERFLOW, if overflow occurs during the conversion.
* DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
*
* NOTES
* This function uses LOCALE_USER_DEFAULT when determining the number format
* characters to use.
*/
HRESULT
WINAPI
VarFormatPercent
(
LPVARIANT
pVarIn
,
INT
nDigits
,
INT
nLeading
,
INT
nParens
,
INT
nGrouping
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
static
const
WCHAR
szPercent
[]
=
{
'%'
,
'\0'
};
static
const
WCHAR
szPercentBracket
[]
=
{
'%'
,
')'
,
'\0'
};
WCHAR
buff
[
256
];
HRESULT
hRet
;
VARIANT
vDbl
;
TRACE
(
"(%p->(%s%s),%d,%d,%d,%d,0x%08lx,%p)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
nDigits
,
nLeading
,
nParens
,
nGrouping
,
dwFlags
,
pbstrOut
);
if
(
!
pVarIn
||
!
pbstrOut
||
nDigits
>
9
)
return
E_INVALIDARG
;
*
pbstrOut
=
NULL
;
V_VT
(
&
vDbl
)
=
VT_EMPTY
;
hRet
=
VariantCopyInd
(
&
vDbl
,
pVarIn
);
if
(
SUCCEEDED
(
hRet
))
{
hRet
=
VariantChangeTypeEx
(
&
vDbl
,
&
vDbl
,
LOCALE_USER_DEFAULT
,
0
,
VT_R8
);
if
(
SUCCEEDED
(
hRet
))
{
if
(
V_R8
(
&
vDbl
)
>
(
R8_MAX
/
100
.
0
))
return
DISP_E_OVERFLOW
;
V_R8
(
&
vDbl
)
*=
100
.
0
;
hRet
=
VarFormatNumber
(
&
vDbl
,
nDigits
,
nLeading
,
nParens
,
nGrouping
,
dwFlags
,
pbstrOut
);
if
(
SUCCEEDED
(
hRet
))
{
DWORD
dwLen
=
strlenW
(
*
pbstrOut
);
BOOL
bBracket
=
(
*
pbstrOut
)[
dwLen
]
==
')'
?
TRUE
:
FALSE
;
dwLen
-=
bBracket
;
memcpy
(
buff
,
*
pbstrOut
,
dwLen
*
sizeof
(
WCHAR
));
strcpyW
(
buff
+
dwLen
,
bBracket
?
szPercentBracket
:
szPercent
);
SysFreeString
(
*
pbstrOut
);
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRet
=
E_OUTOFMEMORY
;
}
}
}
return
hRet
;
}
/**********************************************************************
* VarFormatCurrency [OLEAUT32.127]
*
* Format a variant value as a currency.
*
* PARAMS
* pVarIn [I] Variant to format
* nDigits [I] Number of digits following the decimal point (-1 = user default)
* nLeading [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
* nParens [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
* nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
* dwFlags [I] Currently unused, set to zero
* pbstrOut [O] Destination for formatted string.
*
* RETURNS
* Success: S_OK. pbstrOut contains the formatted value.
* Failure: E_INVALIDARG, if any parameter is invalid.
* E_OUTOFMEMORY, if enough memory cannot be allocated.
* DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
*
* NOTES
* This function uses LOCALE_USER_DEFAULT when determining the currency format
* characters to use.
*/
HRESULT
WINAPI
VarFormatCurrency
(
LPVARIANT
pVarIn
,
INT
nDigits
,
INT
nLeading
,
INT
nParens
,
INT
nGrouping
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
HRESULT
hRet
;
VARIANT
vStr
;
TRACE
(
"(%p->(%s%s),%d,%d,%d,%d,0x%08lx,%p)
\n
"
,
pVarIn
,
debugstr_VT
(
pVarIn
),
debugstr_VF
(
pVarIn
),
nDigits
,
nLeading
,
nParens
,
nGrouping
,
dwFlags
,
pbstrOut
);
if
(
!
pVarIn
||
!
pbstrOut
||
nDigits
>
9
)
return
E_INVALIDARG
;
*
pbstrOut
=
NULL
;
V_VT
(
&
vStr
)
=
VT_EMPTY
;
hRet
=
VariantCopyInd
(
&
vStr
,
pVarIn
);
if
(
SUCCEEDED
(
hRet
))
hRet
=
VariantChangeTypeEx
(
&
vStr
,
&
vStr
,
LOCALE_USER_DEFAULT
,
0
,
VT_BSTR
);
if
(
SUCCEEDED
(
hRet
))
{
WCHAR
buff
[
256
],
decimal
[
8
],
thousands
[
8
],
currency
[
8
];
CURRENCYFMTW
numfmt
;
if
(
nDigits
<
0
)
GETLOCALENUMBER
(
LOCALE_IDIGITS
,
NumDigits
);
else
numfmt
.
NumDigits
=
nDigits
;
if
(
nLeading
==
-
2
)
GETLOCALENUMBER
(
LOCALE_ILZERO
,
LeadingZero
);
else
if
(
nLeading
==
-
1
)
numfmt
.
LeadingZero
=
1
;
else
numfmt
.
LeadingZero
=
0
;
if
(
nGrouping
==
-
2
)
{
WCHAR
nGrouping
[
16
];
nGrouping
[
2
]
=
'\0'
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
nGrouping
,
sizeof
(
nGrouping
)
/
sizeof
(
WCHAR
));
numfmt
.
Grouping
=
nGrouping
[
2
]
==
'2'
?
32
:
nGrouping
[
0
]
-
'0'
;
}
else
if
(
nGrouping
==
-
1
)
numfmt
.
Grouping
=
3
;
/* 3 = "n,nnn.nn" */
else
numfmt
.
Grouping
=
0
;
/* 0 = No grouping */
if
(
nParens
==
-
2
)
GETLOCALENUMBER
(
LOCALE_INEGCURR
,
NegativeOrder
);
else
if
(
nParens
==
-
1
)
numfmt
.
NegativeOrder
=
0
;
/* 0 = "(xxx)" */
else
numfmt
.
NegativeOrder
=
1
;
/* 1 = "-xxx" */
GETLOCALENUMBER
(
LOCALE_ICURRENCY
,
PositiveOrder
);
numfmt
.
lpDecimalSep
=
decimal
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
decimal
,
sizeof
(
decimal
)
/
sizeof
(
WCHAR
));
numfmt
.
lpThousandSep
=
thousands
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
thousands
,
sizeof
(
thousands
)
/
sizeof
(
WCHAR
));
numfmt
.
lpCurrencySymbol
=
currency
;
GetLocaleInfoW
(
LOCALE_USER_DEFAULT
,
LOCALE_SDECIMAL
,
currency
,
sizeof
(
currency
)
/
sizeof
(
WCHAR
));
/* use NLS as per VarFormatNumber() */
if
(
GetCurrencyFormatW
(
LOCALE_USER_DEFAULT
,
0
,
V_BSTR
(
&
vStr
),
&
numfmt
,
buff
,
sizeof
(
buff
)
/
sizeof
(
WCHAR
)))
{
*
pbstrOut
=
SysAllocString
(
buff
);
if
(
!*
pbstrOut
)
hRet
=
E_OUTOFMEMORY
;
}
else
hRet
=
DISP_E_TYPEMISMATCH
;
SysFreeString
(
V_BSTR
(
&
vStr
));
}
return
hRet
;
}
dlls/oleaut32/variant.c
View file @
d537c955
...
...
@@ -66,13 +66,40 @@
WINE_DEFAULT_DEBUG_CHANNEL
(
ole
);
#define SYSDUPSTRING(str) SysAllocStringByteLen((LPCSTR)(str), SysStringByteLen(str))
const
char
*
wine_vtypes
[
VT_CLSID
]
=
{
"VT_EMPTY"
,
"VT_NULL"
,
"VT_I2"
,
"VT_I4"
,
"VT_R4"
,
"VT_R8"
,
"VT_CY"
,
"VT_DATE"
,
"VT_BSTR"
,
"VT_DISPATCH"
,
"VT_ERROR"
,
"VT_BOOL"
,
"VT_VARIANT"
,
"VT_UNKNOWN"
,
"VT_DECIMAL"
,
"15"
,
"VT_I1"
,
"VT_UI1"
,
"VT_UI2"
,
"VT_UI4"
,
"VT_I8"
,
"VT_UI8"
,
"VT_INT"
,
"VT_UINT"
,
"VT_VOID"
,
"VT_HRESULT"
,
"VT_PTR"
,
"VT_SAFEARRAY"
,
"VT_CARRAY"
,
"VT_USERDEFINED"
,
"VT_LPSTR"
,
"VT_LPWSTR""32"
,
"33"
,
"34"
,
"35"
,
"VT_RECORD"
,
"VT_INT_PTR"
,
"VT_UINT_PTR"
,
"39"
,
"40"
,
"41"
,
"42"
,
"43"
,
"44"
,
"45"
,
"46"
,
"47"
,
"48"
,
"49"
,
"50"
,
"51"
,
"52"
,
"53"
,
"54"
,
"55"
,
"56"
,
"57"
,
"58"
,
"59"
,
"60"
,
"61"
,
"62"
,
"63"
,
"VT_FILETIME"
,
"VT_BLOB"
,
"VT_STREAM"
,
"VT_STORAGE"
,
"VT_STREAMED_OBJECT"
,
"VT_STORED_OBJECT"
,
"VT_BLOB_OBJECT"
,
"VT_CF"
,
"VT_CLSID"
};
/* Flags set in V_VT, other than the actual type value */
#define VT_EXTRA_TYPE (VT_VECTOR|VT_ARRAY|VT_BYREF|VT_RESERVED)
const
char
*
wine_vflags
[
16
]
=
{
""
,
"|VT_VECTOR"
,
"|VT_ARRAY"
,
"|VT_VECTOR|VT_ARRAY"
,
"|VT_BYREF"
,
"|VT_VECTOR|VT_ARRAY"
,
"|VT_ARRAY|VT_BYREF"
,
"|VT_VECTOR|VT_ARRAY|VT_BYREF"
,
"|VT_HARDTYPE"
,
"|VT_VECTOR|VT_HARDTYPE"
,
"|VT_ARRAY|VT_HARDTYPE"
,
"|VT_VECTOR|VT_ARRAY|VT_HARDTYPE"
,
"|VT_BYREF|VT_HARDTYPE"
,
"|VT_VECTOR|VT_ARRAY|VT_HARDTYPE"
,
"|VT_ARRAY|VT_BYREF|VT_HARDTYPE"
,
"|VT_VECTOR|VT_ARRAY|VT_BYREF|VT_HARDTYPE"
,
};
/* Get the extra flags from a variant pointer */
#define V_EXTRA_TYPE(v) (V_VT(v) & VT_EXTRA_TYPE)
#define SYSDUPSTRING(str) SysAllocStringByteLen((LPCSTR)(str), SysStringByteLen(str))
/* the largest valid type
*/
...
...
@@ -101,100 +128,6 @@ static char pBuffer[BUFFER_MAX];
*/
static
const
double
DAYS_IN_ONE_YEAR
=
365
.
0
;
/*
* Token definitions for Varient Formatting
* Worked out by experimentation on a w2k machine. Doesnt appear to be
* documented anywhere obviously so keeping definitions internally
*
*/
/* Pre defined tokens */
#define TOK_COPY 0x00
#define TOK_END 0x02
#define LARGEST_TOKENID 6
/* Mapping of token name to id put into the tokenized form
Note testing on W2K shows aaaa and oooo are not parsed??!! */
#define TOK_COLON 0x03
#define TOK_SLASH 0x04
#define TOK_c 0x05
#define TOK_d 0x08
#define TOK_dd 0x09
#define TOK_ddd 0x0a
#define TOK_dddd 0x0b
#define TOK_ddddd 0x0c
#define TOK_dddddd 0x0d
#define TOK_w 0x0f
#define TOK_ww 0x10
#define TOK_m 0x11
#define TOK_mm 0x12
#define TOK_mmm 0x13
#define TOK_mmmm 0x14
#define TOK_q 0x06
#define TOK_y 0x15
#define TOK_yy 0x16
#define TOK_yyyy 0x18
#define TOK_h 0x1e
#define TOK_Hh 0x1f
#define TOK_N 0x1a
#define TOK_Nn 0x1b
#define TOK_S 0x1c
#define TOK_Ss 0x1d
#define TOK_ttttt 0x07
#define TOK_AMsPM 0x2f
#define TOK_amspm 0x32
#define TOK_AsP 0x30
#define TOK_asp 0x33
#define TOK_AMPM 0x2e
typedef
struct
tagFORMATTOKEN
{
const
char
*
str
;
BYTE
tokenSize
;
BYTE
tokenId
;
int
varTypeRequired
;
}
FORMATTOKEN
;
typedef
struct
tagFORMATHDR
{
BYTE
len
;
BYTE
hex3
;
BYTE
hex6
;
BYTE
reserved
[
8
];
}
FORMATHDR
;
FORMATTOKEN
formatTokens
[]
=
{
/* FIXME: Only date formats so far */
{
":"
,
1
,
TOK_COLON
,
0
},
{
"/"
,
1
,
TOK_SLASH
,
0
},
{
"c"
,
1
,
TOK_c
,
VT_DATE
},
{
"dddddd"
,
6
,
TOK_dddddd
,
VT_DATE
},
{
"ddddd"
,
5
,
TOK_ddddd
,
VT_DATE
},
{
"dddd"
,
4
,
TOK_dddd
,
VT_DATE
},
{
"ddd"
,
3
,
TOK_ddd
,
VT_DATE
},
{
"dd"
,
2
,
TOK_dd
,
VT_DATE
},
{
"d"
,
1
,
TOK_d
,
VT_DATE
},
{
"ww"
,
2
,
TOK_ww
,
VT_DATE
},
{
"w"
,
1
,
TOK_w
,
VT_DATE
},
{
"mmmm"
,
4
,
TOK_mmmm
,
VT_DATE
},
{
"mmm"
,
3
,
TOK_mmm
,
VT_DATE
},
{
"mm"
,
2
,
TOK_mm
,
VT_DATE
},
{
"m"
,
1
,
TOK_m
,
VT_DATE
},
{
"q"
,
1
,
TOK_q
,
VT_DATE
},
{
"yyyy"
,
4
,
TOK_yyyy
,
VT_DATE
},
{
"yy"
,
2
,
TOK_yy
,
VT_DATE
},
{
"y"
,
1
,
TOK_y
,
VT_DATE
},
{
"h"
,
1
,
TOK_h
,
VT_DATE
},
{
"Hh"
,
2
,
TOK_Hh
,
VT_DATE
},
{
"Nn"
,
2
,
TOK_Nn
,
VT_DATE
},
{
"N"
,
1
,
TOK_N
,
VT_DATE
},
{
"S"
,
1
,
TOK_S
,
VT_DATE
},
{
"Ss"
,
2
,
TOK_Ss
,
VT_DATE
},
{
"ttttt"
,
5
,
TOK_ttttt
,
VT_DATE
},
{
"AM/PM"
,
5
,
TOK_AMsPM
,
VT_DATE
},
{
"am/pm"
,
5
,
TOK_amspm
,
VT_DATE
},
{
"A/P"
,
3
,
TOK_AsP
,
VT_DATE
},
{
"a/p"
,
3
,
TOK_asp
,
VT_DATE
},
{
"AMPM"
,
4
,
TOK_AMPM
,
VT_DATE
},
{
0x00
,
0
,
0
,
VT_NULL
}
};
/******************************************************************************
* DateTimeStringToTm [INTERNAL]
*
...
...
@@ -1360,7 +1293,12 @@ static HRESULT Coerce( VARIANTARG* pd, LCID lcid, ULONG dwFlags, VARIANTARG* ps,
res
=
VarBstrFromDate
(
V_UNION
(
ps
,
date
),
lcid
,
0
,
&
V_UNION
(
pd
,
bstrVal
)
);
break
;
case
(
VT_BOOL
):
res
=
VarBstrFromBool
(
V_UNION
(
ps
,
boolVal
),
lcid
,
0
,
&
V_UNION
(
pd
,
bstrVal
)
);
if
(
dwFlags
&
VARIANT_ALPHABOOL
)
res
=
VarBstrFromBool
(
V_BOOL
(
ps
),
lcid
,
0
,
&
V_BSTR
(
pd
));
else
if
(
dwFlags
&
VARIANT_LOCALBOOL
)
res
=
VarBstrFromBool
(
V_BOOL
(
ps
),
lcid
,
VAR_LOCALBOOL
,
&
V_BSTR
(
pd
));
else
res
=
VarBstrFromI2
(
V_BOOL
(
ps
),
lcid
,
dwFlags
,
&
V_BSTR
(
pd
));
break
;
case
(
VT_BSTR
):
res
=
VariantCopy
(
pd
,
ps
);
...
...
@@ -4545,7 +4483,7 @@ HRESULT WINAPI VarDateFromUdate(UDATE *pUdateIn, ULONG dwFlags, DATE *pDateOut)
* PARAMS
* datein [I] Variant VT_DATE format date
* dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
* lpUdate [O] Destination for unpacked format date and time
* lpUdate
[O] Destination for unpacked format date and time
*
* RETURNS
* Success: S_OK. *lpUdate contains the converted value.
...
...
@@ -5390,24 +5328,6 @@ HRESULT WINAPI VarNumFromParseNum(NUMPARSE *pNumprs, BYTE *rgbDig,
}
/**********************************************************************
* VarFormatDateTime [OLEAUT32.97]
*/
HRESULT
WINAPI
VarFormatDateTime
(
LPVARIANT
var
,
INT
format
,
ULONG
dwFlags
,
BSTR
*
out
)
{
FIXME
(
"%p %d %lx %p
\n
"
,
var
,
format
,
dwFlags
,
out
);
return
E_NOTIMPL
;
}
/**********************************************************************
* VarFormatCurrency [OLEAUT32.127]
*/
HRESULT
WINAPI
VarFormatCurrency
(
LPVARIANT
var
,
INT
digits
,
INT
lead
,
INT
paren
,
INT
group
,
ULONG
dwFlags
,
BSTR
*
out
)
{
FIXME
(
"%p %d %d %d %d %lx %p
\n
"
,
var
,
digits
,
lead
,
paren
,
group
,
dwFlags
,
out
);
return
E_NOTIMPL
;
}
/**********************************************************************
* VarBstrCmp [OLEAUT32.314]
*
* flags can be:
...
...
@@ -5562,7 +5482,7 @@ HRESULT WINAPI VarCmp(LPVARIANT left, LPVARIANT right, LCID lcid, DWORD flags)
if
(
FAILED
(
rc
))
return
rc
;
rc
=
VariantChangeType
(
&
rv
,
right
,
0
,
VT_R8
);
if
(
FAILED
(
rc
))
return
rc
;
if
(
V_R8
(
&
lv
)
==
V_R8
(
&
rv
))
return
VARCMP_EQ
;
if
(
V_R8
(
&
lv
)
<
V_R8
(
&
rv
))
return
VARCMP_LT
;
if
(
V_R8
(
&
lv
)
>
V_R8
(
&
rv
))
return
VARCMP_GT
;
...
...
@@ -5573,7 +5493,7 @@ HRESULT WINAPI VarCmp(LPVARIANT left, LPVARIANT right, LCID lcid, DWORD flags)
if
(
FAILED
(
rc
))
return
rc
;
rc
=
VariantChangeType
(
&
rv
,
right
,
0
,
VT_R4
);
if
(
FAILED
(
rc
))
return
rc
;
if
(
V_R4
(
&
lv
)
==
V_R4
(
&
rv
))
return
VARCMP_EQ
;
if
(
V_R4
(
&
lv
)
<
V_R4
(
&
rv
))
return
VARCMP_LT
;
if
(
V_R4
(
&
lv
)
>
V_R4
(
&
rv
))
return
VARCMP_GT
;
...
...
@@ -6168,437 +6088,6 @@ HRESULT WINAPI VarNot(LPVARIANT in, LPVARIANT result)
}
/**********************************************************************
* VarTokenizeFormatString [OLEAUT32.140]
*
* From investigation on W2K, a list is built up which is:
*
* <0x00> AA BB - Copy from AA for BB chars (Note 1 byte with wrap!)
* <token> - Insert appropriate token
*
*/
HRESULT
WINAPI
VarTokenizeFormatString
(
LPOLESTR
format
,
LPBYTE
rgbTok
,
int
cbTok
,
int
iFirstDay
,
int
iFirstWeek
,
LCID
lcid
,
int
*
pcbActual
)
{
FORMATHDR
*
hdr
;
int
realLen
,
formatLeft
;
BYTE
*
pData
;
LPSTR
pFormatA
,
pStart
;
int
checkStr
;
BOOL
insertCopy
=
FALSE
;
LPSTR
copyFrom
=
NULL
;
TRACE
(
"'%s', %p %d %d %d only date support
\n
"
,
debugstr_w
(
format
),
rgbTok
,
cbTok
,
iFirstDay
,
iFirstWeek
);
/* Big enough for header? */
if
(
cbTok
<
sizeof
(
FORMATHDR
))
{
return
TYPE_E_BUFFERTOOSMALL
;
}
/* Insert header */
hdr
=
(
FORMATHDR
*
)
rgbTok
;
memset
(
hdr
,
0x00
,
sizeof
(
FORMATHDR
));
hdr
->
hex3
=
0x03
;
/* No idea what these are */
hdr
->
hex6
=
0x06
;
/* Start parsing string */
realLen
=
sizeof
(
FORMATHDR
);
pData
=
rgbTok
+
realLen
;
pFormatA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
format
);
pStart
=
pFormatA
;
formatLeft
=
strlen
(
pFormatA
);
/* Work through the format */
while
(
*
pFormatA
!=
0x00
)
{
checkStr
=
0
;
while
(
checkStr
>=
0
&&
(
formatTokens
[
checkStr
].
tokenSize
!=
0x00
))
{
if
(
formatLeft
>=
formatTokens
[
checkStr
].
tokenSize
&&
strncmp
(
formatTokens
[
checkStr
].
str
,
pFormatA
,
formatTokens
[
checkStr
].
tokenSize
)
==
0
)
{
TRACE
(
"match on '%s'
\n
"
,
formatTokens
[
checkStr
].
str
);
/* Found Match! */
/* If we have skipped chars, insert the copy */
if
(
insertCopy
==
TRUE
)
{
if
((
realLen
+
3
)
>
cbTok
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
TYPE_E_BUFFERTOOSMALL
;
}
insertCopy
=
FALSE
;
*
pData
=
TOK_COPY
;
pData
++
;
*
pData
=
(
BYTE
)(
copyFrom
-
pStart
);
pData
++
;
*
pData
=
(
BYTE
)(
pFormatA
-
copyFrom
);
pData
++
;
realLen
=
realLen
+
3
;
}
/* Now insert the token itself */
if
((
realLen
+
1
)
>
cbTok
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
TYPE_E_BUFFERTOOSMALL
;
}
*
pData
=
formatTokens
[
checkStr
].
tokenId
;
pData
=
pData
+
1
;
realLen
=
realLen
+
1
;
pFormatA
=
pFormatA
+
formatTokens
[
checkStr
].
tokenSize
;
formatLeft
=
formatLeft
-
formatTokens
[
checkStr
].
tokenSize
;
checkStr
=
-
1
;
/* Flag as found and break out of while loop */
}
else
{
checkStr
++
;
}
}
/* Did we ever match a token? */
if
(
checkStr
!=
-
1
&&
insertCopy
==
FALSE
)
{
TRACE
(
"No match - need to insert copy from %p [%p]
\n
"
,
pFormatA
,
pStart
);
insertCopy
=
TRUE
;
copyFrom
=
pFormatA
;
}
else
if
(
checkStr
!=
-
1
)
{
pFormatA
=
pFormatA
+
1
;
}
}
/* Finally, if we have skipped chars, insert the copy */
if
(
insertCopy
==
TRUE
)
{
TRACE
(
"Chars left over, so still copy %p,%p,%p
\n
"
,
copyFrom
,
pStart
,
pFormatA
);
if
((
realLen
+
3
)
>
cbTok
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
TYPE_E_BUFFERTOOSMALL
;
}
insertCopy
=
FALSE
;
*
pData
=
TOK_COPY
;
pData
++
;
*
pData
=
(
BYTE
)(
copyFrom
-
pStart
);
pData
++
;
*
pData
=
(
BYTE
)(
pFormatA
-
copyFrom
);
pData
++
;
realLen
=
realLen
+
3
;
}
/* Finally insert the terminator */
if
((
realLen
+
1
)
>
cbTok
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
TYPE_E_BUFFERTOOSMALL
;
}
*
pData
++
=
TOK_END
;
realLen
=
realLen
+
1
;
/* Finally fill in the length */
hdr
->
len
=
realLen
;
*
pcbActual
=
realLen
;
#if 0
{ int i,j;
for (i=0; i<realLen; i=i+0x10) {
printf(" %4.4x : ", i);
for (j=0; j<0x10 && (i+j < realLen); j++) {
printf("%2.2x ", rgbTok[i+j]);
}
printf("\n");
}
}
#endif
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
S_OK
;
}
/**********************************************************************
* VarFormatFromTokens [OLEAUT32.139]
* FIXME: No account of flags or iFirstDay etc
*/
HRESULT
WINAPI
VarFormatFromTokens
(
LPVARIANT
varIn
,
LPOLESTR
format
,
LPBYTE
pbTokCur
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
,
LCID
lcid
)
{
FORMATHDR
*
hdr
=
(
FORMATHDR
*
)
pbTokCur
;
BYTE
*
pData
=
pbTokCur
+
sizeof
(
FORMATHDR
);
LPSTR
pFormatA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
format
);
char
output
[
BUFFER_MAX
];
char
*
pNextPos
;
int
size
,
whichToken
;
VARIANTARG
Variant
;
struct
tm
TM
;
TRACE
(
"'%s', %p %lx %p only date support
\n
"
,
pFormatA
,
pbTokCur
,
dwFlags
,
pbstrOut
);
TRACE
(
"varIn:
\n
"
);
dump_Variant
(
varIn
);
memset
(
output
,
0x00
,
BUFFER_MAX
);
pNextPos
=
output
;
while
(
*
pData
!=
TOK_END
&&
((
pData
-
pbTokCur
)
<=
(
hdr
->
len
)))
{
TRACE
(
"Output looks like : '%s'
\n
"
,
output
);
/* Convert varient to appropriate data type */
whichToken
=
0
;
while
((
formatTokens
[
whichToken
].
tokenSize
!=
0x00
)
&&
(
formatTokens
[
whichToken
].
tokenId
!=
*
pData
))
{
whichToken
++
;
}
/* Use Variant local from here downwards as always correct type */
if
(
formatTokens
[
whichToken
].
tokenSize
>
0
&&
formatTokens
[
whichToken
].
varTypeRequired
!=
0
)
{
VariantInit
(
&
Variant
);
if
(
Coerce
(
&
Variant
,
lcid
,
dwFlags
,
varIn
,
formatTokens
[
whichToken
].
varTypeRequired
)
!=
S_OK
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
DISP_E_TYPEMISMATCH
;
}
else
if
(
formatTokens
[
whichToken
].
varTypeRequired
==
VT_DATE
)
{
if
(
DateToTm
(
V_UNION
(
&
Variant
,
date
),
dwFlags
,
&
TM
)
==
FALSE
)
{
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
E_INVALIDARG
;
}
}
}
TRACE
(
"Looking for match on token '%x'
\n
"
,
*
pData
);
switch
(
*
pData
)
{
case
TOK_COPY
:
TRACE
(
"Copy from %d for %d bytes
\n
"
,
*
(
pData
+
1
),
*
(
pData
+
2
));
memcpy
(
pNextPos
,
&
pFormatA
[
*
(
pData
+
1
)],
*
(
pData
+
2
));
pNextPos
=
pNextPos
+
*
(
pData
+
2
);
pData
=
pData
+
3
;
break
;
case
TOK_COLON
:
/* Get locale information - Time Separator */
size
=
GetLocaleInfoA
(
lcid
,
LOCALE_STIME
,
NULL
,
0
);
GetLocaleInfoA
(
lcid
,
LOCALE_STIME
,
pNextPos
,
size
);
TRACE
(
"TOK_COLON Time separator is '%s'
\n
"
,
pNextPos
);
pNextPos
=
pNextPos
+
size
;
pData
=
pData
+
1
;
break
;
case
TOK_SLASH
:
/* Get locale information - Date Separator */
size
=
GetLocaleInfoA
(
lcid
,
LOCALE_SDATE
,
NULL
,
0
);
GetLocaleInfoA
(
lcid
,
LOCALE_SDATE
,
pNextPos
,
size
);
TRACE
(
"TOK_COLON Time separator is '%s'
\n
"
,
pNextPos
);
pNextPos
=
pNextPos
+
size
;
pData
=
pData
+
1
;
break
;
case
TOK_d
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_mday
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_dd
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_mday
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_w
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_wday
+
1
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_m
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_mon
+
1
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_mm
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_mon
+
1
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_q
:
sprintf
(
pNextPos
,
"%d"
,
((
TM
.
tm_mon
+
1
)
/
4
)
+
1
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_y
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_yday
+
1
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_yy
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_year
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_yyyy
:
sprintf
(
pNextPos
,
"%4.4d"
,
TM
.
tm_year
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_h
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_hour
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_Hh
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_hour
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_N
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_min
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_Nn
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_min
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_S
:
sprintf
(
pNextPos
,
"%d"
,
TM
.
tm_sec
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
case
TOK_Ss
:
sprintf
(
pNextPos
,
"%2.2d"
,
TM
.
tm_sec
);
pNextPos
=
pNextPos
+
strlen
(
pNextPos
);
pData
=
pData
+
1
;
break
;
/* FIXME: To Do! */
case
TOK_ttttt
:
case
TOK_AMsPM
:
case
TOK_amspm
:
case
TOK_AsP
:
case
TOK_asp
:
case
TOK_AMPM
:
case
TOK_c
:
case
TOK_ddd
:
case
TOK_dddd
:
case
TOK_ddddd
:
case
TOK_dddddd
:
case
TOK_ww
:
case
TOK_mmm
:
case
TOK_mmmm
:
default:
FIXME
(
"Unhandled token for VarFormat %d
\n
"
,
*
pData
);
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
E_INVALIDARG
;
}
}
*
pbstrOut
=
StringDupAtoBstr
(
output
);
HeapFree
(
GetProcessHeap
(),
0
,
pFormatA
);
return
S_OK
;
}
/**********************************************************************
* VarFormat [OLEAUT32.87]
*
*/
HRESULT
WINAPI
VarFormat
(
LPVARIANT
varIn
,
LPOLESTR
format
,
int
firstDay
,
int
firstWeek
,
ULONG
dwFlags
,
BSTR
*
pbstrOut
)
{
LPSTR
pNewString
=
NULL
;
HRESULT
rc
=
S_OK
;
TRACE
(
"mostly stub! format='%s' day=%d, wk=%d, flags=%ld
\n
"
,
debugstr_w
(
format
),
firstDay
,
firstWeek
,
dwFlags
);
TRACE
(
"varIn:
\n
"
);
dump_Variant
(
varIn
);
/* Note: Must Handle references type Variants (contain ptrs
to values rather than values */
/* Get format string */
pNewString
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
format
);
/* FIXME: Handle some simple pre-definted format strings : */
if
(((
V_VT
(
varIn
)
&
VT_TYPEMASK
)
==
VT_CY
)
&&
(
lstrcmpiA
(
pNewString
,
"Currency"
)
==
0
))
{
/* Can't use VarBstrFromCy as it does not put currency sign on nor decimal places */
double
curVal
;
/* Handle references type Variants (contain ptrs to values rather than values */
if
(
V_VT
(
varIn
)
&
VT_BYREF
)
{
rc
=
VarR8FromCy
(
*
(
CY
*
)
V_UNION
(
varIn
,
byref
),
&
curVal
);
}
else
{
rc
=
VarR8FromCy
(
V_UNION
(
varIn
,
cyVal
),
&
curVal
);
}
if
(
rc
==
S_OK
)
{
char
tmpStr
[
BUFFER_MAX
];
sprintf
(
tmpStr
,
"%f"
,
curVal
);
if
(
GetCurrencyFormatA
(
GetUserDefaultLCID
(),
dwFlags
,
tmpStr
,
NULL
,
pBuffer
,
BUFFER_MAX
)
==
0
)
{
return
E_FAIL
;
}
else
{
*
pbstrOut
=
StringDupAtoBstr
(
pBuffer
);
}
}
}
else
if
((
V_VT
(
varIn
)
&
VT_TYPEMASK
)
==
VT_DATE
)
{
/* Attempt to do proper formatting! */
int
firstToken
=
-
1
;
rc
=
VarTokenizeFormatString
(
format
,
pBuffer
,
sizeof
(
pBuffer
),
firstDay
,
firstWeek
,
GetUserDefaultLCID
(),
&
firstToken
);
if
(
rc
==
S_OK
)
{
rc
=
VarFormatFromTokens
(
varIn
,
format
,
pBuffer
,
dwFlags
,
pbstrOut
,
GetUserDefaultLCID
());
}
}
else
if
((
V_VT
(
varIn
)
&
VT_TYPEMASK
)
==
VT_R8
)
{
if
(
V_VT
(
varIn
)
&
VT_BYREF
)
{
sprintf
(
pBuffer
,
"%f"
,
*
V_UNION
(
varIn
,
pdblVal
));
}
else
{
sprintf
(
pBuffer
,
"%f"
,
V_UNION
(
varIn
,
dblVal
));
}
*
pbstrOut
=
StringDupAtoBstr
(
pBuffer
);
}
else
if
((
V_VT
(
varIn
)
&
VT_TYPEMASK
)
==
VT_I2
)
{
if
(
V_VT
(
varIn
)
&
VT_BYREF
)
{
sprintf
(
pBuffer
,
"%d"
,
*
V_UNION
(
varIn
,
piVal
));
}
else
{
sprintf
(
pBuffer
,
"%d"
,
V_UNION
(
varIn
,
iVal
));
}
*
pbstrOut
=
StringDupAtoBstr
(
pBuffer
);
}
else
if
((
V_VT
(
varIn
)
&
VT_TYPEMASK
)
==
VT_BSTR
)
{
if
(
V_VT
(
varIn
)
&
VT_BYREF
)
*
pbstrOut
=
SysAllocString
(
*
V_UNION
(
varIn
,
pbstrVal
)
);
else
*
pbstrOut
=
SysAllocString
(
V_UNION
(
varIn
,
bstrVal
)
);
}
else
{
FIXME
(
"VarFormat: Unsupported format %d!
\n
"
,
V_VT
(
varIn
)
&
VT_TYPEMASK
);
*
pbstrOut
=
StringDupAtoBstr
(
"??"
);
}
/* Free allocated storage */
HeapFree
(
GetProcessHeap
(),
0
,
pNewString
);
TRACE
(
"result: '%s'
\n
"
,
debugstr_w
(
*
pbstrOut
));
return
rc
;
}
/**********************************************************************
* VarCyMulI4 [OLEAUT32.304]
* Multiply currency value by integer
*/
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment