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