xref: /reactos/dll/win32/oleaut32/varformat.c (revision 595b846d)
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,
453                                  sizeof(VARIANT_NamedFormats)/sizeof(NAMED_FORMAT),
454                                  sizeof(NAMED_FORMAT), FormatCompareFn);
455   return fmt ? fmt->format : NULL;
456 }
457 
458 /* Return an error if the token for the value will not fit in the destination */
459 #define NEED_SPACE(x) if (cbTok < (int)(x)) return TYPE_E_BUFFERTOOSMALL; cbTok -= (x)
460 
461 /* Non-zero if the format is unknown or a given type */
462 #define COULD_BE(typ) ((!fmt_number && header->type==FMT_TYPE_UNKNOWN)||header->type==typ)
463 
464 /* State during tokenising */
465 #define FMT_STATE_OPEN_COPY     0x1 /* Last token written was a copy */
466 #define FMT_STATE_WROTE_DECIMAL 0x2 /* Already wrote a decimal separator */
467 #define FMT_STATE_SEEN_HOURS    0x4 /* See the hh specifier */
468 #define FMT_STATE_WROTE_MINUTES 0x8 /* Wrote minutes */
469 
470 /**********************************************************************
471  *              VarTokenizeFormatString [OLEAUT32.140]
472  *
473  * Convert a format string into tokenised form.
474  *
475  * PARAMS
476  *  lpszFormat [I] Format string to tokenise
477  *  rgbTok     [O] Destination for tokenised format
478  *  cbTok      [I] Size of rgbTok in bytes
479  *  nFirstDay  [I] First day of the week (1-7, or 0 for current system default)
480  *  nFirstWeek [I] How to treat the first week (see notes)
481  *  lcid       [I] Locale Id of the format string
482  *  pcbActual  [O] If non-NULL, filled with the first token generated
483  *
484  * RETURNS
485  *  Success: S_OK. rgbTok contains the tokenised format.
486  *  Failure: E_INVALIDARG, if any argument is invalid.
487  *           TYPE_E_BUFFERTOOSMALL, if rgbTok is not large enough.
488  *
489  * NOTES
490  * Valid values for the nFirstWeek parameter are:
491  *| Value  Meaning
492  *| -----  -------
493  *|   0    Use the current system default
494  *|   1    The first week is that containing Jan 1
495  *|   2    Four or more days of the first week are in the current year
496  *|   3    The first week is 7 days long
497  * See Variant-Formats(), VarFormatFromTokens().
498  */
499 HRESULT WINAPI VarTokenizeFormatString(LPOLESTR lpszFormat, LPBYTE rgbTok,
500                                        int cbTok, int nFirstDay, int nFirstWeek,
501                                        LCID lcid, int *pcbActual)
502 {
503   /* Note: none of these strings should be NUL terminated */
504   static const WCHAR szTTTTT[] = { 't','t','t','t','t' };
505   static const WCHAR szAMPM[] = { 'A','M','P','M' };
506   static const WCHAR szampm[] = { 'a','m','p','m' };
507   static const WCHAR szAMSlashPM[] = { 'A','M','/','P','M' };
508   static const WCHAR szamSlashpm[] = { 'a','m','/','p','m' };
509   const BYTE *namedFmt;
510   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
511   FMT_STRING_HEADER *str_header = (FMT_STRING_HEADER*)(rgbTok + sizeof(FMT_HEADER));
512   FMT_NUMBER_HEADER *num_header = (FMT_NUMBER_HEADER*)str_header;
513   BYTE* pOut = rgbTok + sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER);
514   BYTE* pLastHours = NULL;
515   BYTE fmt_number = 0;
516   DWORD fmt_state = 0;
517   LPCWSTR pFormat = lpszFormat;
518 
519   TRACE("(%s,%p,%d,%d,%d,0x%08x,%p)\n", debugstr_w(lpszFormat), rgbTok, cbTok,
520         nFirstDay, nFirstWeek, lcid, pcbActual);
521 
522   if (!rgbTok ||
523       nFirstDay < 0 || nFirstDay > 7 || nFirstWeek < 0 || nFirstWeek > 3)
524     return E_INVALIDARG;
525 
526   if (!lpszFormat || !*lpszFormat)
527   {
528     /* An empty string means 'general format' */
529     NEED_SPACE(sizeof(BYTE));
530     *rgbTok = FMT_TO_STRING;
531     if (pcbActual)
532       *pcbActual = FMT_TO_STRING;
533     return S_OK;
534   }
535 
536   if (cbTok > 255)
537     cbTok = 255; /* Ensure we error instead of wrapping */
538 
539   /* Named formats */
540   namedFmt = VARIANT_GetNamedFormat(lpszFormat);
541   if (namedFmt)
542   {
543     NEED_SPACE(namedFmt[0]);
544     memcpy(rgbTok, namedFmt, namedFmt[0]);
545     TRACE("Using pre-tokenised named format %s\n", debugstr_w(lpszFormat));
546     /* FIXME: pcbActual */
547     return S_OK;
548   }
549 
550   /* Insert header */
551   NEED_SPACE(sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER));
552   memset(header, 0, sizeof(FMT_HEADER));
553   memset(str_header, 0, sizeof(FMT_STRING_HEADER));
554 
555   header->starts[fmt_number] = sizeof(FMT_HEADER);
556 
557   while (*pFormat)
558   {
559     /* --------------
560      * General tokens
561      * --------------
562      */
563     if (*pFormat == ';')
564     {
565       while (*pFormat == ';')
566       {
567         TRACE(";\n");
568         if (++fmt_number > 3)
569           return E_INVALIDARG; /* too many formats */
570         pFormat++;
571       }
572       if (*pFormat)
573       {
574         TRACE("New header\n");
575         NEED_SPACE(sizeof(BYTE) + sizeof(FMT_STRING_HEADER));
576         *pOut++ = FMT_GEN_END;
577 
578         header->starts[fmt_number] = pOut - rgbTok;
579         str_header = (FMT_STRING_HEADER*)pOut;
580         num_header = (FMT_NUMBER_HEADER*)pOut;
581         memset(str_header, 0, sizeof(FMT_STRING_HEADER));
582         pOut += sizeof(FMT_STRING_HEADER);
583         fmt_state = 0;
584         pLastHours = NULL;
585       }
586     }
587     else if (*pFormat == '\\')
588     {
589       /* Escaped character */
590       if (pFormat[1])
591       {
592         NEED_SPACE(3 * sizeof(BYTE));
593         pFormat++;
594         *pOut++ = FMT_GEN_COPY;
595         *pOut++ = pFormat - lpszFormat;
596         *pOut++ = 0x1;
597         fmt_state |= FMT_STATE_OPEN_COPY;
598         TRACE("'\\'\n");
599       }
600       else
601         fmt_state &= ~FMT_STATE_OPEN_COPY;
602       pFormat++;
603     }
604     else if (*pFormat == '"')
605     {
606       /* Escaped string
607        * Note: Native encodes "" as a copy of length zero. That's just dumb, so
608        * here we avoid encoding anything in this case.
609        */
610       if (!pFormat[1])
611         pFormat++;
612       else if (pFormat[1] == '"')
613       {
614         pFormat += 2;
615       }
616       else
617       {
618         LPCWSTR start = ++pFormat;
619         while (*pFormat && *pFormat != '"')
620           pFormat++;
621         NEED_SPACE(3 * sizeof(BYTE));
622         *pOut++ = FMT_GEN_COPY;
623         *pOut++ = start - lpszFormat;
624         *pOut++ = pFormat - start;
625         if (*pFormat == '"')
626           pFormat++;
627         TRACE("Quoted string pos %d, len %d\n", pOut[-2], pOut[-1]);
628       }
629       fmt_state &= ~FMT_STATE_OPEN_COPY;
630     }
631     /* -------------
632      * Number tokens
633      * -------------
634      */
635     else if (*pFormat == '0' && COULD_BE(FMT_TYPE_NUMBER))
636     {
637       /* Number formats: Digit from number or '0' if no digits
638        * Other formats: Literal
639        * Types the format if found
640        */
641       header->type = FMT_TYPE_NUMBER;
642       NEED_SPACE(2 * sizeof(BYTE));
643       *pOut++ = FMT_NUM_COPY_ZERO;
644       *pOut = 0x0;
645       while (*pFormat == '0')
646       {
647         *pOut = *pOut + 1;
648         pFormat++;
649       }
650       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
651         num_header->fractional += *pOut;
652       else
653         num_header->whole += *pOut;
654       TRACE("%d 0's\n", *pOut);
655       pOut++;
656       fmt_state &= ~FMT_STATE_OPEN_COPY;
657     }
658     else if (*pFormat == '#' && COULD_BE(FMT_TYPE_NUMBER))
659     {
660       /* Number formats: Digit from number or blank if no digits
661        * Other formats: Literal
662        * Types the format if found
663        */
664       header->type = FMT_TYPE_NUMBER;
665       NEED_SPACE(2 * sizeof(BYTE));
666       *pOut++ = FMT_NUM_COPY_SKIP;
667       *pOut = 0x0;
668       while (*pFormat == '#')
669       {
670         *pOut = *pOut + 1;
671         pFormat++;
672       }
673       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
674         num_header->fractional += *pOut;
675       else
676         num_header->whole += *pOut;
677       TRACE("%d #'s\n", *pOut);
678       pOut++;
679       fmt_state &= ~FMT_STATE_OPEN_COPY;
680     }
681     else if (*pFormat == '.' && COULD_BE(FMT_TYPE_NUMBER) &&
682               !(fmt_state & FMT_STATE_WROTE_DECIMAL))
683     {
684       /* Number formats: Decimal separator when 1st seen, literal thereafter
685        * Other formats: Literal
686        * Types the format if found
687        */
688       header->type = FMT_TYPE_NUMBER;
689       NEED_SPACE(sizeof(BYTE));
690       *pOut++ = FMT_NUM_DECIMAL;
691       fmt_state |= FMT_STATE_WROTE_DECIMAL;
692       fmt_state &= ~FMT_STATE_OPEN_COPY;
693       pFormat++;
694       TRACE("decimal sep\n");
695     }
696     else if ((*pFormat == 'e' || *pFormat == 'E') && (pFormat[1] == '-' ||
697               pFormat[1] == '+') && header->type == FMT_TYPE_NUMBER)
698     {
699       /* Number formats: Exponent specifier
700        * Other formats: Literal
701        */
702       num_header->flags |= FMT_FLAG_EXPONENT;
703       NEED_SPACE(2 * sizeof(BYTE));
704       if (*pFormat == 'e') {
705         if (pFormat[1] == '+')
706           *pOut = FMT_NUM_EXP_POS_L;
707         else
708           *pOut = FMT_NUM_EXP_NEG_L;
709       } else {
710         if (pFormat[1] == '+')
711           *pOut = FMT_NUM_EXP_POS_U;
712         else
713           *pOut = FMT_NUM_EXP_NEG_U;
714       }
715       pFormat += 2;
716       *++pOut = 0x0;
717       while (*pFormat == '0')
718       {
719         *pOut = *pOut + 1;
720         pFormat++;
721       }
722       pOut++;
723       TRACE("exponent\n");
724     }
725     /* FIXME: %% => Divide by 1000 */
726     else if (*pFormat == ',' && header->type == FMT_TYPE_NUMBER)
727     {
728       /* Number formats: Use the thousands separator
729        * Other formats: Literal
730        */
731       num_header->flags |= FMT_FLAG_THOUSANDS;
732       pFormat++;
733       fmt_state &= ~FMT_STATE_OPEN_COPY;
734       TRACE("thousands sep\n");
735     }
736     /* -----------
737      * Date tokens
738      * -----------
739      */
740     else if (*pFormat == '/' && COULD_BE(FMT_TYPE_DATE))
741     {
742       /* Date formats: Date separator
743        * Other formats: Literal
744        * Types the format if found
745        */
746       header->type = FMT_TYPE_DATE;
747       NEED_SPACE(sizeof(BYTE));
748       *pOut++ = FMT_DATE_DATE_SEP;
749       pFormat++;
750       fmt_state &= ~FMT_STATE_OPEN_COPY;
751       TRACE("date sep\n");
752     }
753     else if (*pFormat == ':' && COULD_BE(FMT_TYPE_DATE))
754     {
755       /* Date formats: Time separator
756        * Other formats: Literal
757        * Types the format if found
758        */
759       header->type = FMT_TYPE_DATE;
760       NEED_SPACE(sizeof(BYTE));
761       *pOut++ = FMT_DATE_TIME_SEP;
762       pFormat++;
763       fmt_state &= ~FMT_STATE_OPEN_COPY;
764       TRACE("time sep\n");
765     }
766     else if ((*pFormat == 'a' || *pFormat == 'A') &&
767               !strncmpiW(pFormat, szAMPM, sizeof(szAMPM)/sizeof(WCHAR)))
768     {
769       /* Date formats: System AM/PM designation
770        * Other formats: Literal
771        * Types the format if found
772        */
773       header->type = FMT_TYPE_DATE;
774       NEED_SPACE(sizeof(BYTE));
775       pFormat += sizeof(szAMPM)/sizeof(WCHAR);
776       if (!strncmpW(pFormat, szampm, sizeof(szampm)/sizeof(WCHAR)))
777         *pOut++ = FMT_DATE_AMPM_SYS2;
778       else
779         *pOut++ = FMT_DATE_AMPM_SYS1;
780       if (pLastHours)
781         *pLastHours = *pLastHours + 2;
782       TRACE("ampm\n");
783     }
784     else if (*pFormat == 'a' && pFormat[1] == '/' &&
785               (pFormat[2] == 'p' || pFormat[2] == 'P'))
786     {
787       /* Date formats: lowercase a or p designation
788        * Other formats: Literal
789        * Types the format if found
790        */
791       header->type = FMT_TYPE_DATE;
792       NEED_SPACE(sizeof(BYTE));
793       pFormat += 3;
794       *pOut++ = FMT_DATE_A_LOWER;
795       if (pLastHours)
796         *pLastHours = *pLastHours + 2;
797       TRACE("a/p\n");
798     }
799     else if (*pFormat == 'A' && pFormat[1] == '/' &&
800               (pFormat[2] == 'p' || pFormat[2] == 'P'))
801     {
802       /* Date formats: Uppercase a or p designation
803        * Other formats: Literal
804        * Types the format if found
805        */
806       header->type = FMT_TYPE_DATE;
807       NEED_SPACE(sizeof(BYTE));
808       pFormat += 3;
809       *pOut++ = FMT_DATE_A_UPPER;
810       if (pLastHours)
811         *pLastHours = *pLastHours + 2;
812       TRACE("A/P\n");
813     }
814     else if (*pFormat == 'a' &&
815               !strncmpW(pFormat, szamSlashpm, sizeof(szamSlashpm)/sizeof(WCHAR)))
816     {
817       /* Date formats: lowercase AM or PM designation
818        * Other formats: Literal
819        * Types the format if found
820        */
821       header->type = FMT_TYPE_DATE;
822       NEED_SPACE(sizeof(BYTE));
823       pFormat += sizeof(szamSlashpm)/sizeof(WCHAR);
824       *pOut++ = FMT_DATE_AMPM_LOWER;
825       if (pLastHours)
826         *pLastHours = *pLastHours + 2;
827       TRACE("AM/PM\n");
828     }
829     else if (*pFormat == 'A' &&
830               !strncmpW(pFormat, szAMSlashPM, sizeof(szAMSlashPM)/sizeof(WCHAR)))
831     {
832       /* Date formats: Uppercase AM or PM designation
833        * Other formats: Literal
834        * Types the format if found
835        */
836       header->type = FMT_TYPE_DATE;
837       NEED_SPACE(sizeof(BYTE));
838       pFormat += sizeof(szAMSlashPM)/sizeof(WCHAR);
839       *pOut++ = FMT_DATE_AMPM_UPPER;
840       TRACE("AM/PM\n");
841     }
842     else if ((*pFormat == 'c' || *pFormat == 'C') && COULD_BE(FMT_TYPE_DATE))
843     {
844       /* Date formats: General date format
845        * Other formats: Literal
846        * Types the format if found
847        */
848       header->type = FMT_TYPE_DATE;
849       NEED_SPACE(sizeof(BYTE));
850       pFormat += sizeof(szAMSlashPM)/sizeof(WCHAR);
851       *pOut++ = FMT_DATE_GENERAL;
852       TRACE("gen date\n");
853     }
854     else if ((*pFormat == 'd' || *pFormat == 'D') && COULD_BE(FMT_TYPE_DATE))
855     {
856       /* Date formats: Day specifier
857        * Other formats: Literal
858        * Types the format if found
859        */
860       int count = -1;
861       header->type = FMT_TYPE_DATE;
862       while ((*pFormat == 'd' || *pFormat == 'D') && count < 6)
863       {
864         pFormat++;
865         count++;
866       }
867       NEED_SPACE(sizeof(BYTE));
868       *pOut++ = FMT_DATE_DAY + count;
869       fmt_state &= ~FMT_STATE_OPEN_COPY;
870       /* When we find the days token, reset the seen hours state so that
871        * 'mm' is again written as month when encountered.
872        */
873       fmt_state &= ~FMT_STATE_SEEN_HOURS;
874       TRACE("%d d's\n", count + 1);
875     }
876     else if ((*pFormat == 'h' || *pFormat == 'H') && COULD_BE(FMT_TYPE_DATE))
877     {
878       /* Date formats: Hour specifier
879        * Other formats: Literal
880        * Types the format if found
881        */
882       header->type = FMT_TYPE_DATE;
883       NEED_SPACE(sizeof(BYTE));
884       pFormat++;
885       /* Record the position of the hours specifier - if we encounter
886        * an am/pm specifier we will change the hours from 24 to 12.
887        */
888       pLastHours = pOut;
889       if (*pFormat == 'h' || *pFormat == 'H')
890       {
891         pFormat++;
892         *pOut++ = FMT_DATE_HOUR_0;
893         TRACE("hh\n");
894       }
895       else
896       {
897         *pOut++ = FMT_DATE_HOUR;
898         TRACE("h\n");
899       }
900       fmt_state &= ~FMT_STATE_OPEN_COPY;
901       /* Note that now we have seen an hours token, the next occurrence of
902        * 'mm' indicates minutes, not months.
903        */
904       fmt_state |= FMT_STATE_SEEN_HOURS;
905     }
906     else if ((*pFormat == 'm' || *pFormat == 'M') && COULD_BE(FMT_TYPE_DATE))
907     {
908       /* Date formats: Month specifier (or Minute specifier, after hour specifier)
909        * Other formats: Literal
910        * Types the format if found
911        */
912       int count = -1;
913       header->type = FMT_TYPE_DATE;
914       while ((*pFormat == 'm' || *pFormat == 'M') && count < 4)
915       {
916         pFormat++;
917         count++;
918       }
919       NEED_SPACE(sizeof(BYTE));
920       if (count <= 1 && fmt_state & FMT_STATE_SEEN_HOURS &&
921           !(fmt_state & FMT_STATE_WROTE_MINUTES))
922       {
923         /* We have seen an hours specifier and not yet written a minutes
924          * specifier. Write this as minutes and thereafter as months.
925          */
926         *pOut++ = count == 1 ? FMT_DATE_MIN_0 : FMT_DATE_MIN;
927         fmt_state |= FMT_STATE_WROTE_MINUTES; /* Hereafter write months */
928       }
929       else
930         *pOut++ = FMT_DATE_MON + count; /* Months */
931       fmt_state &= ~FMT_STATE_OPEN_COPY;
932       TRACE("%d m's\n", count + 1);
933     }
934     else if ((*pFormat == 'n' || *pFormat == 'N') && COULD_BE(FMT_TYPE_DATE))
935     {
936       /* Date formats: Minute specifier
937        * Other formats: Literal
938        * Types the format if found
939        */
940       header->type = FMT_TYPE_DATE;
941       NEED_SPACE(sizeof(BYTE));
942       pFormat++;
943       if (*pFormat == 'n' || *pFormat == 'N')
944       {
945         pFormat++;
946         *pOut++ = FMT_DATE_MIN_0;
947         TRACE("nn\n");
948       }
949       else
950       {
951         *pOut++ = FMT_DATE_MIN;
952         TRACE("n\n");
953       }
954       fmt_state &= ~FMT_STATE_OPEN_COPY;
955     }
956     else if ((*pFormat == 'q' || *pFormat == 'Q') && COULD_BE(FMT_TYPE_DATE))
957     {
958       /* Date formats: Quarter specifier
959        * Other formats: Literal
960        * Types the format if found
961        */
962       header->type = FMT_TYPE_DATE;
963       NEED_SPACE(sizeof(BYTE));
964       *pOut++ = FMT_DATE_QUARTER;
965       pFormat++;
966       fmt_state &= ~FMT_STATE_OPEN_COPY;
967       TRACE("quarter\n");
968     }
969     else if ((*pFormat == 's' || *pFormat == 'S') && COULD_BE(FMT_TYPE_DATE))
970     {
971       /* Date formats: Second specifier
972        * Other formats: Literal
973        * Types the format if found
974        */
975       header->type = FMT_TYPE_DATE;
976       NEED_SPACE(sizeof(BYTE));
977       pFormat++;
978       if (*pFormat == 's' || *pFormat == 'S')
979       {
980         pFormat++;
981         *pOut++ = FMT_DATE_SEC_0;
982         TRACE("ss\n");
983       }
984       else
985       {
986         *pOut++ = FMT_DATE_SEC;
987         TRACE("s\n");
988       }
989       fmt_state &= ~FMT_STATE_OPEN_COPY;
990     }
991     else if ((*pFormat == 't' || *pFormat == 'T') &&
992               !strncmpiW(pFormat, szTTTTT, sizeof(szTTTTT)/sizeof(WCHAR)))
993     {
994       /* Date formats: System time specifier
995        * Other formats: Literal
996        * Types the format if found
997        */
998       header->type = FMT_TYPE_DATE;
999       pFormat += sizeof(szTTTTT)/sizeof(WCHAR);
1000       NEED_SPACE(sizeof(BYTE));
1001       *pOut++ = FMT_DATE_TIME_SYS;
1002       fmt_state &= ~FMT_STATE_OPEN_COPY;
1003     }
1004     else if ((*pFormat == 'w' || *pFormat == 'W') && COULD_BE(FMT_TYPE_DATE))
1005     {
1006       /* Date formats: Week of the year/Day of the week
1007        * Other formats: Literal
1008        * Types the format if found
1009        */
1010       header->type = FMT_TYPE_DATE;
1011       pFormat++;
1012       if (*pFormat == 'w' || *pFormat == 'W')
1013       {
1014         NEED_SPACE(3 * sizeof(BYTE));
1015         pFormat++;
1016         *pOut++ = FMT_DATE_WEEK_YEAR;
1017         *pOut++ = nFirstDay;
1018         *pOut++ = nFirstWeek;
1019         TRACE("ww\n");
1020       }
1021       else
1022       {
1023         NEED_SPACE(2 * sizeof(BYTE));
1024         *pOut++ = FMT_DATE_DAY_WEEK;
1025         *pOut++ = nFirstDay;
1026         TRACE("w\n");
1027       }
1028 
1029       fmt_state &= ~FMT_STATE_OPEN_COPY;
1030     }
1031     else if ((*pFormat == 'y' || *pFormat == 'Y') && COULD_BE(FMT_TYPE_DATE))
1032     {
1033       /* Date formats: Day of year/Year specifier
1034        * Other formats: Literal
1035        * Types the format if found
1036        */
1037       int count = -1;
1038       header->type = FMT_TYPE_DATE;
1039       while ((*pFormat == 'y' || *pFormat == 'Y') && count < 4)
1040       {
1041         pFormat++;
1042         count++;
1043       }
1044       if (count == 2)
1045       {
1046         count--; /* 'yyy' has no meaning, despite what MSDN says */
1047         pFormat--;
1048       }
1049       NEED_SPACE(sizeof(BYTE));
1050       *pOut++ = FMT_DATE_YEAR_DOY + count;
1051       fmt_state &= ~FMT_STATE_OPEN_COPY;
1052       TRACE("%d y's\n", count + 1);
1053     }
1054     /* -------------
1055      * String tokens
1056      * -------------
1057      */
1058     else if (*pFormat == '@' && COULD_BE(FMT_TYPE_STRING))
1059     {
1060       /* String formats: Character from string or space if no char
1061        * Other formats: Literal
1062        * Types the format if found
1063        */
1064       header->type = FMT_TYPE_STRING;
1065       NEED_SPACE(2 * sizeof(BYTE));
1066       *pOut++ = FMT_STR_COPY_SPACE;
1067       *pOut = 0x0;
1068       while (*pFormat == '@')
1069       {
1070         *pOut = *pOut + 1;
1071         str_header->copy_chars++;
1072         pFormat++;
1073       }
1074       TRACE("%d @'s\n", *pOut);
1075       pOut++;
1076       fmt_state &= ~FMT_STATE_OPEN_COPY;
1077     }
1078     else if (*pFormat == '&' && COULD_BE(FMT_TYPE_STRING))
1079     {
1080       /* String formats: Character from string or skip if no char
1081        * Other formats: Literal
1082        * Types the format if found
1083        */
1084       header->type = FMT_TYPE_STRING;
1085       NEED_SPACE(2 * sizeof(BYTE));
1086       *pOut++ = FMT_STR_COPY_SKIP;
1087       *pOut = 0x0;
1088       while (*pFormat == '&')
1089       {
1090         *pOut = *pOut + 1;
1091         str_header->copy_chars++;
1092         pFormat++;
1093       }
1094       TRACE("%d &'s\n", *pOut);
1095       pOut++;
1096       fmt_state &= ~FMT_STATE_OPEN_COPY;
1097     }
1098     else if ((*pFormat == '<' || *pFormat == '>') && COULD_BE(FMT_TYPE_STRING))
1099     {
1100       /* String formats: Use upper/lower case
1101        * Other formats: Literal
1102        * Types the format if found
1103        */
1104       header->type = FMT_TYPE_STRING;
1105       if (*pFormat == '<')
1106         str_header->flags |= FMT_FLAG_LT;
1107       else
1108         str_header->flags |= FMT_FLAG_GT;
1109       TRACE("to %s case\n", *pFormat == '<' ? "lower" : "upper");
1110       pFormat++;
1111       fmt_state &= ~FMT_STATE_OPEN_COPY;
1112     }
1113     else if (*pFormat == '!' && COULD_BE(FMT_TYPE_STRING))
1114     {
1115       /* String formats: Copy right to left
1116        * Other formats: Literal
1117        * Types the format if found
1118        */
1119       header->type = FMT_TYPE_STRING;
1120       str_header->flags |= FMT_FLAG_RTL;
1121       pFormat++;
1122       fmt_state &= ~FMT_STATE_OPEN_COPY;
1123       TRACE("copy right-to-left\n");
1124     }
1125     /* --------
1126      * Literals
1127      * --------
1128      */
1129     /* FIXME: [ seems to be ignored */
1130     else
1131     {
1132       if (*pFormat == '%' && header->type == FMT_TYPE_NUMBER)
1133       {
1134         /* Number formats: Percentage indicator, also a literal
1135          * Other formats: Literal
1136          * Doesn't type the format
1137          */
1138         num_header->flags |= FMT_FLAG_PERCENT;
1139       }
1140 
1141       if (fmt_state & FMT_STATE_OPEN_COPY)
1142       {
1143         pOut[-1] = pOut[-1] + 1; /* Increase the length of the open copy */
1144         TRACE("extend copy (char '%c'), length now %d\n", *pFormat, pOut[-1]);
1145       }
1146       else
1147       {
1148         /* Create a new open copy */
1149         TRACE("New copy (char '%c')\n", *pFormat);
1150         NEED_SPACE(3 * sizeof(BYTE));
1151         *pOut++ = FMT_GEN_COPY;
1152         *pOut++ = pFormat - lpszFormat;
1153         *pOut++ = 0x1;
1154         fmt_state |= FMT_STATE_OPEN_COPY;
1155       }
1156       pFormat++;
1157     }
1158   }
1159 
1160   *pOut++ = FMT_GEN_END;
1161 
1162   header->size = pOut - rgbTok;
1163   if (pcbActual)
1164     *pcbActual = header->size;
1165 
1166   return S_OK;
1167 }
1168 
1169 /* Number formatting state flags */
1170 #define NUM_WROTE_DEC  0x01 /* Written the decimal separator */
1171 #define NUM_WRITE_ON   0x02 /* Started to write the number */
1172 #define NUM_WROTE_SIGN 0x04 /* Written the negative sign */
1173 
1174 /* Format a variant using a number format */
1175 static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1176                                     LPBYTE rgbTok, ULONG dwFlags,
1177                                     BSTR *pbstrOut, LCID lcid)
1178 {
1179   BYTE rgbDig[256], *prgbDig;
1180   NUMPARSE np;
1181   int have_int, need_int = 0, have_frac, need_frac, exponent = 0, pad = 0;
1182   WCHAR buff[256], *pBuff = buff;
1183   WCHAR thousandSeparator[32];
1184   VARIANT vString, vBool;
1185   DWORD dwState = 0;
1186   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1187   FMT_NUMBER_HEADER *numHeader;
1188   const BYTE* pToken = NULL;
1189   HRESULT hRes = S_OK;
1190 
1191   TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
1192         rgbTok, dwFlags, pbstrOut, lcid);
1193 
1194   V_VT(&vString) = VT_EMPTY;
1195   V_VT(&vBool) = VT_BOOL;
1196 
1197   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1198   {
1199     have_int = have_frac = 0;
1200     numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNull(header));
1201     V_BOOL(&vBool) = VARIANT_FALSE;
1202   }
1203   else
1204   {
1205     /* Get a number string from pVarIn, and parse it */
1206     hRes = VariantChangeTypeEx(&vString, pVarIn, lcid, VARIANT_NOUSEROVERRIDE, VT_BSTR);
1207     if (FAILED(hRes))
1208       return hRes;
1209 
1210     np.cDig = sizeof(rgbDig);
1211     np.dwInFlags = NUMPRS_STD;
1212     hRes = VarParseNumFromStr(V_BSTR(&vString), lcid, 0, &np, rgbDig);
1213     if (FAILED(hRes))
1214       return hRes;
1215 
1216     have_int = np.cDig;
1217     have_frac = 0;
1218     exponent = np.nPwr10;
1219 
1220     /* Figure out which format to use */
1221     if (np.dwOutFlags & NUMPRS_NEG)
1222     {
1223       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNegative(header));
1224       V_BOOL(&vBool) = VARIANT_TRUE;
1225     }
1226     else if (have_int == 1 && !exponent && rgbDig[0] == 0)
1227     {
1228       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetZero(header));
1229       V_BOOL(&vBool) = VARIANT_FALSE;
1230     }
1231     else
1232     {
1233       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetPositive(header));
1234       V_BOOL(&vBool) = VARIANT_TRUE;
1235     }
1236 
1237     TRACE("num header: flags = 0x%x, mult=%d, div=%d, whole=%d, fract=%d\n",
1238           numHeader->flags, numHeader->multiplier, numHeader->divisor,
1239           numHeader->whole, numHeader->fractional);
1240 
1241     need_int = numHeader->whole;
1242     need_frac = numHeader->fractional;
1243 
1244     if (numHeader->flags & FMT_FLAG_PERCENT &&
1245         !(have_int == 1 && !exponent && rgbDig[0] == 0))
1246       exponent += 2;
1247 
1248     if (numHeader->flags & FMT_FLAG_EXPONENT)
1249     {
1250       /* Exponent format: length of the integral number part is fixed and
1251          specified by the format. */
1252       pad = need_int - have_int;
1253       exponent -= pad;
1254       if (pad < 0)
1255       {
1256         have_int = need_int;
1257         have_frac -= pad;
1258         pad = 0;
1259       }
1260     }
1261     else
1262     {
1263       /* Convert the exponent */
1264       pad = max(exponent, -have_int);
1265       exponent -= pad;
1266       if (pad < 0)
1267       {
1268         have_int += pad;
1269         have_frac = -pad;
1270         pad = 0;
1271       }
1272       if(exponent < 0 && exponent > (-256 + have_int + have_frac))
1273       {
1274         /* Remove exponent notation */
1275         memmove(rgbDig - exponent, rgbDig, have_int + have_frac);
1276         ZeroMemory(rgbDig, -exponent);
1277         have_frac -= exponent;
1278         exponent = 0;
1279       }
1280     }
1281 
1282     /* Rounding the number */
1283     if (have_frac > need_frac)
1284     {
1285       prgbDig = &rgbDig[have_int + need_frac];
1286       have_frac = need_frac;
1287       if (*prgbDig >= 5)
1288       {
1289         while (prgbDig-- > rgbDig && *prgbDig == 9)
1290           *prgbDig = 0;
1291         if (prgbDig < rgbDig)
1292         {
1293           /* We reached the first digit and that was also a 9 */
1294           rgbDig[0] = 1;
1295           if (numHeader->flags & FMT_FLAG_EXPONENT)
1296             exponent++;
1297           else
1298           {
1299             rgbDig[have_int + need_frac] = 0;
1300             if (exponent < 0)
1301               exponent++;
1302             else
1303               have_int++;
1304           }
1305         }
1306         else
1307           (*prgbDig)++;
1308       }
1309       /* We converted trailing digits to zeroes => have_frac has changed */
1310       while (have_frac > 0 && rgbDig[have_int + have_frac - 1] == 0)
1311         have_frac--;
1312     }
1313     TRACE("have_int=%d,need_int=%d,have_frac=%d,need_frac=%d,pad=%d,exp=%d\n",
1314           have_int, need_int, have_frac, need_frac, pad, exponent);
1315   }
1316 
1317   if (numHeader->flags & FMT_FLAG_THOUSANDS)
1318   {
1319     if (!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousandSeparator,
1320                         sizeof(thousandSeparator)/sizeof(WCHAR)))
1321     {
1322       thousandSeparator[0] = ',';
1323       thousandSeparator[1] = 0;
1324     }
1325   }
1326 
1327   pToken = (const BYTE*)numHeader + sizeof(FMT_NUMBER_HEADER);
1328   prgbDig = rgbDig;
1329 
1330   while (SUCCEEDED(hRes) && *pToken != FMT_GEN_END)
1331   {
1332     WCHAR defaultChar = '?';
1333     DWORD boolFlag, localeValue = 0;
1334     BOOL shouldAdvance = TRUE;
1335 
1336     if (pToken - rgbTok > header->size)
1337     {
1338       ERR("Ran off the end of the format!\n");
1339       hRes = E_INVALIDARG;
1340       goto VARIANT_FormatNumber_Exit;
1341     }
1342 
1343     switch (*pToken)
1344     {
1345     case FMT_GEN_COPY:
1346       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1347       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1348       pBuff += pToken[2];
1349       pToken += 2;
1350       break;
1351 
1352     case FMT_GEN_INLINE:
1353       pToken += 2;
1354       TRACE("copy %s\n", debugstr_a((LPCSTR)pToken));
1355       while (*pToken)
1356         *pBuff++ = *pToken++;
1357       break;
1358 
1359     case FMT_NUM_YES_NO:
1360       boolFlag = VAR_BOOLYESNO;
1361       goto VARIANT_FormatNumber_Bool;
1362 
1363     case FMT_NUM_ON_OFF:
1364       boolFlag = VAR_BOOLONOFF;
1365       goto VARIANT_FormatNumber_Bool;
1366 
1367     case FMT_NUM_TRUE_FALSE:
1368       boolFlag = VAR_LOCALBOOL;
1369 
1370 VARIANT_FormatNumber_Bool:
1371       {
1372         BSTR boolStr = NULL;
1373 
1374         if (pToken[1] != FMT_GEN_END)
1375         {
1376           ERR("Boolean token not at end of format!\n");
1377           hRes = E_INVALIDARG;
1378           goto VARIANT_FormatNumber_Exit;
1379         }
1380         hRes = VarBstrFromBool(V_BOOL(&vBool), lcid, boolFlag, &boolStr);
1381         if (SUCCEEDED(hRes))
1382         {
1383           strcpyW(pBuff, boolStr);
1384           SysFreeString(boolStr);
1385           while (*pBuff)
1386             pBuff++;
1387         }
1388       }
1389       break;
1390 
1391     case FMT_NUM_DECIMAL:
1392       if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
1393       {
1394         /* last chance for a negative sign in the .# case */
1395         TRACE("write negative sign\n");
1396         localeValue = LOCALE_SNEGATIVESIGN;
1397         defaultChar = '-';
1398         dwState |= NUM_WROTE_SIGN;
1399         shouldAdvance = FALSE;
1400         break;
1401       }
1402       TRACE("write decimal separator\n");
1403       localeValue = LOCALE_SDECIMAL;
1404       defaultChar = '.';
1405       dwState |= NUM_WROTE_DEC;
1406       break;
1407 
1408     case FMT_NUM_CURRENCY:
1409       TRACE("write currency symbol\n");
1410       localeValue = LOCALE_SCURRENCY;
1411       defaultChar = '$';
1412       break;
1413 
1414     case FMT_NUM_EXP_POS_U:
1415     case FMT_NUM_EXP_POS_L:
1416     case FMT_NUM_EXP_NEG_U:
1417     case FMT_NUM_EXP_NEG_L:
1418       if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_NEG_L)
1419         *pBuff++ = 'e';
1420       else
1421         *pBuff++ = 'E';
1422       if (exponent < 0)
1423       {
1424         *pBuff++ = '-';
1425         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], -exponent);
1426       }
1427       else
1428       {
1429         if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_POS_U)
1430           *pBuff++ = '+';
1431         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], exponent);
1432       }
1433       while (*pBuff)
1434         pBuff++;
1435       pToken++;
1436       break;
1437 
1438     case FMT_NUM_COPY_ZERO:
1439       dwState |= NUM_WRITE_ON;
1440       /* Fall through */
1441 
1442     case FMT_NUM_COPY_SKIP:
1443       TRACE("write %d %sdigits or %s\n", pToken[1],
1444             dwState & NUM_WROTE_DEC ? "fractional " : "",
1445             *pToken == FMT_NUM_COPY_ZERO ? "0" : "skip");
1446 
1447       if (dwState & NUM_WROTE_DEC)
1448       {
1449         int count, i;
1450 
1451         if (!(numHeader->flags & FMT_FLAG_EXPONENT) && exponent < 0)
1452         {
1453           /* Pad with 0 before writing the fractional digits */
1454           pad = max(exponent, -pToken[1]);
1455           exponent -= pad;
1456           count = min(have_frac, pToken[1] + pad);
1457           for (i = 0; i > pad; i--)
1458             *pBuff++ = '0';
1459         }
1460         else
1461           count = min(have_frac, pToken[1]);
1462 
1463         pad += pToken[1] - count;
1464         have_frac -= count;
1465         while (count--)
1466           *pBuff++ = '0' + *prgbDig++;
1467         if (*pToken == FMT_NUM_COPY_ZERO)
1468         {
1469           for (; pad > 0; pad--)
1470             *pBuff++ = '0'; /* Write zeros for missing trailing digits */
1471         }
1472       }
1473       else
1474       {
1475         int count, count_max, position;
1476 
1477         if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
1478         {
1479           TRACE("write negative sign\n");
1480           localeValue = LOCALE_SNEGATIVESIGN;
1481           defaultChar = '-';
1482           dwState |= NUM_WROTE_SIGN;
1483           shouldAdvance = FALSE;
1484           break;
1485         }
1486 
1487         position = have_int + pad;
1488         if (dwState & NUM_WRITE_ON)
1489           position = max(position, need_int);
1490         need_int -= pToken[1];
1491         count_max = have_int + pad - need_int;
1492         if (count_max < 0)
1493             count_max = 0;
1494         if (dwState & NUM_WRITE_ON)
1495         {
1496           count = pToken[1] - count_max;
1497           TRACE("write %d leading zeros\n", count);
1498           while (count-- > 0)
1499           {
1500             *pBuff++ = '0';
1501             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1502                 position > 1 && (--position % 3) == 0)
1503             {
1504               int k;
1505               TRACE("write thousand separator\n");
1506               for (k = 0; thousandSeparator[k]; k++)
1507                 *pBuff++ = thousandSeparator[k];
1508             }
1509           }
1510         }
1511         if (*pToken == FMT_NUM_COPY_ZERO || have_int > 1 ||
1512             (have_int > 0 && *prgbDig > 0))
1513         {
1514           count = min(count_max, have_int);
1515           count_max -= count;
1516           have_int -= count;
1517           TRACE("write %d whole number digits\n", count);
1518           while (count--)
1519           {
1520             dwState |= NUM_WRITE_ON;
1521             *pBuff++ = '0' + *prgbDig++;
1522             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1523                 position > 1 && (--position % 3) == 0)
1524             {
1525               int k;
1526               TRACE("write thousand separator\n");
1527               for (k = 0; thousandSeparator[k]; k++)
1528                 *pBuff++ = thousandSeparator[k];
1529             }
1530           }
1531         }
1532         count = min(count_max, pad);
1533         pad -= count;
1534         TRACE("write %d whole trailing 0's\n", count);
1535         while (count--)
1536         {
1537           *pBuff++ = '0';
1538           if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
1539               position > 1 && (--position % 3) == 0)
1540           {
1541             int k;
1542             TRACE("write thousand separator\n");
1543             for (k = 0; thousandSeparator[k]; k++)
1544               *pBuff++ = thousandSeparator[k];
1545           }
1546         }
1547       }
1548       pToken++;
1549       break;
1550 
1551     default:
1552       ERR("Unknown token 0x%02x!\n", *pToken);
1553       hRes = E_INVALIDARG;
1554       goto VARIANT_FormatNumber_Exit;
1555     }
1556     if (localeValue)
1557     {
1558       if (GetLocaleInfoW(lcid, localeValue, pBuff,
1559                          sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
1560       {
1561         TRACE("added %s\n", debugstr_w(pBuff));
1562         while (*pBuff)
1563           pBuff++;
1564       }
1565       else
1566       {
1567         TRACE("added %d '%c'\n", defaultChar, defaultChar);
1568         *pBuff++ = defaultChar;
1569       }
1570     }
1571     if (shouldAdvance)
1572       pToken++;
1573   }
1574 
1575 VARIANT_FormatNumber_Exit:
1576   VariantClear(&vString);
1577   *pBuff = '\0';
1578   TRACE("buff is %s\n", debugstr_w(buff));
1579   if (SUCCEEDED(hRes))
1580   {
1581     *pbstrOut = SysAllocString(buff);
1582     if (!*pbstrOut)
1583       hRes = E_OUTOFMEMORY;
1584   }
1585   return hRes;
1586 }
1587 
1588 /* Format a variant using a date format */
1589 static HRESULT VARIANT_FormatDate(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1590                                   LPBYTE rgbTok, ULONG dwFlags,
1591                                   BSTR *pbstrOut, LCID lcid)
1592 {
1593   WCHAR buff[256], *pBuff = buff;
1594   VARIANT vDate;
1595   UDATE udate;
1596   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1597   FMT_DATE_HEADER *dateHeader;
1598   const BYTE* pToken = NULL;
1599   HRESULT hRes;
1600 
1601   TRACE("(%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn),
1602         debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut, lcid);
1603 
1604   V_VT(&vDate) = VT_EMPTY;
1605 
1606   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1607   {
1608     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetNegative(header));
1609     V_DATE(&vDate) = 0;
1610   }
1611   else
1612   {
1613     USHORT usFlags = dwFlags & VARIANT_CALENDAR_HIJRI ? VAR_CALENDAR_HIJRI : 0;
1614 
1615     hRes = VariantChangeTypeEx(&vDate, pVarIn, lcid, usFlags, VT_DATE);
1616     if (FAILED(hRes))
1617       return hRes;
1618     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetPositive(header));
1619   }
1620 
1621   hRes = VarUdateFromDate(V_DATE(&vDate), 0 /* FIXME: flags? */, &udate);
1622   if (FAILED(hRes))
1623     return hRes;
1624   pToken = (const BYTE*)dateHeader + sizeof(FMT_DATE_HEADER);
1625 
1626   while (*pToken != FMT_GEN_END)
1627   {
1628     DWORD dwVal = 0, localeValue = 0, dwFmt = 0;
1629     LPCWSTR szPrintFmt = NULL;
1630     WCHAR defaultChar = '?';
1631 
1632     if (pToken - rgbTok > header->size)
1633     {
1634       ERR("Ran off the end of the format!\n");
1635       hRes = E_INVALIDARG;
1636       goto VARIANT_FormatDate_Exit;
1637     }
1638 
1639     switch (*pToken)
1640     {
1641     case FMT_GEN_COPY:
1642       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1643       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1644       pBuff += pToken[2];
1645       pToken += 2;
1646       break;
1647 
1648     case FMT_GEN_INLINE:
1649       pToken += 2;
1650       TRACE("copy %s\n", debugstr_a((LPCSTR)pToken));
1651       while (*pToken)
1652         *pBuff++ = *pToken++;
1653       break;
1654 
1655     case FMT_DATE_TIME_SEP:
1656       TRACE("time separator\n");
1657       localeValue = LOCALE_STIME;
1658       defaultChar = ':';
1659       break;
1660 
1661     case FMT_DATE_DATE_SEP:
1662       TRACE("date separator\n");
1663       localeValue = LOCALE_SDATE;
1664       defaultChar = '/';
1665       break;
1666 
1667     case FMT_DATE_GENERAL:
1668       {
1669         BSTR date = NULL;
1670         WCHAR *pDate;
1671         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, 0, &date);
1672         if (FAILED(hRes))
1673           goto VARIANT_FormatDate_Exit;
1674 	pDate = date;
1675         while (*pDate)
1676           *pBuff++ = *pDate++;
1677         SysFreeString(date);
1678       }
1679       break;
1680 
1681     case FMT_DATE_QUARTER:
1682       if (udate.st.wMonth <= 3)
1683         *pBuff++ = '1';
1684       else if (udate.st.wMonth <= 6)
1685         *pBuff++ = '2';
1686       else if (udate.st.wMonth <= 9)
1687         *pBuff++ = '3';
1688       else
1689         *pBuff++ = '4';
1690       break;
1691 
1692     case FMT_DATE_TIME_SYS:
1693       {
1694         /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1695         BSTR date = NULL;
1696         WCHAR *pDate;
1697         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, VAR_TIMEVALUEONLY, &date);
1698         if (FAILED(hRes))
1699           goto VARIANT_FormatDate_Exit;
1700 	pDate = date;
1701         while (*pDate)
1702           *pBuff++ = *pDate++;
1703         SysFreeString(date);
1704       }
1705       break;
1706 
1707     case FMT_DATE_DAY:
1708       szPrintFmt = szPercent_d;
1709       dwVal = udate.st.wDay;
1710       break;
1711 
1712     case FMT_DATE_DAY_0:
1713       szPrintFmt = szPercentZeroTwo_d;
1714       dwVal = udate.st.wDay;
1715       break;
1716 
1717     case FMT_DATE_DAY_SHORT:
1718       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1719       TRACE("short day\n");
1720       localeValue = LOCALE_SABBREVDAYNAME1 + (udate.st.wDayOfWeek + 6)%7;
1721       defaultChar = '?';
1722       break;
1723 
1724     case FMT_DATE_DAY_LONG:
1725       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1726       TRACE("long day\n");
1727       localeValue = LOCALE_SDAYNAME1 + (udate.st.wDayOfWeek + 6)%7;
1728       defaultChar = '?';
1729       break;
1730 
1731     case FMT_DATE_SHORT:
1732       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1733       dwFmt = LOCALE_SSHORTDATE;
1734       break;
1735 
1736     case FMT_DATE_LONG:
1737       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1738       dwFmt = LOCALE_SLONGDATE;
1739       break;
1740 
1741     case FMT_DATE_MEDIUM:
1742       FIXME("Medium date treated as long date\n");
1743       dwFmt = LOCALE_SLONGDATE;
1744       break;
1745 
1746     case FMT_DATE_DAY_WEEK:
1747       szPrintFmt = szPercent_d;
1748       if (pToken[1])
1749         dwVal = udate.st.wDayOfWeek + 2 - pToken[1];
1750       else
1751       {
1752         GetLocaleInfoW(lcid,LOCALE_RETURN_NUMBER|LOCALE_IFIRSTDAYOFWEEK,
1753                        (LPWSTR)&dwVal, sizeof(dwVal)/sizeof(WCHAR));
1754         dwVal = udate.st.wDayOfWeek + 1 - dwVal;
1755       }
1756       pToken++;
1757       break;
1758 
1759     case FMT_DATE_WEEK_YEAR:
1760       szPrintFmt = szPercent_d;
1761       dwVal = udate.wDayOfYear / 7 + 1;
1762       pToken += 2;
1763       FIXME("Ignoring nFirstDay of %d, nFirstWeek of %d\n", pToken[0], pToken[1]);
1764       break;
1765 
1766     case FMT_DATE_MON:
1767       szPrintFmt = szPercent_d;
1768       dwVal = udate.st.wMonth;
1769       break;
1770 
1771     case FMT_DATE_MON_0:
1772       szPrintFmt = szPercentZeroTwo_d;
1773       dwVal = udate.st.wMonth;
1774       break;
1775 
1776     case FMT_DATE_MON_SHORT:
1777       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1778       TRACE("short month\n");
1779       localeValue = LOCALE_SABBREVMONTHNAME1 + udate.st.wMonth - 1;
1780       defaultChar = '?';
1781       break;
1782 
1783     case FMT_DATE_MON_LONG:
1784       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
1785       TRACE("long month\n");
1786       localeValue = LOCALE_SMONTHNAME1 + udate.st.wMonth - 1;
1787       defaultChar = '?';
1788       break;
1789 
1790     case FMT_DATE_YEAR_DOY:
1791       szPrintFmt = szPercent_d;
1792       dwVal = udate.wDayOfYear;
1793       break;
1794 
1795     case FMT_DATE_YEAR_0:
1796       szPrintFmt = szPercentZeroTwo_d;
1797       dwVal = udate.st.wYear % 100;
1798       break;
1799 
1800     case FMT_DATE_YEAR_LONG:
1801       szPrintFmt = szPercent_d;
1802       dwVal = udate.st.wYear;
1803       break;
1804 
1805     case FMT_DATE_MIN:
1806       szPrintFmt = szPercent_d;
1807       dwVal = udate.st.wMinute;
1808       break;
1809 
1810     case FMT_DATE_MIN_0:
1811       szPrintFmt = szPercentZeroTwo_d;
1812       dwVal = udate.st.wMinute;
1813       break;
1814 
1815     case FMT_DATE_SEC:
1816       szPrintFmt = szPercent_d;
1817       dwVal = udate.st.wSecond;
1818       break;
1819 
1820     case FMT_DATE_SEC_0:
1821       szPrintFmt = szPercentZeroTwo_d;
1822       dwVal = udate.st.wSecond;
1823       break;
1824 
1825     case FMT_DATE_HOUR:
1826       szPrintFmt = szPercent_d;
1827       dwVal = udate.st.wHour;
1828       break;
1829 
1830     case FMT_DATE_HOUR_0:
1831     case FMT_DATE_TIME_UNK2:
1832       szPrintFmt = szPercentZeroTwo_d;
1833       dwVal = udate.st.wHour;
1834       break;
1835 
1836     case FMT_DATE_HOUR_12:
1837       szPrintFmt = szPercent_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_HOUR_12_0:
1842       szPrintFmt = szPercentZeroTwo_d;
1843       dwVal = udate.st.wHour ? udate.st.wHour > 12 ? udate.st.wHour - 12 : udate.st.wHour : 12;
1844       break;
1845 
1846     case FMT_DATE_AMPM_SYS1:
1847     case FMT_DATE_AMPM_SYS2:
1848       localeValue = udate.st.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
1849       defaultChar = '?';
1850       break;
1851 
1852     case FMT_DATE_AMPM_UPPER:
1853       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
1854       *pBuff++ = 'M';
1855       break;
1856 
1857     case FMT_DATE_A_UPPER:
1858       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
1859       break;
1860 
1861     case FMT_DATE_AMPM_LOWER:
1862       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
1863       *pBuff++ = 'm';
1864       break;
1865 
1866     case FMT_DATE_A_LOWER:
1867       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
1868       break;
1869 
1870     default:
1871       ERR("Unknown token 0x%02x!\n", *pToken);
1872       hRes = E_INVALIDARG;
1873       goto VARIANT_FormatDate_Exit;
1874     }
1875     if (localeValue)
1876     {
1877       *pBuff = '\0';
1878       if (GetLocaleInfoW(lcid, localeValue, pBuff,
1879           sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
1880       {
1881         TRACE("added %s\n", debugstr_w(pBuff));
1882         while (*pBuff)
1883           pBuff++;
1884       }
1885       else
1886       {
1887         TRACE("added %d %c\n", defaultChar, defaultChar);
1888         *pBuff++ = defaultChar;
1889       }
1890     }
1891     else if (dwFmt)
1892     {
1893       WCHAR fmt_buff[80];
1894 
1895       if (!GetLocaleInfoW(lcid, dwFmt, fmt_buff, sizeof(fmt_buff)/sizeof(WCHAR)) ||
1896           !get_date_format(lcid, 0, &udate.st, fmt_buff, pBuff,
1897                           sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
1898       {
1899         hRes = E_INVALIDARG;
1900         goto VARIANT_FormatDate_Exit;
1901       }
1902       while (*pBuff)
1903         pBuff++;
1904     }
1905     else if (szPrintFmt)
1906     {
1907       sprintfW(pBuff, szPrintFmt, dwVal);
1908       while (*pBuff)
1909         pBuff++;
1910     }
1911     pToken++;
1912   }
1913 
1914 VARIANT_FormatDate_Exit:
1915   *pBuff = '\0';
1916   TRACE("buff is %s\n", debugstr_w(buff));
1917   if (SUCCEEDED(hRes))
1918   {
1919     *pbstrOut = SysAllocString(buff);
1920     if (!*pbstrOut)
1921       hRes = E_OUTOFMEMORY;
1922   }
1923   return hRes;
1924 }
1925 
1926 /* Format a variant using a string format */
1927 static HRESULT VARIANT_FormatString(LPVARIANT pVarIn, LPOLESTR lpszFormat,
1928                                     LPBYTE rgbTok, ULONG dwFlags,
1929                                     BSTR *pbstrOut, LCID lcid)
1930 {
1931   static WCHAR szEmpty[] = { '\0' };
1932   WCHAR buff[256], *pBuff = buff;
1933   WCHAR *pSrc;
1934   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
1935   FMT_STRING_HEADER *strHeader;
1936   const BYTE* pToken = NULL;
1937   VARIANT vStr;
1938   int blanks_first;
1939   BOOL bUpper = FALSE;
1940   HRESULT hRes = S_OK;
1941 
1942   TRACE("%s,%s,%p,0x%08x,%p,0x%08x)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
1943         rgbTok, dwFlags, pbstrOut, lcid);
1944 
1945   V_VT(&vStr) = VT_EMPTY;
1946 
1947   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
1948   {
1949     strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
1950     V_BSTR(&vStr) = szEmpty;
1951   }
1952   else
1953   {
1954     hRes = VariantChangeTypeEx(&vStr, pVarIn, lcid, VARIANT_NOUSEROVERRIDE, VT_BSTR);
1955     if (FAILED(hRes))
1956       return hRes;
1957 
1958     if (V_BSTR(&vStr)[0] == '\0')
1959       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
1960     else
1961       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetPositive(header));
1962   }
1963   pSrc = V_BSTR(&vStr);
1964   if ((strHeader->flags & (FMT_FLAG_LT|FMT_FLAG_GT)) == FMT_FLAG_GT)
1965     bUpper = TRUE;
1966   blanks_first = strHeader->copy_chars - strlenW(pSrc);
1967   pToken = (const BYTE*)strHeader + sizeof(FMT_DATE_HEADER);
1968 
1969   while (*pToken != FMT_GEN_END)
1970   {
1971     int dwCount = 0;
1972 
1973     if (pToken - rgbTok > header->size)
1974     {
1975       ERR("Ran off the end of the format!\n");
1976       hRes = E_INVALIDARG;
1977       goto VARIANT_FormatString_Exit;
1978     }
1979 
1980     switch (*pToken)
1981     {
1982     case FMT_GEN_COPY:
1983       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
1984       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
1985       pBuff += pToken[2];
1986       pToken += 2;
1987       break;
1988 
1989     case FMT_STR_COPY_SPACE:
1990     case FMT_STR_COPY_SKIP:
1991       dwCount = pToken[1];
1992       if (*pToken == FMT_STR_COPY_SPACE && blanks_first > 0)
1993       {
1994         TRACE("insert %d initial spaces\n", blanks_first);
1995         while (dwCount > 0 && blanks_first > 0)
1996         {
1997           *pBuff++ = ' ';
1998           dwCount--;
1999           blanks_first--;
2000         }
2001       }
2002       TRACE("copy %d chars%s\n", dwCount,
2003             *pToken == FMT_STR_COPY_SPACE ? " with space" :"");
2004       while (dwCount > 0 && *pSrc)
2005       {
2006         if (bUpper)
2007           *pBuff++ = toupperW(*pSrc);
2008         else
2009           *pBuff++ = tolowerW(*pSrc);
2010         dwCount--;
2011         pSrc++;
2012       }
2013       if (*pToken == FMT_STR_COPY_SPACE && dwCount > 0)
2014       {
2015         TRACE("insert %d spaces\n", dwCount);
2016         while (dwCount-- > 0)
2017           *pBuff++ = ' ';
2018       }
2019       pToken++;
2020       break;
2021 
2022     default:
2023       ERR("Unknown token 0x%02x!\n", *pToken);
2024       hRes = E_INVALIDARG;
2025       goto VARIANT_FormatString_Exit;
2026     }
2027     pToken++;
2028   }
2029 
2030 VARIANT_FormatString_Exit:
2031   /* Copy out any remaining chars */
2032   while (*pSrc)
2033   {
2034     if (bUpper)
2035       *pBuff++ = toupperW(*pSrc);
2036     else
2037       *pBuff++ = tolowerW(*pSrc);
2038     pSrc++;
2039   }
2040   VariantClear(&vStr);
2041   *pBuff = '\0';
2042   TRACE("buff is %s\n", debugstr_w(buff));
2043   if (SUCCEEDED(hRes))
2044   {
2045     *pbstrOut = SysAllocString(buff);
2046     if (!*pbstrOut)
2047       hRes = E_OUTOFMEMORY;
2048   }
2049   return hRes;
2050 }
2051 
2052 #define NUMBER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2| \
2053                        VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8| \
2054                        VTBIT_R4|VTBIT_R8|VTBIT_CY|VTBIT_DECIMAL| \
2055                        VTBIT_BOOL|VTBIT_INT|VTBIT_UINT)
2056 
2057 /**********************************************************************
2058  *              VarFormatFromTokens [OLEAUT32.139]
2059  */
2060 HRESULT WINAPI VarFormatFromTokens(LPVARIANT pVarIn, LPOLESTR lpszFormat,
2061                                    LPBYTE rgbTok, ULONG dwFlags,
2062                                    BSTR *pbstrOut, LCID lcid)
2063 {
2064   FMT_SHORT_HEADER *header = (FMT_SHORT_HEADER *)rgbTok;
2065   VARIANT vTmp;
2066   HRESULT hres;
2067 
2068   TRACE("(%p,%s,%p,%x,%p,0x%08x)\n", pVarIn, debugstr_w(lpszFormat),
2069           rgbTok, dwFlags, pbstrOut, lcid);
2070 
2071   if (!pbstrOut)
2072     return E_INVALIDARG;
2073 
2074   *pbstrOut = NULL;
2075 
2076   if (!pVarIn || !rgbTok)
2077     return E_INVALIDARG;
2078 
2079   if (V_VT(pVarIn) == VT_NULL)
2080     return S_OK;
2081 
2082   if (*rgbTok == FMT_TO_STRING || header->type == FMT_TYPE_GENERAL)
2083   {
2084     /* According to MSDN, general format acts somewhat like the 'Str'
2085      * function in Visual Basic.
2086      */
2087 VarFormatFromTokens_AsStr:
2088     V_VT(&vTmp) = VT_EMPTY;
2089     hres = VariantChangeTypeEx(&vTmp, pVarIn, lcid, dwFlags, VT_BSTR);
2090     *pbstrOut = V_BSTR(&vTmp);
2091   }
2092   else
2093   {
2094     if (header->type == FMT_TYPE_NUMBER ||
2095         (header->type == FMT_TYPE_UNKNOWN && ((1 << V_TYPE(pVarIn)) & NUMBER_VTBITS)))
2096     {
2097       hres = VARIANT_FormatNumber(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2098     }
2099     else if (header->type == FMT_TYPE_DATE ||
2100              (header->type == FMT_TYPE_UNKNOWN && V_TYPE(pVarIn) == VT_DATE))
2101     {
2102       hres = VARIANT_FormatDate(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2103     }
2104     else if (header->type == FMT_TYPE_STRING || V_TYPE(pVarIn) == VT_BSTR)
2105     {
2106       hres = VARIANT_FormatString(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
2107     }
2108     else
2109     {
2110       ERR("unrecognised format type 0x%02x\n", header->type);
2111       return E_INVALIDARG;
2112     }
2113     /* If the coercion failed, still try to create output, unless the
2114      * VAR_FORMAT_NOSUBSTITUTE flag is set.
2115      */
2116     if ((hres == DISP_E_OVERFLOW || hres == DISP_E_TYPEMISMATCH) &&
2117         !(dwFlags & VAR_FORMAT_NOSUBSTITUTE))
2118       goto VarFormatFromTokens_AsStr;
2119   }
2120 
2121   return hres;
2122 }
2123 
2124 /**********************************************************************
2125  *              VarFormat [OLEAUT32.87]
2126  *
2127  * Format a variant from a format string.
2128  *
2129  * PARAMS
2130  *  pVarIn     [I] Variant to format
2131  *  lpszFormat [I] Format string (see notes)
2132  *  nFirstDay  [I] First day of the week, (See VarTokenizeFormatString() for details)
2133  *  nFirstWeek [I] First week of the year (See VarTokenizeFormatString() for details)
2134  *  dwFlags    [I] Flags for the format (VAR_ flags from "oleauto.h")
2135  *  pbstrOut   [O] Destination for formatted string.
2136  *
2137  * RETURNS
2138  *  Success: S_OK. pbstrOut contains the formatted value.
2139  *  Failure: E_INVALIDARG, if any parameter is invalid.
2140  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2141  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2142  *
2143  * NOTES
2144  *  - See Variant-Formats for details concerning creating format strings.
2145  *  - This function uses LOCALE_USER_DEFAULT when calling VarTokenizeFormatString()
2146  *    and VarFormatFromTokens().
2147  */
2148 HRESULT WINAPI VarFormat(LPVARIANT pVarIn, LPOLESTR lpszFormat,
2149                          int nFirstDay, int nFirstWeek, ULONG dwFlags,
2150                          BSTR *pbstrOut)
2151 {
2152   BYTE buff[256];
2153   HRESULT hres;
2154 
2155   TRACE("(%s,%s,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), debugstr_w(lpszFormat),
2156         nFirstDay, nFirstWeek, dwFlags, pbstrOut);
2157 
2158   if (!pbstrOut)
2159     return E_INVALIDARG;
2160   *pbstrOut = NULL;
2161 
2162   hres = VarTokenizeFormatString(lpszFormat, buff, sizeof(buff), nFirstDay,
2163                                  nFirstWeek, LOCALE_USER_DEFAULT, NULL);
2164   if (SUCCEEDED(hres))
2165     hres = VarFormatFromTokens(pVarIn, lpszFormat, buff, dwFlags,
2166                                pbstrOut, LOCALE_USER_DEFAULT);
2167   TRACE("returning 0x%08x, %s\n", hres, debugstr_w(*pbstrOut));
2168   return hres;
2169 }
2170 
2171 /**********************************************************************
2172  *              VarFormatDateTime [OLEAUT32.97]
2173  *
2174  * Format a variant value as a date and/or time.
2175  *
2176  * PARAMS
2177  *  pVarIn    [I] Variant to format
2178  *  nFormat   [I] Format type (see notes)
2179  *  dwFlags   [I] Flags for the format (VAR_ flags from "oleauto.h")
2180  *  pbstrOut  [O] Destination for formatted string.
2181  *
2182  * RETURNS
2183  *  Success: S_OK. pbstrOut contains the formatted value.
2184  *  Failure: E_INVALIDARG, if any parameter is invalid.
2185  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2186  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2187  *
2188  * NOTES
2189  *  This function uses LOCALE_USER_DEFAULT when determining the date format
2190  *  characters to use.
2191  *  Possible values for the nFormat parameter are:
2192  *| Value  Meaning
2193  *| -----  -------
2194  *|   0    General date format
2195  *|   1    Long date format
2196  *|   2    Short date format
2197  *|   3    Long time format
2198  *|   4    Short time format
2199  */
2200 HRESULT WINAPI VarFormatDateTime(LPVARIANT pVarIn, INT nFormat, ULONG dwFlags, BSTR *pbstrOut)
2201 {
2202   static WCHAR szEmpty[] = { '\0' };
2203   const BYTE* lpFmt = NULL;
2204 
2205   TRACE("%s,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nFormat, dwFlags, pbstrOut);
2206 
2207   if (!pVarIn || !pbstrOut || nFormat < 0 || nFormat > 4)
2208     return E_INVALIDARG;
2209 
2210   switch (nFormat)
2211   {
2212   case 0: lpFmt = fmtGeneralDate; break;
2213   case 1: lpFmt = fmtLongDate; break;
2214   case 2: lpFmt = fmtShortDate; break;
2215   case 3: lpFmt = fmtLongTime; break;
2216   case 4: lpFmt = fmtShortTime; break;
2217   }
2218   return VarFormatFromTokens(pVarIn, szEmpty, (BYTE*)lpFmt, dwFlags,
2219                               pbstrOut, LOCALE_USER_DEFAULT);
2220 }
2221 
2222 #define GETLOCALENUMBER(type,field) GetLocaleInfoW(LOCALE_USER_DEFAULT, \
2223                                                    type|LOCALE_RETURN_NUMBER, \
2224                                                    (LPWSTR)&numfmt.field, \
2225                                                    sizeof(numfmt.field)/sizeof(WCHAR))
2226 
2227 /**********************************************************************
2228  *              VarFormatNumber [OLEAUT32.107]
2229  *
2230  * Format a variant value as a number.
2231  *
2232  * PARAMS
2233  *  pVarIn    [I] Variant to format
2234  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2235  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2236  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2237  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2238  *  dwFlags   [I] Currently unused, set to zero
2239  *  pbstrOut  [O] Destination for formatted string.
2240  *
2241  * RETURNS
2242  *  Success: S_OK. pbstrOut contains the formatted value.
2243  *  Failure: E_INVALIDARG, if any parameter is invalid.
2244  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2245  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2246  *
2247  * NOTES
2248  *  This function uses LOCALE_USER_DEFAULT when determining the number format
2249  *  characters to use.
2250  */
2251 HRESULT WINAPI VarFormatNumber(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
2252                                INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
2253 {
2254   HRESULT hRet;
2255   VARIANT vStr;
2256 
2257   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2258         nParens, nGrouping, dwFlags, pbstrOut);
2259 
2260   if (!pVarIn || !pbstrOut || nDigits > 9)
2261     return E_INVALIDARG;
2262 
2263   *pbstrOut = NULL;
2264 
2265   V_VT(&vStr) = VT_EMPTY;
2266   hRet = VariantCopyInd(&vStr, pVarIn);
2267 
2268   if (SUCCEEDED(hRet))
2269     hRet = VariantChangeTypeEx(&vStr, &vStr, LCID_US, 0, VT_BSTR);
2270 
2271   if (SUCCEEDED(hRet))
2272   {
2273     WCHAR buff[256], decimal[8], thousands[8];
2274     NUMBERFMTW numfmt;
2275 
2276     /* Although MSDN makes it clear that the native versions of these functions
2277      * are implemented using VarTokenizeFormatString()/VarFormatFromTokens(),
2278      * using NLS gives us the same result.
2279      */
2280     if (nDigits < 0)
2281       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
2282     else
2283       numfmt.NumDigits = nDigits;
2284 
2285     if (nLeading == -2)
2286       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
2287     else if (nLeading == -1)
2288       numfmt.LeadingZero = 1;
2289     else
2290       numfmt.LeadingZero = 0;
2291 
2292     if (nGrouping == -2)
2293     {
2294       WCHAR grouping[16];
2295       grouping[2] = '\0';
2296       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, grouping,
2297                      sizeof(grouping)/sizeof(WCHAR));
2298       numfmt.Grouping = grouping[2] == '2' ? 32 : grouping[0] - '0';
2299     }
2300     else if (nGrouping == -1)
2301       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
2302     else
2303       numfmt.Grouping = 0; /* 0 = No grouping */
2304 
2305     if (nParens == -2)
2306       GETLOCALENUMBER(LOCALE_INEGNUMBER, NegativeOrder);
2307     else if (nParens == -1)
2308       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
2309     else
2310       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
2311 
2312     numfmt.lpDecimalSep = decimal;
2313     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal,
2314                    sizeof(decimal)/sizeof(WCHAR));
2315     numfmt.lpThousandSep = thousands;
2316     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thousands,
2317                    sizeof(thousands)/sizeof(WCHAR));
2318 
2319     if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt,
2320                          buff, sizeof(buff)/sizeof(WCHAR)))
2321     {
2322       *pbstrOut = SysAllocString(buff);
2323       if (!*pbstrOut)
2324         hRet = E_OUTOFMEMORY;
2325     }
2326     else
2327       hRet = DISP_E_TYPEMISMATCH;
2328 
2329     SysFreeString(V_BSTR(&vStr));
2330   }
2331   return hRet;
2332 }
2333 
2334 /**********************************************************************
2335  *              VarFormatPercent [OLEAUT32.117]
2336  *
2337  * Format a variant value as a percentage.
2338  *
2339  * PARAMS
2340  *  pVarIn    [I] Variant to format
2341  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2342  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2343  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2344  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2345  *  dwFlags   [I] Currently unused, set to zero
2346  *  pbstrOut  [O] Destination for formatted string.
2347  *
2348  * RETURNS
2349  *  Success: S_OK. pbstrOut contains the formatted value.
2350  *  Failure: E_INVALIDARG, if any parameter is invalid.
2351  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2352  *           DISP_E_OVERFLOW, if overflow occurs during the conversion.
2353  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2354  *
2355  * NOTES
2356  *  This function uses LOCALE_USER_DEFAULT when determining the number format
2357  *  characters to use.
2358  */
2359 HRESULT WINAPI VarFormatPercent(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
2360                                 INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
2361 {
2362   static const WCHAR szPercent[] = { '%','\0' };
2363   static const WCHAR szPercentBracket[] = { '%',')','\0' };
2364   WCHAR buff[256];
2365   HRESULT hRet;
2366   VARIANT vDbl;
2367 
2368   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2369         nParens, nGrouping, dwFlags, pbstrOut);
2370 
2371   if (!pVarIn || !pbstrOut || nDigits > 9)
2372     return E_INVALIDARG;
2373 
2374   *pbstrOut = NULL;
2375 
2376   V_VT(&vDbl) = VT_EMPTY;
2377   hRet = VariantCopyInd(&vDbl, pVarIn);
2378 
2379   if (SUCCEEDED(hRet))
2380   {
2381     hRet = VariantChangeTypeEx(&vDbl, &vDbl, LOCALE_USER_DEFAULT, 0, VT_R8);
2382 
2383     if (SUCCEEDED(hRet))
2384     {
2385       if (V_R8(&vDbl) > (R8_MAX / 100.0))
2386         return DISP_E_OVERFLOW;
2387 
2388       V_R8(&vDbl) *= 100.0;
2389       hRet = VarFormatNumber(&vDbl, nDigits, nLeading, nParens,
2390                              nGrouping, dwFlags, pbstrOut);
2391 
2392       if (SUCCEEDED(hRet))
2393       {
2394         DWORD dwLen = strlenW(*pbstrOut);
2395         BOOL bBracket = (*pbstrOut)[dwLen] == ')';
2396 
2397         dwLen -= bBracket;
2398         memcpy(buff, *pbstrOut, dwLen * sizeof(WCHAR));
2399         strcpyW(buff + dwLen, bBracket ? szPercentBracket : szPercent);
2400         SysFreeString(*pbstrOut);
2401         *pbstrOut = SysAllocString(buff);
2402         if (!*pbstrOut)
2403           hRet = E_OUTOFMEMORY;
2404       }
2405     }
2406   }
2407   return hRet;
2408 }
2409 
2410 /**********************************************************************
2411  *              VarFormatCurrency [OLEAUT32.127]
2412  *
2413  * Format a variant value as a currency.
2414  *
2415  * PARAMS
2416  *  pVarIn    [I] Variant to format
2417  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
2418  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
2419  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
2420  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
2421  *  dwFlags   [I] Currently unused, set to zero
2422  *  pbstrOut  [O] Destination for formatted string.
2423  *
2424  * RETURNS
2425  *  Success: S_OK. pbstrOut contains the formatted value.
2426  *  Failure: E_INVALIDARG, if any parameter is invalid.
2427  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2428  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
2429  *
2430  * NOTES
2431  *  This function uses LOCALE_USER_DEFAULT when determining the currency format
2432  *  characters to use.
2433  */
2434 HRESULT WINAPI VarFormatCurrency(LPVARIANT pVarIn, INT nDigits, INT nLeading,
2435                                  INT nParens, INT nGrouping, ULONG dwFlags,
2436                                  BSTR *pbstrOut)
2437 {
2438   HRESULT hRet;
2439   VARIANT vStr;
2440 
2441   TRACE("(%s,%d,%d,%d,%d,0x%08x,%p)\n", debugstr_variant(pVarIn), nDigits, nLeading,
2442         nParens, nGrouping, dwFlags, pbstrOut);
2443 
2444   if (!pVarIn || !pbstrOut || nDigits > 9)
2445     return E_INVALIDARG;
2446 
2447   *pbstrOut = NULL;
2448 
2449   V_VT(&vStr) = VT_EMPTY;
2450   hRet = VariantCopyInd(&vStr, pVarIn);
2451 
2452   if (SUCCEEDED(hRet))
2453     hRet = VariantChangeTypeEx(&vStr, &vStr, LOCALE_USER_DEFAULT, 0, VT_BSTR);
2454 
2455   if (SUCCEEDED(hRet))
2456   {
2457     WCHAR buff[256], decimal[8], thousands[8], currency[8];
2458     CURRENCYFMTW numfmt;
2459 
2460     if (nDigits < 0)
2461       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
2462     else
2463       numfmt.NumDigits = nDigits;
2464 
2465     if (nLeading == -2)
2466       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
2467     else if (nLeading == -1)
2468       numfmt.LeadingZero = 1;
2469     else
2470       numfmt.LeadingZero = 0;
2471 
2472     if (nGrouping == -2)
2473     {
2474       WCHAR grouping[16];
2475       grouping[2] = '\0';
2476       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, grouping,
2477                      sizeof(grouping)/sizeof(WCHAR));
2478       numfmt.Grouping = grouping[2] == '2' ? 32 : grouping[0] - '0';
2479     }
2480     else if (nGrouping == -1)
2481       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
2482     else
2483       numfmt.Grouping = 0; /* 0 = No grouping */
2484 
2485     if (nParens == -2)
2486       GETLOCALENUMBER(LOCALE_INEGCURR, NegativeOrder);
2487     else if (nParens == -1)
2488       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
2489     else
2490       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
2491 
2492     GETLOCALENUMBER(LOCALE_ICURRENCY, PositiveOrder);
2493 
2494     numfmt.lpDecimalSep = decimal;
2495     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal,
2496                    sizeof(decimal)/sizeof(WCHAR));
2497     numfmt.lpThousandSep = thousands;
2498     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, thousands,
2499                    sizeof(thousands)/sizeof(WCHAR));
2500     numfmt.lpCurrencySymbol = currency;
2501     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, currency,
2502                    sizeof(currency)/sizeof(WCHAR));
2503 
2504     /* use NLS as per VarFormatNumber() */
2505     if (GetCurrencyFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt,
2506                            buff, sizeof(buff)/sizeof(WCHAR)))
2507     {
2508       *pbstrOut = SysAllocString(buff);
2509       if (!*pbstrOut)
2510         hRet = E_OUTOFMEMORY;
2511     }
2512     else
2513       hRet = DISP_E_TYPEMISMATCH;
2514 
2515     SysFreeString(V_BSTR(&vStr));
2516   }
2517   return hRet;
2518 }
2519 
2520 /**********************************************************************
2521  *              VarMonthName [OLEAUT32.129]
2522  *
2523  * Print the specified month as localized name.
2524  *
2525  * PARAMS
2526  *  iMonth    [I] month number 1..12
2527  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
2528  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
2529  *  pbstrOut  [O] Destination for month name
2530  *
2531  * RETURNS
2532  *  Success: S_OK. pbstrOut contains the name.
2533  *  Failure: E_INVALIDARG, if any parameter is invalid.
2534  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2535  */
2536 HRESULT WINAPI VarMonthName(INT iMonth, INT fAbbrev, ULONG dwFlags, BSTR *pbstrOut)
2537 {
2538   DWORD localeValue;
2539   INT size;
2540 
2541   if ((iMonth < 1)  || (iMonth > 12))
2542     return E_INVALIDARG;
2543 
2544   if (dwFlags)
2545     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
2546 
2547   if (fAbbrev)
2548 	localeValue = LOCALE_SABBREVMONTHNAME1 + iMonth - 1;
2549   else
2550 	localeValue = LOCALE_SMONTHNAME1 + iMonth - 1;
2551 
2552   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, NULL, 0);
2553   if (!size) {
2554     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2555     return HRESULT_FROM_WIN32(GetLastError());
2556   }
2557   *pbstrOut = SysAllocStringLen(NULL,size - 1);
2558   if (!*pbstrOut)
2559     return E_OUTOFMEMORY;
2560   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, *pbstrOut, size);
2561   if (!size) {
2562     ERR("GetLocaleInfo of 0x%x failed in 2nd stage?!\n", localeValue);
2563     SysFreeString(*pbstrOut);
2564     return HRESULT_FROM_WIN32(GetLastError());
2565   }
2566   return S_OK;
2567 }
2568 
2569 /**********************************************************************
2570  *              VarWeekdayName [OLEAUT32.129]
2571  *
2572  * Print the specified weekday as localized name.
2573  *
2574  * PARAMS
2575  *  iWeekday  [I] day of week, 1..7, 1="the first day of the week"
2576  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
2577  *  iFirstDay [I] first day of week,
2578  *                0=system default, 1=Sunday, 2=Monday, .. (contrary to MSDN)
2579  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
2580  *  pbstrOut  [O] Destination for weekday name.
2581  *
2582  * RETURNS
2583  *  Success: S_OK, pbstrOut contains the name.
2584  *  Failure: E_INVALIDARG, if any parameter is invalid.
2585  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
2586  */
2587 HRESULT WINAPI VarWeekdayName(INT iWeekday, INT fAbbrev, INT iFirstDay,
2588                               ULONG dwFlags, BSTR *pbstrOut)
2589 {
2590   DWORD localeValue;
2591   INT size;
2592 
2593   /* Windows XP oleaut32.dll doesn't allow iWekday==0, contrary to MSDN */
2594   if (iWeekday < 1 || iWeekday > 7)
2595     return E_INVALIDARG;
2596   if (iFirstDay < 0 || iFirstDay > 7)
2597     return E_INVALIDARG;
2598   if (!pbstrOut)
2599     return E_INVALIDARG;
2600 
2601   if (dwFlags)
2602     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
2603 
2604   /* If we have to use the default firstDay, find which one it is */
2605   if (iFirstDay == 0) {
2606     DWORD firstDay;
2607     localeValue = LOCALE_RETURN_NUMBER | LOCALE_IFIRSTDAYOFWEEK;
2608     size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue,
2609                           (LPWSTR)&firstDay, sizeof(firstDay) / sizeof(WCHAR));
2610     if (!size) {
2611       ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2612       return HRESULT_FROM_WIN32(GetLastError());
2613     }
2614     iFirstDay = firstDay + 2;
2615   }
2616 
2617   /* Determine what we need to return */
2618   localeValue = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
2619   localeValue += (7 + iWeekday - 1 + iFirstDay - 2) % 7;
2620 
2621   /* Determine the size of the data, allocate memory and retrieve the data */
2622   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, NULL, 0);
2623   if (!size) {
2624     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
2625     return HRESULT_FROM_WIN32(GetLastError());
2626   }
2627   *pbstrOut = SysAllocStringLen(NULL, size - 1);
2628   if (!*pbstrOut)
2629     return E_OUTOFMEMORY;
2630   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, *pbstrOut, size);
2631   if (!size) {
2632     ERR("GetLocaleInfo 0x%x failed in 2nd stage?!\n", localeValue);
2633     SysFreeString(*pbstrOut);
2634     return HRESULT_FROM_WIN32(GetLastError());
2635   }
2636   return S_OK;
2637 }
2638