xref: /reactos/dll/win32/oleaut32/varformat.c (revision 58588b76)
1 /*
2  * Variant formatting functions
3  *
4  * Copyright 2008 Damjan Jovanovic
5  * Copyright 2003 Jon Griffiths
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * NOTES
22  *  Since the formatting functions aren't properly documented, I used the
23  *  Visual Basic documentation as a guide to implementing these functions. This
24  *  means that some named or user-defined formats may work slightly differently.
25  *  Please submit a test case if you find a difference.
26  */
27 
28 #include "config.h"
29 
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wine/unicode.h"
38 #include "winerror.h"
39 #include "variant.h"
40 #include "wine/debug.h"
41 
42 WINE_DEFAULT_DEBUG_CHANNEL(variant);
43 
44 /* Make sure internal conversions to strings use the '.','+'/'-' and ','
45  * format chars from the US locale. This enables us to parse the created
46  * strings to determine the number of decimal places, exponent, etc.
47  */
48 #define LCID_US MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT)
49 
50 static const WCHAR szPercent_d[] = { '%','d','\0' };
51 static const WCHAR szPercentZeroTwo_d[] = { '%','0','2','d','\0' };
52 static const WCHAR szPercentZeroStar_d[] = { '%','0','*','d','\0' };
53 
54 /******************************************************************************
55  * Variant-Formats {OLEAUT32}
56  *
57  * NOTES
58  *  When formatting a variant a variety of format strings may be used to generate
59  *  different kinds of formatted output. A format string consists of either a named
60  *  format, or a user-defined format.
61  *
62  *  The following named formats are defined:
63  *| Name           Description
64  *| ----           -----------
65  *| General Date   Display Date, and time for non-integer values
66  *| Short Date     Short date format as defined by locale settings
67  *| Medium Date    Medium date format as defined by locale settings
68  *| Long Date      Long date format as defined by locale settings
69  *| Short Time     Short Time format as defined by locale settings
70  *| Medium Time    Medium time format as defined by locale settings
71  *| Long Time      Long time format as defined by locale settings
72  *| True/False     Localised text of "True" or "False"
73  *| Yes/No         Localised text of "Yes" or "No"
74  *| On/Off         Localised text of "On" or "Off"
75  *| General Number No thousands separator. No decimal points for integers
76  *| Currency       General currency format using localised characters
77  *| Fixed          At least one whole and two fractional digits
78  *| Standard       Same as 'Fixed', but including decimal separators
79  *| Percent        Multiply by 100 and display a trailing '%' character
80  *| Scientific     Display with exponent
81  *
82  *  User-defined formats consist of a combination of tokens and literal
83  *  characters. Literal characters are copied unmodified to the formatted
84  *  output at the position they occupy in the format string. Any character
85  *  that is not recognised as a token is treated as a literal. A literal can
86  *  also be specified by preceding it with a backslash character
87  *  (e.g. "\L\i\t\e\r\a\l") or enclosing it in double quotes.
88  *
89  *  A user-defined format can have up to 4 sections, depending on the type of
90  *  format. The following table lists sections and their meaning:
91  *| Format Type  Sections Meaning
92  *| -----------  -------- -------
93  *| Number       1        Use the same format for all numbers
94  *| Number       2        Use format 1 for positive and 2 for negative numbers
95  *| Number       3        Use format 1 for positive, 2 for zero, and 3
96  *|                       for negative numbers.
97  *| Number       4        Use format 1 for positive, 2 for zero, 3 for
98  *|                       negative, and 4 for null numbers.
99  *| String       1        Use the same format for all strings
100  *| String       2        Use format 2 for null and empty strings, otherwise
101  *|                       use format 1.
102  *| Date         1        Use the same format for all dates
103  *
104  *  The formatting tokens fall into several categories depending on the type
105  *  of formatted output. For more information on each type, see
106  *  VarFormat-Dates(), VarFormat-Strings() and VarFormat-Numbers().
107  *
108  *  SEE ALSO
109  *  VarTokenizeFormatString(), VarFormatFromTokens(), VarFormat(),
110  *  VarFormatDateTime(), VarFormatNumber(), VarFormatCurrency().
111  */
112 
113 /******************************************************************************
114  * VarFormat-Strings {OLEAUT32}
115  *
116  * NOTES
117  *  When formatting a variant as a string, it is first converted to a VT_BSTR.
118  *  The user-format string defines which characters are copied into which
119  *  positions in the output string. Literals may be inserted in the format
120  *  string. When creating the formatted string, excess characters in the string
121  *  (those not consumed by a token) are appended to the end of the output. If
122  *  there are more tokens than characters in the string to format, spaces will
123  *  be inserted at the start of the string if the '@' token was used.
124  *
125  *  By default strings are converted to lowercase, or uppercase if the '>' token
126  *  is encountered. This applies to the whole string: it is not possible to
127  *  generate a mixed-case output string.
128  *
129  *  In user-defined string formats, the following tokens are recognised:
130  *| Token  Description
131  *| -----  -----------
132  *| '@'    Copy a char from the source, or a space if no chars are left.
133  *| '&'    Copy a char from the source, or write nothing if no chars are left.
134  *| '<'    Output the whole string as lower-case (the default).
135  *| '>'    Output the whole string as upper-case.
136  *| '!'    MSDN indicates that this character should cause right-to-left
137  *|        copying, however tests show that it is tokenised but not processed.
138  */
139 
140 /*
141  * Common format definitions
142  */
143 
144  /* Format types */
145 #define FMT_TYPE_UNKNOWN 0x0
146 #define FMT_TYPE_GENERAL 0x1
147 #define FMT_TYPE_NUMBER  0x2
148 #define FMT_TYPE_DATE    0x3
149 #define FMT_TYPE_STRING  0x4
150 
151 #define FMT_TO_STRING    0x0 /* If header->size == this, act like VB's Str() fn */
152 
153 typedef struct tagFMT_SHORT_HEADER
154 {
155   BYTE   size;      /* Size of tokenised block (including header), or FMT_TO_STRING */
156   BYTE   type;      /* Allowable types (FMT_TYPE_*) */
157   BYTE   offset[1]; /* Offset of the first (and only) format section */
158 } FMT_SHORT_HEADER;
159 
160 typedef struct tagFMT_HEADER
161 {
162   BYTE   size;      /* Total size of the whole tokenised block (including header) */
163   BYTE   type;      /* Allowable types (FMT_TYPE_*) */
164   BYTE   starts[4]; /* Offset of each of the 4 format sections, or 0 if none */
165 } FMT_HEADER;
166 
167 #define FmtGetPositive(x)  (x->starts[0])
168 #define FmtGetNegative(x)  (x->starts[1] ? x->starts[1] : x->starts[0])
169 #define FmtGetZero(x)      (x->starts[2] ? x->starts[2] : x->starts[0])
170 #define FmtGetNull(x)      (x->starts[3] ? x->starts[3] : x->starts[0])
171 
172 /*
173  * String formats
174  */
175 
176 #define FMT_FLAG_LT  0x1 /* Has '<' (lower case) */
177 #define FMT_FLAG_GT  0x2 /* Has '>' (upper case) */
178 #define FMT_FLAG_RTL 0x4 /* Has '!' (Copy right to left) */
179 
180 typedef struct tagFMT_STRING_HEADER
181 {
182   BYTE   flags;      /* LT, GT, RTL */
183   BYTE   unknown1;
184   BYTE   unknown2;
185   BYTE   copy_chars; /* Number of chars to be copied */
186   BYTE   unknown3;
187 } FMT_STRING_HEADER;
188 
189 /*
190  * Number formats
191  */
192 
193 #define FMT_FLAG_PERCENT   0x1  /* Has '%' (Percentage) */
194 #define FMT_FLAG_EXPONENT  0x2  /* Has 'e' (Exponent/Scientific notation) */
195 #define FMT_FLAG_THOUSANDS 0x4  /* Has ',' (Standard use of the thousands separator) */
196 #define FMT_FLAG_BOOL      0x20 /* Boolean format */
197 
198 typedef struct tagFMT_NUMBER_HEADER
199 {
200   BYTE   flags;      /* PERCENT, EXPONENT, THOUSANDS, BOOL */
201   BYTE   multiplier; /* Multiplier, 100 for percentages */
202   BYTE   divisor;    /* Divisor, 1000 if '%%' was used */
203   BYTE   whole;      /* Number of digits before the decimal point */
204   BYTE   fractional; /* Number of digits after the decimal point */
205 } FMT_NUMBER_HEADER;
206 
207 /*
208  * Date Formats
209  */
210 typedef struct tagFMT_DATE_HEADER
211 {
212   BYTE   flags;
213   BYTE   unknown1;
214   BYTE   unknown2;
215   BYTE   unknown3;
216   BYTE   unknown4;
217 } FMT_DATE_HEADER;
218 
219 /*
220  * Format token values
221  */
222 #define FMT_GEN_COPY        0x00 /* \n, "lit" => 0,pos,len: Copy len chars from input+pos */
223 #define FMT_GEN_INLINE      0x01 /*      => 1,len,[chars]: Copy len chars from token stream */
224 #define FMT_GEN_END         0x02 /* \0,; => 2: End of the tokenised format */
225 #define FMT_DATE_TIME_SEP   0x03 /* Time separator char */
226 #define FMT_DATE_DATE_SEP   0x04 /* Date separator char */
227 #define FMT_DATE_GENERAL    0x05 /* General format date */
228 #define FMT_DATE_QUARTER    0x06 /* Quarter of the year from 1-4 */
229 #define FMT_DATE_TIME_SYS   0x07 /* System long time format */
230 #define FMT_DATE_DAY        0x08 /* Day with no leading 0 */
231 #define FMT_DATE_DAY_0      0x09 /* Day with leading 0 */
232 #define FMT_DATE_DAY_SHORT  0x0A /* Short day name */
233 #define FMT_DATE_DAY_LONG   0x0B /* Long day name */
234 #define FMT_DATE_SHORT      0x0C /* Short date format */
235 #define FMT_DATE_LONG       0x0D /* Long date format */
236 #define FMT_DATE_MEDIUM     0x0E /* Medium date format */
237 #define FMT_DATE_DAY_WEEK   0x0F /* First day of the week */
238 #define FMT_DATE_WEEK_YEAR  0x10 /* First week of the year */
239 #define FMT_DATE_MON        0x11 /* Month with no leading 0 */
240 #define FMT_DATE_MON_0      0x12 /* Month with leading 0 */
241 #define FMT_DATE_MON_SHORT  0x13 /* Short month name */
242 #define FMT_DATE_MON_LONG   0x14 /* Long month name */
243 #define FMT_DATE_YEAR_DOY   0x15 /* Day of the year with no leading 0 */
244 #define FMT_DATE_YEAR_0     0x16 /* 2 digit year with leading 0 */
245 /* NOTE: token 0x17 is not defined, 'yyy' is not valid */
246 #define FMT_DATE_YEAR_LONG  0x18 /* 4 digit year */
247 #define FMT_DATE_MIN        0x1A /* Minutes with no leading 0 */
248 #define FMT_DATE_MIN_0      0x1B /* Minutes with leading 0 */
249 #define FMT_DATE_SEC        0x1C /* Seconds with no leading 0 */
250 #define FMT_DATE_SEC_0      0x1D /* Seconds with leading 0 */
251 #define FMT_DATE_HOUR       0x1E /* Hours with no leading 0 */
252 #define FMT_DATE_HOUR_0     0x1F /* Hours with leading 0 */
253 #define FMT_DATE_HOUR_12    0x20 /* Hours with no leading 0, 12 hour clock */
254 #define FMT_DATE_HOUR_12_0  0x21 /* Hours with leading 0, 12 hour clock */
255 #define FMT_DATE_TIME_UNK2  0x23 /* same as FMT_DATE_HOUR_0, for "short time" format */
256 /* FIXME: probably missing some here */
257 #define FMT_DATE_AMPM_SYS1  0x2E /* AM/PM as defined by system settings */
258 #define FMT_DATE_AMPM_UPPER 0x2F /* Upper-case AM or PM */
259 #define FMT_DATE_A_UPPER    0x30 /* Upper-case A or P */
260 #define FMT_DATE_AMPM_SYS2  0x31 /* AM/PM as defined by system settings */
261 #define FMT_DATE_AMPM_LOWER 0x32 /* Lower-case AM or PM */
262 #define FMT_DATE_A_LOWER    0x33 /* Lower-case A or P */
263 #define FMT_NUM_COPY_ZERO   0x34 /* Copy 1 digit or 0 if no digit */
264 #define FMT_NUM_COPY_SKIP   0x35 /* Copy 1 digit or skip if no digit */
265 #define FMT_NUM_DECIMAL     0x36 /* Decimal separator */
266 #define FMT_NUM_EXP_POS_U   0x37 /* Scientific notation, uppercase, + sign */
267 #define FMT_NUM_EXP_NEG_U   0x38 /* Scientific notation, uppercase, - sign */
268 #define FMT_NUM_EXP_POS_L   0x39 /* Scientific notation, lowercase, + sign */
269 #define FMT_NUM_EXP_NEG_L   0x3A /* Scientific notation, lowercase, - sign */
270 #define FMT_NUM_CURRENCY    0x3B /* Currency symbol */
271 #define FMT_NUM_TRUE_FALSE  0x3D /* Convert to "True" or "False" */
272 #define FMT_NUM_YES_NO      0x3E /* Convert to "Yes" or "No" */
273 #define FMT_NUM_ON_OFF      0x3F /* Convert to "On" or "Off"  */
274 #define FMT_STR_COPY_SPACE  0x40 /* Copy len chars with space if no char */
275 #define FMT_STR_COPY_SKIP   0x41 /* Copy len chars or skip if no char */
276 
277 /* Named Formats and their tokenised values */
278 static const WCHAR szGeneralDate[] = { 'G','e','n','e','r','a','l',' ','D','a','t','e','\0' };
279 static const BYTE fmtGeneralDate[0x0a] =
280 {
281   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
282   0x0,0x0,0x0,0x0,0x0,
283   FMT_DATE_GENERAL,FMT_GEN_END
284 };
285 
286 static const WCHAR szShortDate[] = { 'S','h','o','r','t',' ','D','a','t','e','\0' };
287 static const BYTE fmtShortDate[0x0a] =
288 {
289   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
290   0x0,0x0,0x0,0x0,0x0,
291   FMT_DATE_SHORT,FMT_GEN_END
292 };
293 
294 static const WCHAR szMediumDate[] = { 'M','e','d','i','u','m',' ','D','a','t','e','\0' };
295 static const BYTE fmtMediumDate[0x0a] =
296 {
297   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
298   0x0,0x0,0x0,0x0,0x0,
299   FMT_DATE_MEDIUM,FMT_GEN_END
300 };
301 
302 static const WCHAR szLongDate[] = { 'L','o','n','g',' ','D','a','t','e','\0' };
303 static const BYTE fmtLongDate[0x0a] =
304 {
305   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
306   0x0,0x0,0x0,0x0,0x0,
307   FMT_DATE_LONG,FMT_GEN_END
308 };
309 
310 static const WCHAR szShortTime[] = { 'S','h','o','r','t',' ','T','i','m','e','\0' };
311 static const BYTE fmtShortTime[0x0c] =
312 {
313   0x0c,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
314   0x0,0x0,0x0,0x0,0x0,
315   FMT_DATE_TIME_UNK2,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,FMT_GEN_END
316 };
317 
318 static const WCHAR szMediumTime[] = { 'M','e','d','i','u','m',' ','T','i','m','e','\0' };
319 static const BYTE fmtMediumTime[0x11] =
320 {
321   0x11,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
322   0x0,0x0,0x0,0x0,0x0,
323   FMT_DATE_HOUR_12_0,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,
324   FMT_GEN_INLINE,0x01,' ','\0',FMT_DATE_AMPM_SYS1,FMT_GEN_END
325 };
326 
327 static const WCHAR szLongTime[] = { 'L','o','n','g',' ','T','i','m','e','\0' };
328 static const BYTE fmtLongTime[0x0d] =
329 {
330   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
331   0x0,0x0,0x0,0x0,0x0,
332   FMT_DATE_TIME_SYS,FMT_GEN_END
333 };
334 
335 static const WCHAR szTrueFalse[] = { 'T','r','u','e','/','F','a','l','s','e','\0' };
336 static const BYTE fmtTrueFalse[0x0d] =
337 {
338   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
339   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
340   FMT_NUM_TRUE_FALSE,FMT_GEN_END
341 };
342 
343 static const WCHAR szYesNo[] = { 'Y','e','s','/','N','o','\0' };
344 static const BYTE fmtYesNo[0x0d] =
345 {
346   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
347   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
348   FMT_NUM_YES_NO,FMT_GEN_END
349 };
350 
351 static const WCHAR szOnOff[] = { 'O','n','/','O','f','f','\0' };
352 static const BYTE fmtOnOff[0x0d] =
353 {
354   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
355   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
356   FMT_NUM_ON_OFF,FMT_GEN_END
357 };
358 
359 static const WCHAR szGeneralNumber[] = { 'G','e','n','e','r','a','l',' ','N','u','m','b','e','r','\0' };
360 static const BYTE fmtGeneralNumber[sizeof(FMT_HEADER)] =
361 {
362   sizeof(FMT_HEADER),FMT_TYPE_GENERAL,sizeof(FMT_HEADER),0x0,0x0,0x0
363 };
364 
365 static const WCHAR szCurrency[] = { 'C','u','r','r','e','n','c','y','\0' };
366 static const BYTE fmtCurrency[0x26] =
367 {
368   0x26,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x12,0x0,0x0,
369   /* Positive numbers */
370   FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
371   FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
372   FMT_GEN_END,
373   /* Negative numbers */
374   FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
375   FMT_GEN_INLINE,0x1,'(','\0',FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,
376   FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_INLINE,0x1,')','\0',
377   FMT_GEN_END
378 };
379 
380 static const WCHAR szFixed[] = { 'F','i','x','e','d','\0' };
381 static const BYTE fmtFixed[0x11] =
382 {
383   0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
384   0x0,0x0,0x0,0x1,0x2,
385   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
386 };
387 
388 static const WCHAR szStandard[] = { 'S','t','a','n','d','a','r','d','\0' };
389 static const BYTE fmtStandard[0x11] =
390 {
391   0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
392   FMT_FLAG_THOUSANDS,0x0,0x0,0x1,0x2,
393   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
394 };
395 
396 static const WCHAR szPercent[] = { 'P','e','r','c','e','n','t','\0' };
397 static const BYTE fmtPercent[0x15] =
398 {
399   0x15,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
400   FMT_FLAG_PERCENT,0x1,0x0,0x1,0x2,
401   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
402   FMT_GEN_INLINE,0x1,'%','\0',FMT_GEN_END
403 };
404 
405 static const WCHAR szScientific[] = { 'S','c','i','e','n','t','i','f','i','c','\0' };
406 static const BYTE fmtScientific[0x13] =
407 {
408   0x13,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
409   FMT_FLAG_EXPONENT,0x0,0x0,0x1,0x2,
410   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_NUM_EXP_POS_U,0x2,FMT_GEN_END
411 };
412 
413 typedef struct tagNAMED_FORMAT
414 {
415   LPCWSTR name;
416   const BYTE* format;
417 } NAMED_FORMAT;
418 
419 /* Format name to tokenised format. Must be kept sorted by name */
420 static const NAMED_FORMAT VARIANT_NamedFormats[] =
421 {
422   { szCurrency, fmtCurrency },
423   { szFixed, fmtFixed },
424   { szGeneralDate, fmtGeneralDate },
425   { szGeneralNumber, fmtGeneralNumber },
426   { szLongDate, fmtLongDate },
427   { szLongTime, fmtLongTime },
428   { szMediumDate, fmtMediumDate },
429   { szMediumTime, fmtMediumTime },
430   { szOnOff, fmtOnOff },
431   { szPercent, fmtPercent },
432   { szScientific, fmtScientific },
433   { szShortDate, fmtShortDate },
434   { szShortTime, fmtShortTime },
435   { szStandard, fmtStandard },
436   { szTrueFalse, fmtTrueFalse },
437   { szYesNo, fmtYesNo }
438 };
439 typedef const NAMED_FORMAT *LPCNAMED_FORMAT;
440 
441 static int FormatCompareFn(const void *l, const void *r)
442 {
443   return strcmpiW(((LPCNAMED_FORMAT)l)->name, ((LPCNAMED_FORMAT)r)->name);
444 }
445 
446 static inline const BYTE *VARIANT_GetNamedFormat(LPCWSTR lpszFormat)
447 {
448   NAMED_FORMAT key;
449   LPCNAMED_FORMAT fmt;
450 
451   key.name = lpszFormat;
452   fmt = bsearch(&key, VARIANT_NamedFormats, ARRAY_SIZE(VARIANT_NamedFormats),
453                                  sizeof(NAMED_FORMAT), FormatCompareFn);
454   return fmt ? fmt->format : NULL;
455 }
456 
457 /* Return an error if the token for the value will not fit in the destination */
458 #define NEED_SPACE(x) if (cbTok < (int)(x)) return TYPE_E_BUFFERTOOSMALL; cbTok -= (x)
459 
460 /* Non-zero if the format is unknown or a given type */
461 #define COULD_BE(typ) ((!fmt_number && header->type==FMT_TYPE_UNKNOWN)||header->type==typ)
462 
463 /* State during tokenising */
464 #define FMT_STATE_OPEN_COPY     0x1 /* Last token written was a copy */
465 #define FMT_STATE_WROTE_DECIMAL 0x2 /* Already wrote a decimal separator */
466 #define FMT_STATE_SEEN_HOURS    0x4 /* See the hh specifier */
467 #define FMT_STATE_WROTE_MINUTES 0x8 /* Wrote minutes */
468 
469 /**********************************************************************
470  *              VarTokenizeFormatString [OLEAUT32.140]
471  *
472  * Convert a format string into tokenised form.
473  *
474  * PARAMS
475  *  lpszFormat [I] Format string to tokenise
476  *  rgbTok     [O] Destination for tokenised format
477  *  cbTok      [I] Size of rgbTok in bytes
478  *  nFirstDay  [I] First day of the week (1-7, or 0 for current system default)
479  *  nFirstWeek [I] How to treat the first week (see notes)
480  *  lcid       [I] Locale Id of the format string
481  *  pcbActual  [O] If non-NULL, filled with the first token generated
482  *
483  * RETURNS
484  *  Success: S_OK. rgbTok contains the tokenised format.
485  *  Failure: E_INVALIDARG, if any argument is invalid.
486  *           TYPE_E_BUFFERTOOSMALL, if rgbTok is not large enough.
487  *
488  * NOTES
489  * Valid values for the nFirstWeek parameter are:
490  *| Value  Meaning
491  *| -----  -------
492  *|   0    Use the current system default
493  *|   1    The first week is that containing Jan 1
494  *|   2    Four or more days of the first week are in the current year
495  *|   3    The first week is 7 days long
496  * See Variant-Formats(), VarFormatFromTokens().
497  */
498 HRESULT WINAPI VarTokenizeFormatString(LPOLESTR lpszFormat, LPBYTE rgbTok,
499                                        int cbTok, int nFirstDay, int nFirstWeek,
500                                        LCID lcid, int *pcbActual)
501 {
502   /* Note: none of these strings should be NUL terminated */
503   static const WCHAR szTTTTT[] = { 't','t','t','t','t' };
504   static const WCHAR szAMPM[] = { 'A','M','P','M' };
505   static const WCHAR szampm[] = { 'a','m','p','m' };
506   static const WCHAR szAMSlashPM[] = { 'A','M','/','P','M' };
507   static const WCHAR szamSlashpm[] = { 'a','m','/','p','m' };
508   const BYTE *namedFmt;
509   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
510   FMT_STRING_HEADER *str_header = (FMT_STRING_HEADER*)(rgbTok + sizeof(FMT_HEADER));
511   FMT_NUMBER_HEADER *num_header = (FMT_NUMBER_HEADER*)str_header;
512   BYTE* pOut = rgbTok + sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER);
513   BYTE* pLastHours = NULL;
514   BYTE fmt_number = 0;
515   DWORD fmt_state = 0;
516   LPCWSTR pFormat = lpszFormat;
517 
518   TRACE("(%s,%p,%d,%d,%d,0x%08x,%p)\n", debugstr_w(lpszFormat), rgbTok, cbTok,
519         nFirstDay, nFirstWeek, lcid, pcbActual);
520 
521   if (!rgbTok ||
522       nFirstDay < 0 || nFirstDay > 7 || nFirstWeek < 0 || nFirstWeek > 3)
523     return E_INVALIDARG;
524 
525   if (!lpszFormat || !*lpszFormat)
526   {
527     /* An empty string means 'general format' */
528     NEED_SPACE(sizeof(BYTE));
529     *rgbTok = FMT_TO_STRING;
530     if (pcbActual)
531       *pcbActual = FMT_TO_STRING;
532     return S_OK;
533   }
534 
535   if (cbTok > 255)
536     cbTok = 255; /* Ensure we error instead of wrapping */
537 
538   /* Named formats */
539   namedFmt = VARIANT_GetNamedFormat(lpszFormat);
540   if (namedFmt)
541   {
542     NEED_SPACE(namedFmt[0]);
543     memcpy(rgbTok, namedFmt, namedFmt[0]);
544     TRACE("Using pre-tokenised named format %s\n", debugstr_w(lpszFormat));
545     /* FIXME: pcbActual */
546     return S_OK;
547   }
548 
549   /* Insert header */
550   NEED_SPACE(sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER));
551   memset(header, 0, sizeof(FMT_HEADER));
552   memset(str_header, 0, sizeof(FMT_STRING_HEADER));
553 
554   header->starts[fmt_number] = sizeof(FMT_HEADER);
555 
556   while (*pFormat)
557   {
558     /* --------------
559      * General tokens
560      * --------------
561      */
562     if (*pFormat == ';')
563     {
564       while (*pFormat == ';')
565       {
566         TRACE(";\n");
567         if (++fmt_number > 3)
568           return E_INVALIDARG; /* too many formats */
569         pFormat++;
570       }
571       if (*pFormat)
572       {
573         TRACE("New header\n");
574         NEED_SPACE(sizeof(BYTE) + sizeof(FMT_STRING_HEADER));
575         *pOut++ = FMT_GEN_END;
576 
577         header->starts[fmt_number] = pOut - rgbTok;
578         str_header = (FMT_STRING_HEADER*)pOut;
579         num_header = (FMT_NUMBER_HEADER*)pOut;
580         memset(str_header, 0, sizeof(FMT_STRING_HEADER));
581         pOut += sizeof(FMT_STRING_HEADER);
582         fmt_state = 0;
583         pLastHours = NULL;
584       }
585     }
586     else if (*pFormat == '\\')
587     {
588       /* Escaped character */
589       if (pFormat[1])
590       {
591         NEED_SPACE(3 * sizeof(BYTE));
592         pFormat++;
593         *pOut++ = FMT_GEN_COPY;
594         *pOut++ = pFormat - lpszFormat;
595         *pOut++ = 0x1;
596         fmt_state |= FMT_STATE_OPEN_COPY;
597         TRACE("'\\'\n");
598       }
599       else
600         fmt_state &= ~FMT_STATE_OPEN_COPY;
601       pFormat++;
602     }
603     else if (*pFormat == '"')
604     {
605       /* Escaped string
606        * Note: Native encodes "" as a copy of length zero. That's just dumb, so
607        * here we avoid encoding anything in this case.
608        */
609       if (!pFormat[1])
610         pFormat++;
611       else if (pFormat[1] == '"')
612       {
613         pFormat += 2;
614       }
615       else
616       {
617         LPCWSTR start = ++pFormat;
618         while (*pFormat && *pFormat != '"')
619           pFormat++;
620         NEED_SPACE(3 * sizeof(BYTE));
621         *pOut++ = FMT_GEN_COPY;
622         *pOut++ = start - lpszFormat;
623         *pOut++ = pFormat - start;
624         if (*pFormat == '"')
625           pFormat++;
626         TRACE("Quoted string pos %d, len %d\n", pOut[-2], pOut[-1]);
627       }
628       fmt_state &= ~FMT_STATE_OPEN_COPY;
629     }
630     /* -------------
631      * Number tokens
632      * -------------
633      */
634     else if (*pFormat == '0' && COULD_BE(FMT_TYPE_NUMBER))
635     {
636       /* Number formats: Digit from number or '0' if no digits
637        * Other formats: Literal
638        * Types the format if found
639        */
640       header->type = FMT_TYPE_NUMBER;
641       NEED_SPACE(2 * sizeof(BYTE));
642       *pOut++ = FMT_NUM_COPY_ZERO;
643       *pOut = 0x0;
644       while (*pFormat == '0')
645       {
646         *pOut = *pOut + 1;
647         pFormat++;
648       }
649       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
650         num_header->fractional += *pOut;
651       else
652         num_header->whole += *pOut;
653       TRACE("%d 0's\n", *pOut);
654       pOut++;
655       fmt_state &= ~FMT_STATE_OPEN_COPY;
656     }
657     else if (*pFormat == '#' && COULD_BE(FMT_TYPE_NUMBER))
658     {
659       /* Number formats: Digit from number or blank if no digits
660        * Other formats: Literal
661        * Types the format if found
662        */
663       header->type = FMT_TYPE_NUMBER;
664       NEED_SPACE(2 * sizeof(BYTE));
665       *pOut++ = FMT_NUM_COPY_SKIP;
666       *pOut = 0x0;
667       while (*pFormat == '#')
668       {
669         *pOut = *pOut + 1;
670         pFormat++;
671       }
672       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
673         num_header->fractional += *pOut;
674       else
675         num_header->whole += *pOut;
676       TRACE("%d #'s\n", *pOut);
677       pOut++;
678       fmt_state &= ~FMT_STATE_OPEN_COPY;
679     }
680     else if (*pFormat == '.' && COULD_BE(FMT_TYPE_NUMBER) &&
681               !(fmt_state & FMT_STATE_WROTE_DECIMAL))
682     {
683       /* Number formats: Decimal separator when 1st seen, literal thereafter
684        * Other formats: Literal
685        * Types the format if found
686        */
687       header->type = FMT_TYPE_NUMBER;
688       NEED_SPACE(sizeof(BYTE));
689       *pOut++ = FMT_NUM_DECIMAL;
690       fmt_state |= FMT_STATE_WROTE_DECIMAL;
691       fmt_state &= ~FMT_STATE_OPEN_COPY;
692       pFormat++;
693       TRACE("decimal sep\n");
694     }
695     else if ((*pFormat == 'e' || *pFormat == 'E') && (pFormat[1] == '-' ||
696               pFormat[1] == '+') && header->type == FMT_TYPE_NUMBER)
697     {
698       /* Number formats: Exponent specifier
699        * Other formats: Literal
700        */
701       num_header->flags |= FMT_FLAG_EXPONENT;
702       NEED_SPACE(2 * sizeof(BYTE));
703       if (*pFormat == 'e') {
704         if (pFormat[1] == '+')
705           *pOut = FMT_NUM_EXP_POS_L;
706         else
707           *pOut = FMT_NUM_EXP_NEG_L;
708       } else {
709         if (pFormat[1] == '+')
710           *pOut = FMT_NUM_EXP_POS_U;
711         else
712           *pOut = FMT_NUM_EXP_NEG_U;
713       }
714       pFormat += 2;
715       *++pOut = 0x0;
716       while (*pFormat == '0')
717       {
718         *pOut = *pOut + 1;
719         pFormat++;
720       }
721       pOut++;
722       TRACE("exponent\n");
723     }
724     /* FIXME: %% => Divide by 1000 */
725     else if (*pFormat == ',' && header->type == FMT_TYPE_NUMBER)
726     {
727       /* Number formats: Use the thousands separator
728        * Other formats: Literal
729        */
730       num_header->flags |= FMT_FLAG_THOUSANDS;
731       pFormat++;
732       fmt_state &= ~FMT_STATE_OPEN_COPY;
733       TRACE("thousands sep\n");
734     }
735     /* -----------
736      * Date tokens
737      * -----------
738      */
739     else if (*pFormat == '/' && COULD_BE(FMT_TYPE_DATE))
740     {
741       /* Date formats: Date separator
742        * Other formats: Literal
743        * Types the format if found
744        */
745       header->type = FMT_TYPE_DATE;
746       NEED_SPACE(sizeof(BYTE));
747       *pOut++ = FMT_DATE_DATE_SEP;
748       pFormat++;
749       fmt_state &= ~FMT_STATE_OPEN_COPY;
750       TRACE("date sep\n");
751     }
752     else if (*pFormat == ':' && COULD_BE(FMT_TYPE_DATE))
753     {
754       /* Date formats: Time separator
755        * Other formats: Literal
756        * Types the format if found
757        */
758       header->type = FMT_TYPE_DATE;
759       NEED_SPACE(sizeof(BYTE));
760       *pOut++ = FMT_DATE_TIME_SEP;
761       pFormat++;
762       fmt_state &= ~FMT_STATE_OPEN_COPY;
763       TRACE("time sep\n");
764     }
765     else if ((*pFormat == 'a' || *pFormat == 'A') &&
766               !strncmpiW(pFormat, szAMPM, ARRAY_SIZE(szAMPM)))
767     {
768       /* Date formats: System AM/PM designation
769        * Other formats: Literal
770        * Types the format if found
771        */
772       header->type = FMT_TYPE_DATE;
773       NEED_SPACE(sizeof(BYTE));
774       pFormat += ARRAY_SIZE(szAMPM);
775       if (!strncmpW(pFormat, szampm, ARRAY_SIZE(szampm)))
776         *pOut++ = FMT_DATE_AMPM_SYS2;
777       else
778         *pOut++ = FMT_DATE_AMPM_SYS1;
779       if (pLastHours)
780         *pLastHours = *pLastHours + 2;
781       TRACE("ampm\n");
782     }
783     else if (*pFormat == 'a' && pFormat[1] == '/' &&
784               (pFormat[2] == 'p' || pFormat[2] == 'P'))
785     {
786       /* Date formats: lowercase a or p designation
787        * Other formats: Literal
788        * Types the format if found
789        */
790       header->type = FMT_TYPE_DATE;
791       NEED_SPACE(sizeof(BYTE));
792       pFormat += 3;
793       *pOut++ = FMT_DATE_A_LOWER;
794       if (pLastHours)
795         *pLastHours = *pLastHours + 2;
796       TRACE("a/p\n");
797     }
798     else if (*pFormat == 'A' && pFormat[1] == '/' &&
799               (pFormat[2] == 'p' || pFormat[2] == 'P'))
800     {
801       /* Date formats: Uppercase a or p designation
802        * Other formats: Literal
803        * Types the format if found
804        */
805       header->type = FMT_TYPE_DATE;
806       NEED_SPACE(sizeof(BYTE));
807       pFormat += 3;
808       *pOut++ = FMT_DATE_A_UPPER;
809       if (pLastHours)
810         *pLastHours = *pLastHours + 2;
811       TRACE("A/P\n");
812     }
813     else if (*pFormat == 'a' && !strncmpW(pFormat, szamSlashpm, ARRAY_SIZE(szamSlashpm)))
814     {
815       /* Date formats: lowercase AM or PM designation
816        * Other formats: Literal
817        * Types the format if found
818        */
819       header->type = FMT_TYPE_DATE;
820       NEED_SPACE(sizeof(BYTE));
821       pFormat += ARRAY_SIZE(szamSlashpm);
822       *pOut++ = FMT_DATE_AMPM_LOWER;
823       if (pLastHours)
824         *pLastHours = *pLastHours + 2;
825       TRACE("AM/PM\n");
826     }
827     else if (*pFormat == 'A' && !strncmpW(pFormat, szAMSlashPM, ARRAY_SIZE(szAMSlashPM)))
828     {
829       /* Date formats: Uppercase AM or PM designation
830        * Other formats: Literal
831        * Types the format if found
832        */
833       header->type = FMT_TYPE_DATE;
834       NEED_SPACE(sizeof(BYTE));
835       pFormat += ARRAY_SIZE(szAMSlashPM);
836       *pOut++ = FMT_DATE_AMPM_UPPER;
837       TRACE("AM/PM\n");
838     }
839     else if ((*pFormat == 'c' || *pFormat == 'C') && COULD_BE(FMT_TYPE_DATE))
840     {
841       /* Date formats: General date format
842        * Other formats: Literal
843        * Types the format if found
844        */
845       header->type = FMT_TYPE_DATE;
846       NEED_SPACE(sizeof(BYTE));
847       pFormat += ARRAY_SIZE(szAMSlashPM);
848       *pOut++ = FMT_DATE_GENERAL;
849       TRACE("gen date\n");
850     }
851     else if ((*pFormat == 'd' || *pFormat == 'D') && COULD_BE(FMT_TYPE_DATE))
852     {
853       /* Date formats: Day specifier
854        * Other formats: Literal
855        * Types the format if found
856        */
857       int count = -1;
858       header->type = FMT_TYPE_DATE;
859       while ((*pFormat == 'd' || *pFormat == 'D') && count < 6)
860       {
861         pFormat++;
862         count++;
863       }
864       NEED_SPACE(sizeof(BYTE));
865       *pOut++ = FMT_DATE_DAY + count;
866       fmt_state &= ~FMT_STATE_OPEN_COPY;
867       /* When we find the days token, reset the seen hours state so that
868        * 'mm' is again written as month when encountered.
869        */
870       fmt_state &= ~FMT_STATE_SEEN_HOURS;
871       TRACE("%d d's\n", count + 1);
872     }
873     else if ((*pFormat == 'h' || *pFormat == 'H') && COULD_BE(FMT_TYPE_DATE))
874     {
875       /* Date formats: Hour specifier
876        * Other formats: Literal
877        * Types the format if found
878        */
879       header->type = FMT_TYPE_DATE;
880       NEED_SPACE(sizeof(BYTE));
881       pFormat++;
882       /* Record the position of the hours specifier - if we encounter
883        * an am/pm specifier we will change the hours from 24 to 12.
884        */
885       pLastHours = pOut;
886       if (*pFormat == 'h' || *pFormat == 'H')
887       {
888         pFormat++;
889         *pOut++ = FMT_DATE_HOUR_0;
890         TRACE("hh\n");
891       }
892       else
893       {
894         *pOut++ = FMT_DATE_HOUR;
895         TRACE("h\n");
896       }
897       fmt_state &= ~FMT_STATE_OPEN_COPY;
898       /* Note that now we have seen an hours token, the next occurrence of
899        * 'mm' indicates minutes, not months.
900        */
901       fmt_state |= FMT_STATE_SEEN_HOURS;
902     }
903     else if ((*pFormat == 'm' || *pFormat == 'M') && COULD_BE(FMT_TYPE_DATE))
904     {
905       /* Date formats: Month specifier (or Minute specifier, after hour specifier)
906        * Other formats: Literal
907        * Types the format if found
908        */
909       int count = -1;
910       header->type = FMT_TYPE_DATE;
911       while ((*pFormat == 'm' || *pFormat == 'M') && count < 4)
912       {
913         pFormat++;
914         count++;
915       }
916       NEED_SPACE(sizeof(BYTE));
917       if (count <= 1 && fmt_state & FMT_STATE_SEEN_HOURS &&
918           !(fmt_state & FMT_STATE_WROTE_MINUTES))
919       {
920         /* We have seen an hours specifier and not yet written a minutes
921          * specifier. Write this as minutes and thereafter as months.
922          */
923         *pOut++ = count == 1 ? FMT_DATE_MIN_0 : FMT_DATE_MIN;
924         fmt_state |= FMT_STATE_WROTE_MINUTES; /* Hereafter write months */
925       }
926       else
927         *pOut++ = FMT_DATE_MON + count; /* Months */
928       fmt_state &= ~FMT_STATE_OPEN_COPY;
929       TRACE("%d m's\n", count + 1);
930     }
931     else if ((*pFormat == 'n' || *pFormat == 'N') && COULD_BE(FMT_TYPE_DATE))
932     {
933       /* Date formats: Minute specifier
934        * Other formats: Literal
935        * Types the format if found
936        */
937       header->type = FMT_TYPE_DATE;
938       NEED_SPACE(sizeof(BYTE));
939       pFormat++;
940       if (*pFormat == 'n' || *pFormat == 'N')
941       {
942         pFormat++;
943         *pOut++ = FMT_DATE_MIN_0;
944         TRACE("nn\n");
945       }
946       else
947       {
948         *pOut++ = FMT_DATE_MIN;
949         TRACE("n\n");
950       }
951       fmt_state &= ~FMT_STATE_OPEN_COPY;
952     }
953     else if ((*pFormat == 'q' || *pFormat == 'Q') && COULD_BE(FMT_TYPE_DATE))
954     {
955       /* Date formats: Quarter specifier
956        * Other formats: Literal
957        * Types the format if found
958        */
959       header->type = FMT_TYPE_DATE;
960       NEED_SPACE(sizeof(BYTE));
961       *pOut++ = FMT_DATE_QUARTER;
962       pFormat++;
963       fmt_state &= ~FMT_STATE_OPEN_COPY;
964       TRACE("quarter\n");
965     }
966     else if ((*pFormat == 's' || *pFormat == 'S') && COULD_BE(FMT_TYPE_DATE))
967     {
968       /* Date formats: Second specifier
969        * Other formats: Literal
970        * Types the format if found
971        */
972       header->type = FMT_TYPE_DATE;
973       NEED_SPACE(sizeof(BYTE));
974       pFormat++;
975       if (*pFormat == 's' || *pFormat == 'S')
976       {
977         pFormat++;
978         *pOut++ = FMT_DATE_SEC_0;
979         TRACE("ss\n");
980       }
981       else
982       {
983         *pOut++ = FMT_DATE_SEC;
984         TRACE("s\n");
985       }
986       fmt_state &= ~FMT_STATE_OPEN_COPY;
987     }
988     else if ((*pFormat == 't' || *pFormat == 'T') &&
989               !strncmpiW(pFormat, szTTTTT, ARRAY_SIZE(szTTTTT)))
990     {
991       /* Date formats: System time specifier
992        * Other formats: Literal
993        * Types the format if found
994        */
995       header->type = FMT_TYPE_DATE;
996       pFormat += ARRAY_SIZE(szTTTTT);
997       NEED_SPACE(sizeof(BYTE));
998       *pOut++ = FMT_DATE_TIME_SYS;
999       fmt_state &= ~FMT_STATE_OPEN_COPY;
1000     }
1001     else if ((*pFormat == 'w' || *pFormat == 'W') && COULD_BE(FMT_TYPE_DATE))
1002     {
1003       /* Date formats: Week of the year/Day of the week
1004        * Other formats: Literal
1005        * Types the format if found
1006        */
1007       header->type = FMT_TYPE_DATE;
1008       pFormat++;
1009       if (*pFormat == 'w' || *pFormat == 'W')
1010       {
1011         NEED_SPACE(3 * sizeof(BYTE));
1012         pFormat++;
1013         *pOut++ = FMT_DATE_WEEK_YEAR;
1014         *pOut++ = nFirstDay;
1015         *pOut++ = nFirstWeek;
1016         TRACE("ww\n");
1017       }
1018       else
1019       {
1020         NEED_SPACE(2 * sizeof(BYTE));
1021         *pOut++ = FMT_DATE_DAY_WEEK;
1022         *pOut++ = nFirstDay;
1023         TRACE("w\n");
1024       }
1025 
1026       fmt_state &= ~FMT_STATE_OPEN_COPY;
1027     }
1028     else if ((*pFormat == 'y' || *pFormat == 'Y') && COULD_BE(FMT_TYPE_DATE))
1029     {
1030       /* Date formats: Day of year/Year specifier
1031        * Other formats: Literal
1032        * Types the format if found
1033        */
1034       int count = -1;
1035       header->type = FMT_TYPE_DATE;
1036       while ((*pFormat == 'y' || *pFormat == 'Y') && count < 4)
1037       {
1038         pFormat++;
1039         count++;
1040       }
1041       if (count == 2)
1042       {
1043         count--; /* 'yyy' has no meaning, despite what MSDN says */
1044         pFormat--;
1045       }
1046       NEED_SPACE(sizeof(BYTE));
1047       *pOut++ = FMT_DATE_YEAR_DOY + count;
1048       fmt_state &= ~FMT_STATE_OPEN_COPY;
1049       TRACE("%d y's\n", count + 1);
1050     }
1051     /* -------------
1052      * String tokens
1053      * -------------
1054      */
1055     else if (*pFormat == '@' && COULD_BE(FMT_TYPE_STRING))
1056     {
1057       /* String formats: Character from string or space if no char
1058        * Other formats: Literal
1059        * Types the format if found
1060        */
1061       header->type = FMT_TYPE_STRING;
1062       NEED_SPACE(2 * sizeof(BYTE));
1063       *pOut++ = FMT_STR_COPY_SPACE;
1064       *pOut = 0x0;
1065       while (*pFormat == '@')
1066       {
1067         *pOut = *pOut + 1;
1068         str_header->copy_chars++;
1069         pFormat++;
1070       }
1071       TRACE("%d @'s\n", *pOut);
1072       pOut++;
1073       fmt_state &= ~FMT_STATE_OPEN_COPY;
1074     }
1075     else if (*pFormat == '&' && COULD_BE(FMT_TYPE_STRING))
1076     {
1077       /* String formats: Character from string or skip if no char
1078        * Other formats: Literal
1079        * Types the format if found
1080        */
1081       header->type = FMT_TYPE_STRING;
1082       NEED_SPACE(2 * sizeof(BYTE));
1083       *pOut++ = FMT_STR_COPY_SKIP;
1084       *pOut = 0x0;
1085       while (*pFormat == '&')
1086       {
1087         *pOut = *pOut + 1;
1088         str_header->copy_chars++;
1089         pFormat++;
1090       }
1091       TRACE("%d &'s\n", *pOut);
1092       pOut++;
1093       fmt_state &= ~FMT_STATE_OPEN_COPY;
1094     }
1095     else if ((*pFormat == '<' || *pFormat == '>') && COULD_BE(FMT_TYPE_STRING))
1096     {
1097       /* String formats: Use upper/lower case
1098        * Other formats: Literal
1099        * Types the format if found
1100        */
1101       header->type = FMT_TYPE_STRING;
1102       if (*pFormat == '<')
1103         str_header->flags |= FMT_FLAG_LT;
1104       else
1105         str_header->flags |= FMT_FLAG_GT;
1106       TRACE("to %s case\n", *pFormat == '<' ? "lower" : "upper");
1107       pFormat++;
1108       fmt_state &= ~FMT_STATE_OPEN_COPY;
1109     }
1110     else if (*pFormat == '!' && COULD_BE(FMT_TYPE_STRING))
1111     {
1112       /* String formats: Copy right to left
1113        * Other formats: Literal
1114        * Types the format if found
1115        */
1116       header->type = FMT_TYPE_STRING;
1117       str_header->flags |= FMT_FLAG_RTL;
1118       pFormat++;
1119       fmt_state &= ~FMT_STATE_OPEN_COPY;
1120       TRACE("copy right-to-left\n");
1121     }
1122     /* --------
1123      * Literals
1124      * --------
1125      */
1126     /* FIXME: [ seems to be ignored */
1127     else
1128     {
1129       if (*pFormat == '%' && header->type == FMT_TYPE_NUMBER)
1130       {
1131         /* Number formats: Percentage indicator, also a literal
1132          * Other formats: Literal
1133          * Doesn't type the format
1134          */
1135         num_header->flags |= FMT_FLAG_PERCENT;
1136       }
1137 
1138       if (fmt_state & FMT_STATE_OPEN_COPY)
1139       {
1140         pOut[-1] = pOut[-1] + 1; /* Increase the length of the open copy */
1141         TRACE("extend copy (char '%c'), length now %d\n", *pFormat, pOut[-1]);
1142       }
1143       else
1144       {
1145         /* Create a new open copy */
1146         TRACE("New copy (char '%c')\n", *pFormat);
1147         NEED_SPACE(3 * sizeof(BYTE));
1148         *pOut++ = FMT_GEN_COPY;
1149         *pOut++ = pFormat - lpszFormat;
1150         *pOut++ = 0x1;
1151         fmt_state |= FMT_STATE_OPEN_COPY;
1152       }
1153       pFormat++;
1154     }
1155   }
1156 
1157   *pOut++ = FMT_GEN_END;
1158 
1159   header->size = pOut - rgbTok;
1160   if (pcbActual)
1161     *pcbActual = header->size;
1162 
1163   return S_OK;
1164 }
1165 
1166 /* Number formatting state flags */
1167 #define NUM_WROTE_DEC  0x01 /* Written the decimal separator */
1168 #define NUM_WRITE_ON   0x02 /* Started to write the number */
1169 #define NUM_WROTE_SIGN 0x04 /* Written the negative sign */
1170 
1171 /* Format a variant using a number format */
1172 static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1173                                     LPBYTE rgbTok, ULONG dwFlags,
1174                                     BSTR *pbstrOut, LCID lcid)
1175 {
1176   BYTE rgbDig[256], *prgbDig;
1177   NUMPARSE np;
1178   int have_int, need_int = 0, have_frac, need_frac, exponent = 0, pad = 0;
1179   WCHAR buff[256], *pBuff = buff;
1180   WCHAR thousandSeparator[32];
1181   VARIANT vString, vBool;
1182   DWORD dwState = 0;
1183   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1184   FMT_NUMBER_HEADER *numHeader;
1185   const BYTE* pToken = NULL;
1186   HRESULT hRes = S_OK;
1187 
1188   TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
1189         rgbTok, dwFlags, pbstrOut, lcid);
1190 
1191   V_VT(&vString) = VT_EMPTY;
1192   V_VT(&vBool) = VT_BOOL;
1193 
1194   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1195   {
1196     have_int = have_frac = 0;
1197     numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNull(header));
1198     V_BOOL(&vBool) = VARIANT_FALSE;
1199   }
1200   else
1201   {
1202     /* Get a number string from pVarIn, and parse it */
1203     hRes = VariantChangeTypeEx(&vString, pVarIn, lcid, VARIANT_NOUSEROVERRIDE, VT_BSTR);
1204     if (FAILED(hRes))
1205       return hRes;
1206 
1207     np.cDig = sizeof(rgbDig);
1208     np.dwInFlags = NUMPRS_STD;
1209     hRes = VarParseNumFromStr(V_BSTR(&vString), lcid, 0, &np, rgbDig);
1210     if (FAILED(hRes))
1211       return hRes;
1212 
1213     have_int = np.cDig;
1214     have_frac = 0;
1215     exponent = np.nPwr10;
1216 
1217     /* Figure out which format to use */
1218     if (np.dwOutFlags & NUMPRS_NEG)
1219     {
1220       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNegative(header));
1221       V_BOOL(&vBool) = VARIANT_TRUE;
1222     }
1223     else if (have_int == 1 && !exponent && rgbDig[0] == 0)
1224     {
1225       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetZero(header));
1226       V_BOOL(&vBool) = VARIANT_FALSE;
1227     }
1228     else
1229     {
1230       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetPositive(header));
1231       V_BOOL(&vBool) = VARIANT_TRUE;
1232     }
1233 
1234     TRACE("num header: flags = 0x%x, mult=%d, div=%d, whole=%d, fract=%d\n",
1235           numHeader->flags, numHeader->multiplier, numHeader->divisor,
1236           numHeader->whole, numHeader->fractional);
1237 
1238     need_int = numHeader->whole;
1239     need_frac = numHeader->fractional;
1240 
1241     if (numHeader->flags & FMT_FLAG_PERCENT &&
1242         !(have_int == 1 && !exponent && rgbDig[0] == 0))
1243       exponent += 2;
1244 
1245     if (numHeader->flags & FMT_FLAG_EXPONENT)
1246     {
1247       /* Exponent format: length of the integral number part is fixed and
1248          specified by the format. */
1249       pad = need_int - have_int;
1250       exponent -= pad;
1251       if (pad < 0)
1252       {
1253         have_int = need_int;
1254         have_frac -= pad;
1255         pad = 0;
1256       }
1257     }
1258     else
1259     {
1260       /* Convert the exponent */
1261       pad = max(exponent, -have_int);
1262       exponent -= pad;
1263       if (pad < 0)
1264       {
1265         have_int += pad;
1266         have_frac = -pad;
1267         pad = 0;
1268       }
1269       if(exponent < 0 && exponent > (-256 + have_int + have_frac))
1270       {
1271         /* Remove exponent notation */
1272         memmove(rgbDig - exponent, rgbDig, have_int + have_frac);
1273         ZeroMemory(rgbDig, -exponent);
1274         have_frac -= exponent;
1275         exponent = 0;
1276       }
1277     }
1278 
1279     /* Rounding the number */
1280     if (have_frac > need_frac)
1281     {
1282       prgbDig = &rgbDig[have_int + need_frac];
1283       have_frac = need_frac;
1284       if (*prgbDig >= 5)
1285       {
1286         while (prgbDig-- > rgbDig && *prgbDig == 9)
1287           *prgbDig = 0;
1288         if (prgbDig < rgbDig)
1289         {
1290           /* We reached the first digit and that was also a 9 */
1291           rgbDig[0] = 1;
1292           if (numHeader->flags & FMT_FLAG_EXPONENT)
1293             exponent++;
1294           else
1295           {
1296             rgbDig[have_int + need_frac] = 0;
1297             if (exponent < 0)
1298               exponent++;
1299             else
1300               have_int++;
1301           }
1302         }
1303         else
1304           (*prgbDig)++;
1305       }
1306       /* We converted trailing digits to zeroes => have_frac has changed */
1307       while (have_frac > 0 && rgbDig[have_int + have_frac - 1] == 0)
1308         have_frac--;
1309     }
1310     TRACE("have_int=%d,need_int=%d,have_frac=%d,need_frac=%d,pad=%d,exp=%d\n",
1311           have_int, need_int, have_frac, need_frac, pad, exponent);
1312   }
1313 
1314   if (numHeader->flags & FMT_FLAG_THOUSANDS)
1315   {
1316     if (!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousandSeparator, ARRAY_SIZE(thousandSeparator)))
1317     {
1318       thousandSeparator[0] = ',';
1319       thousandSeparator[1] = 0;
1320     }
1321   }
1322 
1323   pToken = (const BYTE*)numHeader + sizeof(FMT_NUMBER_HEADER);
1324   prgbDig = rgbDig;
1325 
1326   while (SUCCEEDED(hRes) && *pToken != FMT_GEN_END)
1327   {
1328     WCHAR defaultChar = '?';
1329     DWORD boolFlag, localeValue = 0;
1330     BOOL shouldAdvance = TRUE;
1331 
1332     if (pToken - rgbTok > header->size)
1333     {
1334       ERR("Ran off the end of the format!\n");
1335       hRes = E_INVALIDARG;
1336       goto VARIANT_FormatNumber_Exit;
1337     }
1338 
1339     switch (*pToken)
1340     {
1341     case FMT_GEN_COPY:
1342       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1343       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1344       pBuff += pToken[2];
1345       pToken += 2;
1346       break;
1347 
1348     case FMT_GEN_INLINE:
1349       pToken += 2;
1350       TRACE("copy %s\n", debugstr_a((LPCSTR)pToken));
1351       while (*pToken)
1352         *pBuff++ = *pToken++;
1353       break;
1354 
1355     case FMT_NUM_YES_NO:
1356       boolFlag = VAR_BOOLYESNO;
1357       goto VARIANT_FormatNumber_Bool;
1358 
1359     case FMT_NUM_ON_OFF:
1360       boolFlag = VAR_BOOLONOFF;
1361       goto VARIANT_FormatNumber_Bool;
1362 
1363     case FMT_NUM_TRUE_FALSE:
1364       boolFlag = VAR_LOCALBOOL;
1365 
1366 VARIANT_FormatNumber_Bool:
1367       {
1368         BSTR boolStr = NULL;
1369 
1370         if (pToken[1] != FMT_GEN_END)
1371         {
1372           ERR("Boolean token not at end of format!\n");
1373           hRes = E_INVALIDARG;
1374           goto VARIANT_FormatNumber_Exit;
1375         }
1376         hRes = VarBstrFromBool(V_BOOL(&vBool), lcid, boolFlag, &boolStr);
1377         if (SUCCEEDED(hRes))
1378         {
1379           strcpyW(pBuff, boolStr);
1380           SysFreeString(boolStr);
1381           while (*pBuff)
1382             pBuff++;
1383         }
1384       }
1385       break;
1386 
1387     case FMT_NUM_DECIMAL:
1388       if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
1389       {
1390         /* last chance for a negative sign in the .# case */
1391         TRACE("write negative sign\n");
1392         localeValue = LOCALE_SNEGATIVESIGN;
1393         defaultChar = '-';
1394         dwState |= NUM_WROTE_SIGN;
1395         shouldAdvance = FALSE;
1396         break;
1397       }
1398       TRACE("write decimal separator\n");
1399       localeValue = LOCALE_SDECIMAL;
1400       defaultChar = '.';
1401       dwState |= NUM_WROTE_DEC;
1402       break;
1403 
1404     case FMT_NUM_CURRENCY:
1405       TRACE("write currency symbol\n");
1406       localeValue = LOCALE_SCURRENCY;
1407       defaultChar = '$';
1408       break;
1409 
1410     case FMT_NUM_EXP_POS_U:
1411     case FMT_NUM_EXP_POS_L:
1412     case FMT_NUM_EXP_NEG_U:
1413     case FMT_NUM_EXP_NEG_L:
1414       if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_NEG_L)
1415         *pBuff++ = 'e';
1416       else
1417         *pBuff++ = 'E';
1418       if (exponent < 0)
1419       {
1420         *pBuff++ = '-';
1421         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], -exponent);
1422       }
1423       else
1424       {
1425         if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_POS_U)
1426           *pBuff++ = '+';
1427         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], exponent);
1428       }
1429       while (*pBuff)
1430         pBuff++;
1431       pToken++;
1432       break;
1433 
1434     case FMT_NUM_COPY_ZERO:
1435       dwState |= NUM_WRITE_ON;
1436       /* Fall through */
1437 
1438     case FMT_NUM_COPY_SKIP:
1439       TRACE("write %d %sdigits or %s\n", pToken[1],
1440             dwState & NUM_WROTE_DEC ? "fractional " : "",
1441             *pToken == FMT_NUM_COPY_ZERO ? "0" : "skip");
1442 
1443       if (dwState & NUM_WROTE_DEC)
1444       {
1445         int count, i;
1446 
1447         if (!(numHeader->flags & FMT_FLAG_EXPONENT) && exponent < 0)
1448         {
1449           /* Pad with 0 before writing the fractional digits */
1450           pad = max(exponent, -pToken[1]);
1451           exponent -= pad;
1452           count = min(have_frac, pToken[1] + pad);
1453           for (i = 0; i > pad; i--)
1454             *pBuff++ = '0';
1455         }
1456         else
1457           count = min(have_frac, pToken[1]);
1458 
1459         pad += pToken[1] - count;
1460         have_frac -= count;
1461         while (count--)
1462           *pBuff++ = '0' + *prgbDig++;
1463         if (*pToken == FMT_NUM_COPY_ZERO)
1464         {
1465           for (; pad > 0; pad--)
1466             *pBuff++ = '0'; /* Write zeros for missing trailing digits */
1467         }
1468       }
1469       else
1470       {
1471         int count, count_max, position;
1472 
1473         if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
1474         {
1475           TRACE("write negative sign\n");
1476           localeValue = LOCALE_SNEGATIVESIGN;
1477           defaultChar = '-';
1478           dwState |= NUM_WROTE_SIGN;
1479           shouldAdvance = FALSE;
1480           break;
1481         }
1482 
1483         position = have_int + pad;
1484         if (dwState & NUM_WRITE_ON)
1485           position = max(position, need_int);
1486         need_int -= pToken[1];
1487         count_max = have_int + pad - need_int;
1488         if (count_max < 0)
1489             count_max = 0;
1490         if (dwState & NUM_WRITE_ON)
1491         {
1492           count = pToken[1] - count_max;
1493           TRACE("write %d leading zeros\n", count);
1494           while (count-- > 0)
1495           {
1496             *pBuff++ = '0';
1497             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1498                 position > 1 && (--position % 3) == 0)
1499             {
1500               int k;
1501               TRACE("write thousand separator\n");
1502               for (k = 0; thousandSeparator[k]; k++)
1503                 *pBuff++ = thousandSeparator[k];
1504             }
1505           }
1506         }
1507         if (*pToken == FMT_NUM_COPY_ZERO || have_int > 1 ||
1508             (have_int > 0 && *prgbDig > 0))
1509         {
1510           count = min(count_max, have_int);
1511           count_max -= count;
1512           have_int -= count;
1513           TRACE("write %d whole number digits\n", count);
1514           while (count--)
1515           {
1516             dwState |= NUM_WRITE_ON;
1517             *pBuff++ = '0' + *prgbDig++;
1518             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1519                 position > 1 && (--position % 3) == 0)
1520             {
1521               int k;
1522               TRACE("write thousand separator\n");
1523               for (k = 0; thousandSeparator[k]; k++)
1524                 *pBuff++ = thousandSeparator[k];
1525             }
1526           }
1527         }
1528         count = min(count_max, pad);
1529         pad -= count;
1530         TRACE("write %d whole trailing 0's\n", count);
1531         while (count--)
1532         {
1533           *pBuff++ = '0';
1534           if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1535               position > 1 && (--position % 3) == 0)
1536           {
1537             int k;
1538             TRACE("write thousand separator\n");
1539             for (k = 0; thousandSeparator[k]; k++)
1540               *pBuff++ = thousandSeparator[k];
1541           }
1542         }
1543       }
1544       pToken++;
1545       break;
1546 
1547     default:
1548       ERR("Unknown token 0x%02x!\n", *pToken);
1549       hRes = E_INVALIDARG;
1550       goto VARIANT_FormatNumber_Exit;
1551     }
1552     if (localeValue)
1553     {
1554       if (GetLocaleInfoW(lcid, localeValue, pBuff, ARRAY_SIZE(buff)-(pBuff-buff)))
1555       {
1556         TRACE("added %s\n", debugstr_w(pBuff));
1557         while (*pBuff)
1558           pBuff++;
1559       }
1560       else
1561       {
1562         TRACE("added %d '%c'\n", defaultChar, defaultChar);
1563         *pBuff++ = defaultChar;
1564       }
1565     }
1566     if (shouldAdvance)
1567       pToken++;
1568   }
1569 
1570 VARIANT_FormatNumber_Exit:
1571   VariantClear(&vString);
1572   *pBuff = '\0';
1573   TRACE("buff is %s\n", debugstr_w(buff));
1574   if (SUCCEEDED(hRes))
1575   {
1576     *pbstrOut = SysAllocString(buff);
1577     if (!*pbstrOut)
1578       hRes = E_OUTOFMEMORY;
1579   }
1580   return hRes;
1581 }
1582 
1583 /* Format a variant using a date format */
1584 static HRESULT VARIANT_FormatDate(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1585                                   LPBYTE rgbTok, ULONG dwFlags,
1586                                   BSTR *pbstrOut, LCID lcid)
1587 {
1588   WCHAR buff[256], *pBuff = buff;
1589   VARIANT vDate;
1590   UDATE udate;
1591   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1592   FMT_DATE_HEADER *dateHeader;
1593   const BYTE* pToken = NULL;
1594   HRESULT hRes;
1595 
1596   TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn),
1597         debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, lcid);
1598 
1599   V_VT(&vDate) = VT_EMPTY;
1600 
1601   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1602   {
1603     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetNegative(header));
1604     V_DATE(&vDate) = 0;
1605   }
1606   else
1607   {
1608     USHORT usFlags = dwFlags & VARIANT_CALENDAR_HIJRI ? VAR_CALENDAR_HIJRI : 0;
1609 
1610     hRes = VariantChangeTypeEx(&vDate, pVarIn, lcid, usFlags, VT_DATE);
1611     if (FAILED(hRes))
1612       return hRes;
1613     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetPositive(header));
1614   }
1615 
1616   hRes = VarUdateFromDate(V_DATE(&vDate), 0 /* FIXME: flags? */, &udate);
1617   if (FAILED(hRes))
1618     return hRes;
1619   pToken = (const BYTE*)dateHeader + sizeof(FMT_DATE_HEADER);
1620 
1621   while (*pToken != FMT_GEN_END)
1622   {
1623     DWORD dwVal = 0, localeValue = 0, dwFmt = 0;
1624     LPCWSTR szPrintFmt = NULL;
1625     WCHAR defaultChar = '?';
1626 
1627     if (pToken - rgbTok > header->size)
1628     {
1629       ERR("Ran off the end of the format!\n");
1630       hRes = E_INVALIDARG;
1631       goto VARIANT_FormatDate_Exit;
1632     }
1633 
1634     switch (*pToken)
1635     {
1636     case FMT_GEN_COPY:
1637       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1638       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1639       pBuff += pToken[2];
1640       pToken += 2;
1641       break;
1642 
1643     case FMT_GEN_INLINE:
1644       pToken += 2;
1645       TRACE("copy %s\n", debugstr_a((LPCSTR)pToken));
1646       while (*pToken)
1647         *pBuff++ = *pToken++;
1648       break;
1649 
1650     case FMT_DATE_TIME_SEP:
1651       TRACE("time separator\n");
1652       localeValue = LOCALE_STIME;
1653       defaultChar = ':';
1654       break;
1655 
1656     case FMT_DATE_DATE_SEP:
1657       TRACE("date separator\n");
1658       localeValue = LOCALE_SDATE;
1659       defaultChar = '/';
1660       break;
1661 
1662     case FMT_DATE_GENERAL:
1663       {
1664         BSTR date = NULL;
1665         WCHAR *pDate;
1666         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, 0, &date);
1667         if (FAILED(hRes))
1668           goto VARIANT_FormatDate_Exit;
1669 	pDate = date;
1670         while (*pDate)
1671           *pBuff++ = *pDate++;
1672         SysFreeString(date);
1673       }
1674       break;
1675 
1676     case FMT_DATE_QUARTER:
1677       if (udate.st.wMonth <= 3)
1678         *pBuff++ = '1';
1679       else if (udate.st.wMonth <= 6)
1680         *pBuff++ = '2';
1681       else if (udate.st.wMonth <= 9)
1682         *pBuff++ = '3';
1683       else
1684         *pBuff++ = '4';
1685       break;
1686 
1687     case FMT_DATE_TIME_SYS:
1688       {
1689         /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1690         BSTR date = NULL;
1691         WCHAR *pDate;
1692         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, VAR_TIMEVALUEONLY, &date);
1693         if (FAILED(hRes))
1694           goto VARIANT_FormatDate_Exit;
1695 	pDate = date;
1696         while (*pDate)
1697           *pBuff++ = *pDate++;
1698         SysFreeString(date);
1699       }
1700       break;
1701 
1702     case FMT_DATE_DAY:
1703       szPrintFmt = szPercent_d;
1704       dwVal = udate.st.wDay;
1705       break;
1706 
1707     case FMT_DATE_DAY_0:
1708       szPrintFmt = szPercentZeroTwo_d;
1709       dwVal = udate.st.wDay;
1710       break;
1711 
1712     case FMT_DATE_DAY_SHORT:
1713       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1714       TRACE("short day\n");
1715       localeValue = LOCALE_SABBREVDAYNAME1 + (udate.st.wDayOfWeek + 6)%7;
1716       defaultChar = '?';
1717       break;
1718 
1719     case FMT_DATE_DAY_LONG:
1720       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1721       TRACE("long day\n");
1722       localeValue = LOCALE_SDAYNAME1 + (udate.st.wDayOfWeek + 6)%7;
1723       defaultChar = '?';
1724       break;
1725 
1726     case FMT_DATE_SHORT:
1727       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1728       dwFmt = LOCALE_SSHORTDATE;
1729       break;
1730 
1731     case FMT_DATE_LONG:
1732       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1733       dwFmt = LOCALE_SLONGDATE;
1734       break;
1735 
1736     case FMT_DATE_MEDIUM:
1737       FIXME("Medium date treated as long date\n");
1738       dwFmt = LOCALE_SLONGDATE;
1739       break;
1740 
1741     case FMT_DATE_DAY_WEEK:
1742       szPrintFmt = szPercent_d;
1743       if (pToken[1])
1744         dwVal = udate.st.wDayOfWeek + 2 - pToken[1];
1745       else
1746       {
1747         GetLocaleInfoW(lcid,LOCALE_RETURN_NUMBER|LOCALE_IFIRSTDAYOFWEEK,
1748                        (LPWSTR)&dwVal, sizeof(dwVal)/sizeof(WCHAR));
1749         dwVal = udate.st.wDayOfWeek + 1 - dwVal;
1750       }
1751       pToken++;
1752       break;
1753 
1754     case FMT_DATE_WEEK_YEAR:
1755       szPrintFmt = szPercent_d;
1756       dwVal = udate.wDayOfYear / 7 + 1;
1757       pToken += 2;
1758       FIXME("Ignoring nFirstDay of %d, nFirstWeek of %d\n", pToken[0], pToken[1]);
1759       break;
1760 
1761     case FMT_DATE_MON:
1762       szPrintFmt = szPercent_d;
1763       dwVal = udate.st.wMonth;
1764       break;
1765 
1766     case FMT_DATE_MON_0:
1767       szPrintFmt = szPercentZeroTwo_d;
1768       dwVal = udate.st.wMonth;
1769       break;
1770 
1771     case FMT_DATE_MON_SHORT:
1772       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1773       TRACE("short month\n");
1774       localeValue = LOCALE_SABBREVMONTHNAME1 + udate.st.wMonth - 1;
1775       defaultChar = '?';
1776       break;
1777 
1778     case FMT_DATE_MON_LONG:
1779       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1780       TRACE("long month\n");
1781       localeValue = LOCALE_SMONTHNAME1 + udate.st.wMonth - 1;
1782       defaultChar = '?';
1783       break;
1784 
1785     case FMT_DATE_YEAR_DOY:
1786       szPrintFmt = szPercent_d;
1787       dwVal = udate.wDayOfYear;
1788       break;
1789 
1790     case FMT_DATE_YEAR_0:
1791       szPrintFmt = szPercentZeroTwo_d;
1792       dwVal = udate.st.wYear % 100;
1793       break;
1794 
1795     case FMT_DATE_YEAR_LONG:
1796       szPrintFmt = szPercent_d;
1797       dwVal = udate.st.wYear;
1798       break;
1799 
1800     case FMT_DATE_MIN:
1801       szPrintFmt = szPercent_d;
1802       dwVal = udate.st.wMinute;
1803       break;
1804 
1805     case FMT_DATE_MIN_0:
1806       szPrintFmt = szPercentZeroTwo_d;
1807       dwVal = udate.st.wMinute;
1808       break;
1809 
1810     case FMT_DATE_SEC:
1811       szPrintFmt = szPercent_d;
1812       dwVal = udate.st.wSecond;
1813       break;
1814 
1815     case FMT_DATE_SEC_0:
1816       szPrintFmt = szPercentZeroTwo_d;
1817       dwVal = udate.st.wSecond;
1818       break;
1819 
1820     case FMT_DATE_HOUR:
1821       szPrintFmt = szPercent_d;
1822       dwVal = udate.st.wHour;
1823       break;
1824 
1825     case FMT_DATE_HOUR_0:
1826     case FMT_DATE_TIME_UNK2:
1827       szPrintFmt = szPercentZeroTwo_d;
1828       dwVal = udate.st.wHour;
1829       break;
1830 
1831     case FMT_DATE_HOUR_12:
1832       szPrintFmt = szPercent_d;
1833       dwVal = udate.st.wHour ? udate.st.wHour > 12 ? udate.st.wHour - 12 : udate.st.wHour : 12;
1834       break;
1835 
1836     case FMT_DATE_HOUR_12_0:
1837       szPrintFmt = szPercentZeroTwo_d;
1838       dwVal = udate.st.wHour ? udate.st.wHour > 12 ? udate.st.wHour - 12 : udate.st.wHour : 12;
1839       break;
1840 
1841     case FMT_DATE_AMPM_SYS1:
1842     case FMT_DATE_AMPM_SYS2:
1843       localeValue = udate.st.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
1844       defaultChar = '?';
1845       break;
1846 
1847     case FMT_DATE_AMPM_UPPER:
1848       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
1849       *pBuff++ = 'M';
1850       break;
1851 
1852     case FMT_DATE_A_UPPER:
1853       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
1854       break;
1855 
1856     case FMT_DATE_AMPM_LOWER:
1857       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
1858       *pBuff++ = 'm';
1859       break;
1860 
1861     case FMT_DATE_A_LOWER:
1862       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
1863       break;
1864 
1865     default:
1866       ERR("Unknown token 0x%02x!\n", *pToken);
1867       hRes = E_INVALIDARG;
1868       goto VARIANT_FormatDate_Exit;
1869     }
1870     if (localeValue)
1871     {
1872       *pBuff = '\0';
1873       if (GetLocaleInfoW(lcid, localeValue, pBuff, ARRAY_SIZE(buff)-(pBuff-buff)))
1874       {
1875         TRACE("added %s\n", debugstr_w(pBuff));
1876         while (*pBuff)
1877           pBuff++;
1878       }
1879       else
1880       {
1881         TRACE("added %d %c\n", defaultChar, defaultChar);
1882         *pBuff++ = defaultChar;
1883       }
1884     }
1885     else if (dwFmt)
1886     {
1887       WCHAR fmt_buff[80];
1888 
1889       if (!GetLocaleInfoW(lcid, dwFmt, fmt_buff, ARRAY_SIZE(fmt_buff)) ||
1890           !get_date_format(lcid, 0, &udate.st, fmt_buff, pBuff, ARRAY_SIZE(buff)-(pBuff-buff)))
1891       {
1892         hRes = E_INVALIDARG;
1893         goto VARIANT_FormatDate_Exit;
1894       }
1895       while (*pBuff)
1896         pBuff++;
1897     }
1898     else if (szPrintFmt)
1899     {
1900       sprintfW(pBuff, szPrintFmt, dwVal);
1901       while (*pBuff)
1902         pBuff++;
1903     }
1904     pToken++;
1905   }
1906 
1907 VARIANT_FormatDate_Exit:
1908   *pBuff = '\0';
1909   TRACE("buff is %s\n", debugstr_w(buff));
1910   if (SUCCEEDED(hRes))
1911   {
1912     *pbstrOut = SysAllocString(buff);
1913     if (!*pbstrOut)
1914       hRes = E_OUTOFMEMORY;
1915   }
1916   return hRes;
1917 }
1918 
1919 /* Format a variant using a string format */
1920 static HRESULT VARIANT_FormatString(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1921                                     LPBYTE rgbTok, ULONG dwFlags,
1922                                     BSTR *pbstrOut, LCID lcid)
1923 {
1924   static WCHAR szEmpty[] = { '\0' };
1925   WCHAR buff[256], *pBuff = buff;
1926   WCHAR *pSrc;
1927   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1928   FMT_STRING_HEADER *strHeader;
1929   const BYTE* pToken = NULL;
1930   VARIANT vStr;
1931   int blanks_first;
1932   BOOL bUpper = FALSE;
1933   HRESULT hRes = S_OK;
1934 
1935   TRACE("%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
1936         rgbTok, dwFlags, pbstrOut, lcid);
1937 
1938   V_VT(&vStr) = VT_EMPTY;
1939 
1940   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1941   {
1942     strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
1943     V_BSTR(&vStr) = szEmpty;
1944   }
1945   else
1946   {
1947     hRes = VariantChangeTypeEx(&vStr, pVarIn, lcid, VARIANT_NOUSEROVERRIDE, VT_BSTR);
1948     if (FAILED(hRes))
1949       return hRes;
1950 
1951     if (V_BSTR(&vStr)[0] == '\0')
1952       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
1953     else
1954       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetPositive(header));
1955   }
1956   pSrc = V_BSTR(&vStr);
1957   if ((strHeader->flags & (FMT_FLAG_LT|FMT_FLAG_GT)) == FMT_FLAG_GT)
1958     bUpper = TRUE;
1959   blanks_first = strHeader->copy_chars - strlenW(pSrc);
1960   pToken = (const BYTE*)strHeader + sizeof(FMT_DATE_HEADER);
1961 
1962   while (*pToken != FMT_GEN_END)
1963   {
1964     int dwCount = 0;
1965 
1966     if (pToken - rgbTok > header->size)
1967     {
1968       ERR("Ran off the end of the format!\n");
1969       hRes = E_INVALIDARG;
1970       goto VARIANT_FormatString_Exit;
1971     }
1972 
1973     switch (*pToken)
1974     {
1975     case FMT_GEN_COPY:
1976       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1977       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1978       pBuff += pToken[2];
1979       pToken += 2;
1980       break;
1981 
1982     case FMT_STR_COPY_SPACE:
1983     case FMT_STR_COPY_SKIP:
1984       dwCount = pToken[1];
1985       if (*pToken == FMT_STR_COPY_SPACE && blanks_first > 0)
1986       {
1987         TRACE("insert %d initial spaces\n", blanks_first);
1988         while (dwCount > 0 && blanks_first > 0)
1989         {
1990           *pBuff++ = ' ';
1991           dwCount--;
1992           blanks_first--;
1993         }
1994       }
1995       TRACE("copy %d chars%s\n", dwCount,
1996             *pToken == FMT_STR_COPY_SPACE ? " with space" :"");
1997       while (dwCount > 0 && *pSrc)
1998       {
1999         if (bUpper)
2000           *pBuff++ = toupperW(*pSrc);
2001         else
2002           *pBuff++ = tolowerW(*pSrc);
2003         dwCount--;
2004         pSrc++;
2005       }
2006       if (*pToken == FMT_STR_COPY_SPACE && dwCount > 0)
2007       {
2008         TRACE("insert %d spaces\n", dwCount);
2009         while (dwCount-- > 0)
2010           *pBuff++ = ' ';
2011       }
2012       pToken++;
2013       break;
2014 
2015     default:
2016       ERR("Unknown token 0x%02x!\n", *pToken);
2017       hRes = E_INVALIDARG;
2018       goto VARIANT_FormatString_Exit;
2019     }
2020     pToken++;
2021   }
2022 
2023 VARIANT_FormatString_Exit:
2024   /* Copy out any remaining chars */
2025   while (*pSrc)
2026   {
2027     if (bUpper)
2028       *pBuff++ = toupperW(*pSrc);
2029     else
2030       *pBuff++ = tolowerW(*pSrc);
2031     pSrc++;
2032   }
2033   VariantClear(&vStr);
2034   *pBuff = '\0';
2035   TRACE("buff is %s\n", debugstr_w(buff));
2036   if (SUCCEEDED(hRes))
2037   {
2038     *pbstrOut = SysAllocString(buff);
2039     if (!*pbstrOut)
2040       hRes = E_OUTOFMEMORY;
2041   }
2042   return hRes;
2043 }
2044 
2045 #define NUMBER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2| \
2046                        VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8| \
2047                        VTBIT_R4|VTBIT_R8|VTBIT_CY|VTBIT_DECIMAL| \
2048                        VTBIT_BOOL|VTBIT_INT|VTBIT_UINT)
2049 
2050 /**********************************************************************
2051  *              VarFormatFromTokens [OLEAUT32.139]
2052  */
2053 HRESULT WINAPI VarFormatFromTokens(LPVARIANT pVarIn, LPOLESTR lpszFormat,
2054                                    LPBYTE rgbTok, ULONG dwFlags,
2055                                    BSTR *pbstrOut, LCID lcid)
2056 {
2057   FMT_SHORT_HEADER *header = (FMT_SHORT_HEADER *)rgbTok;
2058   VARIANT vTmp;
2059   HRESULT hres;
2060 
2061   TRACE("(%p,%s,%p,%x,%p,0x%08x)\n", pVarIn, debugstr_w(lpszFormat),
2062           rgbTok, dwFlags, pbstrOut, lcid);
2063 
2064   if (!pbstrOut)
2065     return E_INVALIDARG;
2066 
2067   *pbstrOut = NULL;
2068 
2069   if (!pVarIn || !rgbTok)
2070     return E_INVALIDARG;
2071 
2072   if (V_VT(pVarIn) == VT_NULL)
2073     return S_OK;
2074 
2075   if (*rgbTok == FMT_TO_STRING || header->type == FMT_TYPE_GENERAL)
2076   {
2077     /* According to MSDN, general format acts somewhat like the 'Str'
2078      * function in Visual Basic.
2079      */
2080 VarFormatFromTokens_AsStr:
2081     V_VT(&vTmp) = VT_EMPTY;
2082     hres = VariantChangeTypeEx(&vTmp, pVarIn, lcid, dwFlags, VT_BSTR);
2083     *pbstrOut = V_BSTR(&vTmp);
2084   }
2085   else
2086   {
2087     if (header->type == FMT_TYPE_NUMBER ||
2088         (header->type == FMT_TYPE_UNKNOWN && ((1 << V_TYPE(pVarIn)) & NUMBER_VTBITS)))
2089     {
2090       hres = VARIANT_FormatNumber(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2091     }
2092     else if (header->type == FMT_TYPE_DATE ||
2093              (header->type == FMT_TYPE_UNKNOWN && V_TYPE(pVarIn) == VT_DATE))
2094     {
2095       hres = VARIANT_FormatDate(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2096     }
2097     else if (header->type == FMT_TYPE_STRING || V_TYPE(pVarIn) == VT_BSTR)
2098     {
2099       hres = VARIANT_FormatString(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2100     }
2101     else
2102     {
2103       ERR("unrecognised format type 0x%02x\n", header->type);
2104       return E_INVALIDARG;
2105     }
2106     /* If the coercion failed, still try to create output, unless the
2107      * VAR_FORMAT_NOSUBSTITUTE flag is set.
2108      */
2109     if ((hres == DISP_E_OVERFLOW || hres == DISP_E_TYPEMISMATCH) &&
2110         !(dwFlags & VAR_FORMAT_NOSUBSTITUTE))
2111       goto VarFormatFromTokens_AsStr;
2112   }
2113 
2114   return hres;
2115 }
2116 
2117 /**********************************************************************
2118  *              VarFormat [OLEAUT32.87]
2119  *
2120  * Format a variant from a format string.
2121  *
2122  * PARAMS
2123  *  pVarIn     [I] Variant to format
2124  *  lpszFormat [I] Format string (see notes)
2125  *  nFirstDay  [I] First day of the week, (See VarTokenizeFormatString() for details)
2126  *  nFirstWeek [I] First week of the year (See VarTokenizeFormatString() for details)
2127  *  dwFlags    [I] Flags for the format (VAR_ flags from "oleauto.h")
2128  *  pbstrOut   [O] Destination for formatted string.
2129  *
2130  * RETURNS
2131  *  Success: S_OK. pbstrOut contains the formatted value.
2132  *  Failure: E_INVALIDARG, if any parameter is invalid.
2133  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2134  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2135  *
2136  * NOTES
2137  *  - See Variant-Formats for details concerning creating format strings.
2138  *  - This function uses LOCALE_USER_DEFAULT when calling VarTokenizeFormatString()
2139  *    and VarFormatFromTokens().
2140  */
2141 HRESULT WINAPI VarFormat(LPVARIANT pVarIn, LPOLESTR lpszFormat,
2142                          int nFirstDay, int nFirstWeek, ULONG dwFlags,
2143                          BSTR *pbstrOut)
2144 {
2145   BYTE buff[256];
2146   HRESULT hres;
2147 
2148   TRACE("(%s,%s,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
2149         nFirstDay, nFirstWeek, dwFlags, pbstrOut);
2150 
2151   if (!pbstrOut)
2152     return E_INVALIDARG;
2153   *pbstrOut = NULL;
2154 
2155   hres = VarTokenizeFormatString(lpszFormat, buff, sizeof(buff), nFirstDay,
2156                                  nFirstWeek, LOCALE_USER_DEFAULT, NULL);
2157   if (SUCCEEDED(hres))
2158     hres = VarFormatFromTokens(pVarIn, lpszFormat, buff, dwFlags,
2159                                pbstrOut, LOCALE_USER_DEFAULT);
2160   TRACE("returning 0x%08x, %s\n", hres, debugstr_w(*pbstrOut));
2161   return hres;
2162 }
2163 
2164 /**********************************************************************
2165  *              VarFormatDateTime [OLEAUT32.97]
2166  *
2167  * Format a variant value as a date and/or time.
2168  *
2169  * PARAMS
2170  *  pVarIn    [I] Variant to format
2171  *  nFormat   [I] Format type (see notes)
2172  *  dwFlags   [I] Flags for the format (VAR_ flags from "oleauto.h")
2173  *  pbstrOut  [O] Destination for formatted string.
2174  *
2175  * RETURNS
2176  *  Success: S_OK. pbstrOut contains the formatted value.
2177  *  Failure: E_INVALIDARG, if any parameter is invalid.
2178  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2179  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2180  *
2181  * NOTES
2182  *  This function uses LOCALE_USER_DEFAULT when determining the date format
2183  *  characters to use.
2184  *  Possible values for the nFormat parameter are:
2185  *| Value  Meaning
2186  *| -----  -------
2187  *|   0    General date format
2188  *|   1    Long date format
2189  *|   2    Short date format
2190  *|   3    Long time format
2191  *|   4    Short time format
2192  */
2193 HRESULT WINAPI VarFormatDateTime(LPVARIANT pVarIn, INT nFormat, ULONG dwFlags, BSTR *pbstrOut)
2194 {
2195   static WCHAR szEmpty[] = { '\0' };
2196   const BYTE* lpFmt = NULL;
2197 
2198   TRACE("%s,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nFormat, dwFlags, pbstrOut);
2199 
2200   if (!pVarIn || !pbstrOut || nFormat < 0 || nFormat > 4)
2201     return E_INVALIDARG;
2202 
2203   switch (nFormat)
2204   {
2205   case 0: lpFmt = fmtGeneralDate; break;
2206   case 1: lpFmt = fmtLongDate; break;
2207   case 2: lpFmt = fmtShortDate; break;
2208   case 3: lpFmt = fmtLongTime; break;
2209   case 4: lpFmt = fmtShortTime; break;
2210   }
2211   return VarFormatFromTokens(pVarIn, szEmpty, (BYTE*)lpFmt, dwFlags,
2212                               pbstrOut, LOCALE_USER_DEFAULT);
2213 }
2214 
2215 #define GETLOCALENUMBER(type,field) GetLocaleInfoW(LOCALE_USER_DEFAULT, \
2216                                                    type|LOCALE_RETURN_NUMBER, \
2217                                                    (LPWSTR)&numfmt.field, \
2218                                                    sizeof(numfmt.field)/sizeof(WCHAR))
2219 
2220 /**********************************************************************
2221  *              VarFormatNumber [OLEAUT32.107]
2222  *
2223  * Format a variant value as a number.
2224  *
2225  * PARAMS
2226  *  pVarIn    [I] Variant to format
2227  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2228  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2229  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2230  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2231  *  dwFlags   [I] Currently unused, set to zero
2232  *  pbstrOut  [O] Destination for formatted string.
2233  *
2234  * RETURNS
2235  *  Success: S_OK. pbstrOut contains the formatted value.
2236  *  Failure: E_INVALIDARG, if any parameter is invalid.
2237  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2238  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2239  *
2240  * NOTES
2241  *  This function uses LOCALE_USER_DEFAULT when determining the number format
2242  *  characters to use.
2243  */
2244 HRESULT WINAPI VarFormatNumber(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
2245                                INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
2246 {
2247   HRESULT hRet;
2248   VARIANT vStr;
2249 
2250   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2251         nParens, nGrouping, dwFlags, pbstrOut);
2252 
2253   if (!pVarIn || !pbstrOut || nDigits > 9)
2254     return E_INVALIDARG;
2255 
2256   *pbstrOut = NULL;
2257 
2258   V_VT(&vStr) = VT_EMPTY;
2259   hRet = VariantCopyInd(&vStr, pVarIn);
2260 
2261   if (SUCCEEDED(hRet))
2262     hRet = VariantChangeTypeEx(&vStr, &vStr, LCID_US, 0, VT_BSTR);
2263 
2264   if (SUCCEEDED(hRet))
2265   {
2266     WCHAR buff[256], decimal[8], thousands[8];
2267     NUMBERFMTW numfmt;
2268 
2269     /* Although MSDN makes it clear that the native versions of these functions
2270      * are implemented using VarTokenizeFormatString()/VarFormatFromTokens(),
2271      * using NLS gives us the same result.
2272      */
2273     if (nDigits < 0)
2274       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
2275     else
2276       numfmt.NumDigits = nDigits;
2277 
2278     if (nLeading == -2)
2279       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
2280     else if (nLeading == -1)
2281       numfmt.LeadingZero = 1;
2282     else
2283       numfmt.LeadingZero = 0;
2284 
2285     if (nGrouping == -2)
2286     {
2287       WCHAR grouping[16];
2288       grouping[2] = '\0';
2289       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, grouping, ARRAY_SIZE(grouping));
2290       numfmt.Grouping = grouping[2] == '2' ? 32 : grouping[0] - '0';
2291     }
2292     else if (nGrouping == -1)
2293       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
2294     else
2295       numfmt.Grouping = 0; /* 0 = No grouping */
2296 
2297     if (nParens == -2)
2298       GETLOCALENUMBER(LOCALE_INEGNUMBER, NegativeOrder);
2299     else if (nParens == -1)
2300       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
2301     else
2302       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
2303 
2304     numfmt.lpDecimalSep = decimal;
2305     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal, ARRAY_SIZE(decimal));
2306     numfmt.lpThousandSep = thousands;
2307     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousands, ARRAY_SIZE(thousands));
2308 
2309     if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt, buff, ARRAY_SIZE(buff)))
2310     {
2311       *pbstrOut = SysAllocString(buff);
2312       if (!*pbstrOut)
2313         hRet = E_OUTOFMEMORY;
2314     }
2315     else
2316       hRet = DISP_E_TYPEMISMATCH;
2317 
2318     SysFreeString(V_BSTR(&vStr));
2319   }
2320   return hRet;
2321 }
2322 
2323 /**********************************************************************
2324  *              VarFormatPercent [OLEAUT32.117]
2325  *
2326  * Format a variant value as a percentage.
2327  *
2328  * PARAMS
2329  *  pVarIn    [I] Variant to format
2330  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2331  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2332  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2333  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2334  *  dwFlags   [I] Currently unused, set to zero
2335  *  pbstrOut  [O] Destination for formatted string.
2336  *
2337  * RETURNS
2338  *  Success: S_OK. pbstrOut contains the formatted value.
2339  *  Failure: E_INVALIDARG, if any parameter is invalid.
2340  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2341  *           DISP_E_OVERFLOW, if overflow occurs during the conversion.
2342  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2343  *
2344  * NOTES
2345  *  This function uses LOCALE_USER_DEFAULT when determining the number format
2346  *  characters to use.
2347  */
2348 HRESULT WINAPI VarFormatPercent(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
2349                                 INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
2350 {
2351   static const WCHAR szPercent[] = { '%','\0' };
2352   static const WCHAR szPercentBracket[] = { '%',')','\0' };
2353   WCHAR buff[256];
2354   HRESULT hRet;
2355   VARIANT vDbl;
2356 
2357   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2358         nParens, nGrouping, dwFlags, pbstrOut);
2359 
2360   if (!pVarIn || !pbstrOut || nDigits > 9)
2361     return E_INVALIDARG;
2362 
2363   *pbstrOut = NULL;
2364 
2365   V_VT(&vDbl) = VT_EMPTY;
2366   hRet = VariantCopyInd(&vDbl, pVarIn);
2367 
2368   if (SUCCEEDED(hRet))
2369   {
2370     hRet = VariantChangeTypeEx(&vDbl, &vDbl, LOCALE_USER_DEFAULT, 0, VT_R8);
2371 
2372     if (SUCCEEDED(hRet))
2373     {
2374       if (V_R8(&vDbl) > (R8_MAX / 100.0))
2375         return DISP_E_OVERFLOW;
2376 
2377       V_R8(&vDbl) *= 100.0;
2378       hRet = VarFormatNumber(&vDbl, nDigits, nLeading, nParens,
2379                              nGrouping, dwFlags, pbstrOut);
2380 
2381       if (SUCCEEDED(hRet))
2382       {
2383         DWORD dwLen = strlenW(*pbstrOut);
2384         BOOL bBracket = (*pbstrOut)[dwLen] == ')';
2385 
2386         dwLen -= bBracket;
2387         memcpy(buff, *pbstrOut, dwLen * sizeof(WCHAR));
2388         strcpyW(buff + dwLen, bBracket ? szPercentBracket : szPercent);
2389         SysFreeString(*pbstrOut);
2390         *pbstrOut = SysAllocString(buff);
2391         if (!*pbstrOut)
2392           hRet = E_OUTOFMEMORY;
2393       }
2394     }
2395   }
2396   return hRet;
2397 }
2398 
2399 /**********************************************************************
2400  *              VarFormatCurrency [OLEAUT32.127]
2401  *
2402  * Format a variant value as a currency.
2403  *
2404  * PARAMS
2405  *  pVarIn    [I] Variant to format
2406  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2407  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2408  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2409  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2410  *  dwFlags   [I] Currently unused, set to zero
2411  *  pbstrOut  [O] Destination for formatted string.
2412  *
2413  * RETURNS
2414  *  Success: S_OK. pbstrOut contains the formatted value.
2415  *  Failure: E_INVALIDARG, if any parameter is invalid.
2416  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2417  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2418  *
2419  * NOTES
2420  *  This function uses LOCALE_USER_DEFAULT when determining the currency format
2421  *  characters to use.
2422  */
2423 HRESULT WINAPI VarFormatCurrency(LPVARIANT pVarIn, INT nDigits, INT nLeading,
2424                                  INT nParens, INT nGrouping, ULONG dwFlags,
2425                                  BSTR *pbstrOut)
2426 {
2427   HRESULT hRet;
2428   VARIANT vStr;
2429 
2430   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2431         nParens, nGrouping, dwFlags, pbstrOut);
2432 
2433   if (!pVarIn || !pbstrOut || nDigits > 9)
2434     return E_INVALIDARG;
2435 
2436   *pbstrOut = NULL;
2437 
2438   V_VT(&vStr) = VT_EMPTY;
2439   hRet = VariantCopyInd(&vStr, pVarIn);
2440 
2441   if (SUCCEEDED(hRet))
2442     hRet = VariantChangeTypeEx(&vStr, &vStr, LOCALE_USER_DEFAULT, 0, VT_BSTR);
2443 
2444   if (SUCCEEDED(hRet))
2445   {
2446     WCHAR buff[256], decimal[8], thousands[8], currency[8];
2447     CURRENCYFMTW numfmt;
2448 
2449     if (nDigits < 0)
2450       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
2451     else
2452       numfmt.NumDigits = nDigits;
2453 
2454     if (nLeading == -2)
2455       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
2456     else if (nLeading == -1)
2457       numfmt.LeadingZero = 1;
2458     else
2459       numfmt.LeadingZero = 0;
2460 
2461     if (nGrouping == -2)
2462     {
2463       WCHAR grouping[16];
2464       grouping[2] = '\0';
2465       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, grouping, ARRAY_SIZE(grouping));
2466       numfmt.Grouping = grouping[2] == '2' ? 32 : grouping[0] - '0';
2467     }
2468     else if (nGrouping == -1)
2469       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
2470     else
2471       numfmt.Grouping = 0; /* 0 = No grouping */
2472 
2473     if (nParens == -2)
2474       GETLOCALENUMBER(LOCALE_INEGCURR, NegativeOrder);
2475     else if (nParens == -1)
2476       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
2477     else
2478       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
2479 
2480     GETLOCALENUMBER(LOCALE_ICURRENCY, PositiveOrder);
2481 
2482     numfmt.lpDecimalSep = decimal;
2483     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal, ARRAY_SIZE(decimal));
2484     numfmt.lpThousandSep = thousands;
2485     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, thousands, ARRAY_SIZE(thousands));
2486     numfmt.lpCurrencySymbol = currency;
2487     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, currency, ARRAY_SIZE(currency));
2488 
2489     /* use NLS as per VarFormatNumber() */
2490     if (GetCurrencyFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt, buff, ARRAY_SIZE(buff)))
2491     {
2492       *pbstrOut = SysAllocString(buff);
2493       if (!*pbstrOut)
2494         hRet = E_OUTOFMEMORY;
2495     }
2496     else
2497       hRet = DISP_E_TYPEMISMATCH;
2498 
2499     SysFreeString(V_BSTR(&vStr));
2500   }
2501   return hRet;
2502 }
2503 
2504 /**********************************************************************
2505  *              VarMonthName [OLEAUT32.129]
2506  *
2507  * Print the specified month as localized name.
2508  *
2509  * PARAMS
2510  *  iMonth    [I] month number 1..12
2511  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
2512  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
2513  *  pbstrOut  [O] Destination for month name
2514  *
2515  * RETURNS
2516  *  Success: S_OK. pbstrOut contains the name.
2517  *  Failure: E_INVALIDARG, if any parameter is invalid.
2518  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2519  */
2520 HRESULT WINAPI VarMonthName(INT iMonth, INT fAbbrev, ULONG dwFlags, BSTR *pbstrOut)
2521 {
2522   DWORD localeValue;
2523   INT size;
2524 
2525   if ((iMonth < 1)  || (iMonth > 12))
2526     return E_INVALIDARG;
2527 
2528   if (dwFlags)
2529     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
2530 
2531   if (fAbbrev)
2532 	localeValue = LOCALE_SABBREVMONTHNAME1 + iMonth - 1;
2533   else
2534 	localeValue = LOCALE_SMONTHNAME1 + iMonth - 1;
2535 
2536   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, NULL, 0);
2537   if (!size) {
2538     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2539     return HRESULT_FROM_WIN32(GetLastError());
2540   }
2541   *pbstrOut = SysAllocStringLen(NULL,size - 1);
2542   if (!*pbstrOut)
2543     return E_OUTOFMEMORY;
2544   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, *pbstrOut, size);
2545   if (!size) {
2546     ERR("GetLocaleInfo of 0x%x failed in 2nd stage?!\n", localeValue);
2547     SysFreeString(*pbstrOut);
2548     return HRESULT_FROM_WIN32(GetLastError());
2549   }
2550   return S_OK;
2551 }
2552 
2553 /**********************************************************************
2554  *              VarWeekdayName [OLEAUT32.129]
2555  *
2556  * Print the specified weekday as localized name.
2557  *
2558  * PARAMS
2559  *  iWeekday  [I] day of week, 1..7, 1="the first day of the week"
2560  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
2561  *  iFirstDay [I] first day of week,
2562  *                0=system default, 1=Sunday, 2=Monday, .. (contrary to MSDN)
2563  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
2564  *  pbstrOut  [O] Destination for weekday name.
2565  *
2566  * RETURNS
2567  *  Success: S_OK, pbstrOut contains the name.
2568  *  Failure: E_INVALIDARG, if any parameter is invalid.
2569  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2570  */
2571 HRESULT WINAPI VarWeekdayName(INT iWeekday, INT fAbbrev, INT iFirstDay,
2572                               ULONG dwFlags, BSTR *pbstrOut)
2573 {
2574   DWORD localeValue;
2575   INT size;
2576 
2577   /* Windows XP oleaut32.dll doesn't allow iWekday==0, contrary to MSDN */
2578   if (iWeekday < 1 || iWeekday > 7)
2579     return E_INVALIDARG;
2580   if (iFirstDay < 0 || iFirstDay > 7)
2581     return E_INVALIDARG;
2582   if (!pbstrOut)
2583     return E_INVALIDARG;
2584 
2585   if (dwFlags)
2586     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
2587 
2588   /* If we have to use the default firstDay, find which one it is */
2589   if (iFirstDay == 0) {
2590     DWORD firstDay;
2591     localeValue = LOCALE_RETURN_NUMBER | LOCALE_IFIRSTDAYOFWEEK;
2592     size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue,
2593                           (LPWSTR)&firstDay, sizeof(firstDay) / sizeof(WCHAR));
2594     if (!size) {
2595       ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2596       return HRESULT_FROM_WIN32(GetLastError());
2597     }
2598     iFirstDay = firstDay + 2;
2599   }
2600 
2601   /* Determine what we need to return */
2602   localeValue = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
2603   localeValue += (7 + iWeekday - 1 + iFirstDay - 2) % 7;
2604 
2605   /* Determine the size of the data, allocate memory and retrieve the data */
2606   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, NULL, 0);
2607   if (!size) {
2608     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2609     return HRESULT_FROM_WIN32(GetLastError());
2610   }
2611   *pbstrOut = SysAllocStringLen(NULL, size - 1);
2612   if (!*pbstrOut)
2613     return E_OUTOFMEMORY;
2614   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, *pbstrOut, size);
2615   if (!size) {
2616     ERR("GetLocaleInfo 0x%x failed in 2nd stage?!\n", localeValue);
2617     SysFreeString(*pbstrOut);
2618     return HRESULT_FROM_WIN32(GetLastError());
2619   }
2620   return S_OK;
2621 }
2622