ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

varformat.c
Go to the documentation of this file.
00001 /*
00002  * Variant formatting functions
00003  *
00004  * Copyright 2008 Damjan Jovanovic
00005  * Copyright 2003 Jon Griffiths
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00020  *
00021  * NOTES
00022  *  Since the formatting functions aren't properly documented, I used the
00023  *  Visual Basic documentation as a guide to implementing these functions. This
00024  *  means that some named or user-defined formats may work slightly differently.
00025  *  Please submit a test case if you find a difference.
00026  */
00027 
00028 #include "config.h"
00029 
00030 #include <string.h>
00031 #include <stdlib.h>
00032 #include <stdarg.h>
00033 #include <stdio.h>
00034 
00035 #define NONAMELESSUNION
00036 #define NONAMELESSSTRUCT
00037 #include "windef.h"
00038 #include "winbase.h"
00039 #include "wine/unicode.h"
00040 #include "winerror.h"
00041 #include "variant.h"
00042 #include "wine/debug.h"
00043 
00044 WINE_DEFAULT_DEBUG_CHANNEL(variant);
00045 
00046 /* Make sure internal conversions to strings use the '.','+'/'-' and ','
00047  * format chars from the US locale. This enables us to parse the created
00048  * strings to determine the number of decimal places, exponent, etc.
00049  */
00050 #define LCID_US MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT)
00051 
00052 static const WCHAR szPercent_d[] = { '%','d','\0' };
00053 static const WCHAR szPercentZeroTwo_d[] = { '%','0','2','d','\0' };
00054 static const WCHAR szPercentZeroStar_d[] = { '%','0','*','d','\0' };
00055 
00056 #if 0
00057 #define dump_tokens(rgb) do { \
00058   int i_; TRACE("Tokens->{\n"); \
00059   for (i_ = 0; i_ < rgb[0]; i_++) \
00060     TRACE("%s0x%02x", i_?",":"",rgb[i_]); \
00061   TRACE(" }\n"); \
00062   } while(0)
00063 #endif
00064 
00065 /******************************************************************************
00066  * Variant-Formats {OLEAUT32}
00067  *
00068  * NOTES
00069  *  When formatting a variant a variety of format strings may be used to generate
00070  *  different kinds of formatted output. A format string consists of either a named
00071  *  format, or a user-defined format.
00072  *
00073  *  The following named formats are defined:
00074  *| Name           Description
00075  *| ----           -----------
00076  *| General Date   Display Date, and time for non-integer values
00077  *| Short Date     Short date format as defined by locale settings
00078  *| Medium Date    Medium date format as defined by locale settings
00079  *| Long Date      Long date format as defined by locale settings
00080  *| Short Time     Short Time format as defined by locale settings
00081  *| Medium Time    Medium time format as defined by locale settings
00082  *| Long Time      Long time format as defined by locale settings
00083  *| True/False     Localised text of "True" or "False"
00084  *| Yes/No         Localised text of "Yes" or "No"
00085  *| On/Off         Localised text of "On" or "Off"
00086  *| General Number No thousands separator. No decimal points for integers
00087  *| Currency       General currency format using localised characters
00088  *| Fixed          At least one whole and two fractional digits
00089  *| Standard       Same as 'Fixed', but including decimal separators
00090  *| Percent        Multiply by 100 and display a trailing '%' character
00091  *| Scientific     Display with exponent
00092  *
00093  *  User-defined formats consist of a combination of tokens and literal
00094  *  characters. Literal characters are copied unmodified to the formatted
00095  *  output at the position they occupy in the format string. Any character
00096  *  that is not recognised as a token is treated as a literal. A literal can
00097  *  also be specified by preceding it with a backslash character
00098  *  (e.g. "\L\i\t\e\r\a\l") or enclosing it in double quotes.
00099  *
00100  *  A user-defined format can have up to 4 sections, depending on the type of
00101  *  format. The following table lists sections and their meaning:
00102  *| Format Type  Sections Meaning
00103  *| -----------  -------- -------
00104  *| Number       1        Use the same format for all numbers
00105  *| Number       2        Use format 1 for positive and 2 for negative numbers
00106  *| Number       3        Use format 1 for positive, 2 for zero, and 3
00107  *|                       for negative numbers.
00108  *| Number       4        Use format 1 for positive, 2 for zero, 3 for
00109  *|                       negative, and 4 for null numbers.
00110  *| String       1        Use the same format for all strings
00111  *| String       2        Use format 2 for null and empty strings, otherwise
00112  *|                       use format 1.
00113  *| Date         1        Use the same format for all dates
00114  *
00115  *  The formatting tokens fall into several categories depending on the type
00116  *  of formatted output. For more information on each type, see
00117  *  VarFormat-Dates(), VarFormat-Strings() and VarFormat-Numbers().
00118  *
00119  *  SEE ALSO
00120  *  VarTokenizeFormatString(), VarFormatFromTokens(), VarFormat(),
00121  *  VarFormatDateTime(), VarFormatNumber(), VarFormatCurrency().
00122  */
00123 
00124 /******************************************************************************
00125  * VarFormat-Strings {OLEAUT32}
00126  *
00127  * NOTES
00128  *  When formatting a variant as a string, it is first converted to a VT_BSTR.
00129  *  The user-format string defines which characters are copied into which
00130  *  positions in the output string. Literals may be inserted in the format
00131  *  string. When creating the formatted string, excess characters in the string
00132  *  (those not consumed by a token) are appended to the end of the output. If
00133  *  there are more tokens than characters in the string to format, spaces will
00134  *  be inserted at the start of the string if the '@' token was used.
00135  *
00136  *  By default strings are converted to lowercase, or uppercase if the '>' token
00137  *  is encountered. This applies to the whole string: it is not possible to
00138  *  generate a mixed-case output string.
00139  *
00140  *  In user-defined string formats, the following tokens are recognised:
00141  *| Token  Description
00142  *| -----  -----------
00143  *| '@'    Copy a char from the source, or a space if no chars are left.
00144  *| '&'    Copy a char from the source, or write nothing if no chars are left.
00145  *| '<'    Output the whole string as lower-case (the default).
00146  *| '>'    Output the whole string as upper-case.
00147  *| '!'    MSDN indicates that this character should cause right-to-left
00148  *|        copying, however tests show that it is tokenised but not processed.
00149  */
00150 
00151 /*
00152  * Common format definitions
00153  */
00154 
00155  /* Format types */
00156 #define FMT_TYPE_UNKNOWN 0x0
00157 #define FMT_TYPE_GENERAL 0x1
00158 #define FMT_TYPE_NUMBER  0x2
00159 #define FMT_TYPE_DATE    0x3
00160 #define FMT_TYPE_STRING  0x4
00161 
00162 #define FMT_TO_STRING    0x0 /* If header->size == this, act like VB's Str() fn */
00163 
00164 typedef struct tagFMT_SHORT_HEADER
00165 {
00166   BYTE   size;      /* Size of tokenised block (including header), or FMT_TO_STRING */
00167   BYTE   type;      /* Allowable types (FMT_TYPE_*) */
00168   BYTE   offset[1]; /* Offset of the first (and only) format section */
00169 } FMT_SHORT_HEADER;
00170 
00171 typedef struct tagFMT_HEADER
00172 {
00173   BYTE   size;      /* Total size of the whole tokenised block (including header) */
00174   BYTE   type;      /* Allowable types (FMT_TYPE_*) */
00175   BYTE   starts[4]; /* Offset of each of the 4 format sections, or 0 if none */
00176 } FMT_HEADER;
00177 
00178 #define FmtGetPositive(x)  (x->starts[0])
00179 #define FmtGetNegative(x)  (x->starts[1] ? x->starts[1] : x->starts[0])
00180 #define FmtGetZero(x)      (x->starts[2] ? x->starts[2] : x->starts[0])
00181 #define FmtGetNull(x)      (x->starts[3] ? x->starts[3] : x->starts[0])
00182 
00183 /*
00184  * String formats
00185  */
00186 
00187 #define FMT_FLAG_LT  0x1 /* Has '<' (lower case) */
00188 #define FMT_FLAG_GT  0x2 /* Has '>' (upper case) */
00189 #define FMT_FLAG_RTL 0x4 /* Has '!' (Copy right to left) */
00190 
00191 typedef struct tagFMT_STRING_HEADER
00192 {
00193   BYTE   flags;      /* LT, GT, RTL */
00194   BYTE   unknown1;
00195   BYTE   unknown2;
00196   BYTE   copy_chars; /* Number of chars to be copied */
00197   BYTE   unknown3;
00198 } FMT_STRING_HEADER;
00199 
00200 /*
00201  * Number formats
00202  */
00203 
00204 #define FMT_FLAG_PERCENT   0x1  /* Has '%' (Percentage) */
00205 #define FMT_FLAG_EXPONENT  0x2  /* Has 'e' (Exponent/Scientific notation) */
00206 #define FMT_FLAG_THOUSANDS 0x4  /* Has ',' (Standard use of the thousands separator) */
00207 #define FMT_FLAG_BOOL      0x20 /* Boolean format */
00208 
00209 typedef struct tagFMT_NUMBER_HEADER
00210 {
00211   BYTE   flags;      /* PERCENT, EXPONENT, THOUSANDS, BOOL */
00212   BYTE   multiplier; /* Multiplier, 100 for percentages */
00213   BYTE   divisor;    /* Divisor, 1000 if '%%' was used */
00214   BYTE   whole;      /* Number of digits before the decimal point */
00215   BYTE   fractional; /* Number of digits after the decimal point */
00216 } FMT_NUMBER_HEADER;
00217 
00218 /*
00219  * Date Formats
00220  */
00221 typedef struct tagFMT_DATE_HEADER
00222 {
00223   BYTE   flags;
00224   BYTE   unknown1;
00225   BYTE   unknown2;
00226   BYTE   unknown3;
00227   BYTE   unknown4;
00228 } FMT_DATE_HEADER;
00229 
00230 /*
00231  * Format token values
00232  */
00233 #define FMT_GEN_COPY        0x00 /* \n, "lit" => 0,pos,len: Copy len chars from input+pos */
00234 #define FMT_GEN_INLINE      0x01 /*      => 1,len,[chars]: Copy len chars from token stream */
00235 #define FMT_GEN_END         0x02 /* \0,; => 2: End of the tokenised format */
00236 #define FMT_DATE_TIME_SEP   0x03 /* Time separator char */
00237 #define FMT_DATE_DATE_SEP   0x04 /* Date separator char */
00238 #define FMT_DATE_GENERAL    0x05 /* General format date */
00239 #define FMT_DATE_QUARTER    0x06 /* Quarter of the year from 1-4 */
00240 #define FMT_DATE_TIME_SYS   0x07 /* System long time format */
00241 #define FMT_DATE_DAY        0x08 /* Day with no leading 0 */
00242 #define FMT_DATE_DAY_0      0x09 /* Day with leading 0 */
00243 #define FMT_DATE_DAY_SHORT  0x0A /* Short day name */
00244 #define FMT_DATE_DAY_LONG   0x0B /* Long day name */
00245 #define FMT_DATE_SHORT      0x0C /* Short date format */
00246 #define FMT_DATE_LONG       0x0D /* Long date format */
00247 #define FMT_DATE_MEDIUM     0x0E /* Medium date format */
00248 #define FMT_DATE_DAY_WEEK   0x0F /* First day of the week */
00249 #define FMT_DATE_WEEK_YEAR  0x10 /* First week of the year */
00250 #define FMT_DATE_MON        0x11 /* Month with no leading 0 */
00251 #define FMT_DATE_MON_0      0x12 /* Month with leading 0 */
00252 #define FMT_DATE_MON_SHORT  0x13 /* Short month name */
00253 #define FMT_DATE_MON_LONG   0x14 /* Long month name */
00254 #define FMT_DATE_YEAR_DOY   0x15 /* Day of the year with no leading 0 */
00255 #define FMT_DATE_YEAR_0     0x16 /* 2 digit year with leading 0 */
00256 /* NOTE: token 0x17 is not defined, 'yyy' is not valid */
00257 #define FMT_DATE_YEAR_LONG  0x18 /* 4 digit year */
00258 #define FMT_DATE_MIN        0x1A /* Minutes with no leading 0 */
00259 #define FMT_DATE_MIN_0      0x1B /* Minutes with leading 0 */
00260 #define FMT_DATE_SEC        0x1C /* Seconds with no leading 0 */
00261 #define FMT_DATE_SEC_0      0x1D /* Seconds with leading 0 */
00262 #define FMT_DATE_HOUR       0x1E /* Hours with no leading 0 */
00263 #define FMT_DATE_HOUR_0     0x1F /* Hours with leading 0 */
00264 #define FMT_DATE_HOUR_12    0x20 /* Hours with no leading 0, 12 hour clock */
00265 #define FMT_DATE_HOUR_12_0  0x21 /* Hours with leading 0, 12 hour clock */
00266 #define FMT_DATE_TIME_UNK2  0x23
00267 /* FIXME: probably missing some here */
00268 #define FMT_DATE_AMPM_SYS1  0x2E /* AM/PM as defined by system settings */
00269 #define FMT_DATE_AMPM_UPPER 0x2F /* Upper-case AM or PM */
00270 #define FMT_DATE_A_UPPER    0x30 /* Upper-case A or P */
00271 #define FMT_DATE_AMPM_SYS2  0x31 /* AM/PM as defined by system settings */
00272 #define FMT_DATE_AMPM_LOWER 0x32 /* Lower-case AM or PM */
00273 #define FMT_DATE_A_LOWER    0x33 /* Lower-case A or P */
00274 #define FMT_NUM_COPY_ZERO   0x34 /* Copy 1 digit or 0 if no digit */
00275 #define FMT_NUM_COPY_SKIP   0x35 /* Copy 1 digit or skip if no digit */
00276 #define FMT_NUM_DECIMAL     0x36 /* Decimal separator */
00277 #define FMT_NUM_EXP_POS_U   0x37 /* Scientific notation, uppercase, + sign */
00278 #define FMT_NUM_EXP_NEG_U   0x38 /* Scientific notation, uppercase, - sign */
00279 #define FMT_NUM_EXP_POS_L   0x39 /* Scientific notation, lowercase, + sign */
00280 #define FMT_NUM_EXP_NEG_L   0x3A /* Scientific notation, lowercase, - sign */
00281 #define FMT_NUM_CURRENCY    0x3B /* Currency symbol */
00282 #define FMT_NUM_TRUE_FALSE  0x3D /* Convert to "True" or "False" */
00283 #define FMT_NUM_YES_NO      0x3E /* Convert to "Yes" or "No" */
00284 #define FMT_NUM_ON_OFF      0x3F /* Convert to "On" or "Off"  */
00285 #define FMT_STR_COPY_SPACE  0x40 /* Copy len chars with space if no char */
00286 #define FMT_STR_COPY_SKIP   0x41 /* Copy len chars or skip if no char */
00287 /* Wine additions */
00288 #define FMT_WINE_HOURS_12   0x81 /* Hours using 12 hour clock */
00289 
00290 /* Named Formats and their tokenised values */
00291 static const WCHAR szGeneralDate[] = { 'G','e','n','e','r','a','l',' ','D','a','t','e','\0' };
00292 static const BYTE fmtGeneralDate[0x0a] =
00293 {
00294   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00295   0x0,0x0,0x0,0x0,0x0,
00296   FMT_DATE_GENERAL,FMT_GEN_END
00297 };
00298 
00299 static const WCHAR szShortDate[] = { 'S','h','o','r','t',' ','D','a','t','e','\0' };
00300 static const BYTE fmtShortDate[0x0a] =
00301 {
00302   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00303   0x0,0x0,0x0,0x0,0x0,
00304   FMT_DATE_SHORT,FMT_GEN_END
00305 };
00306 
00307 static const WCHAR szMediumDate[] = { 'M','e','d','i','u','m',' ','D','a','t','e','\0' };
00308 static const BYTE fmtMediumDate[0x0a] =
00309 {
00310   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00311   0x0,0x0,0x0,0x0,0x0,
00312   FMT_DATE_MEDIUM,FMT_GEN_END
00313 };
00314 
00315 static const WCHAR szLongDate[] = { 'L','o','n','g',' ','D','a','t','e','\0' };
00316 static const BYTE fmtLongDate[0x0a] =
00317 {
00318   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00319   0x0,0x0,0x0,0x0,0x0,
00320   FMT_DATE_LONG,FMT_GEN_END
00321 };
00322 
00323 static const WCHAR szShortTime[] = { 'S','h','o','r','t',' ','T','i','m','e','\0' };
00324 static const BYTE fmtShortTime[0x0c] =
00325 {
00326   0x0c,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00327   0x0,0x0,0x0,0x0,0x0,
00328   FMT_DATE_TIME_UNK2,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,FMT_GEN_END
00329 };
00330 
00331 static const WCHAR szMediumTime[] = { 'M','e','d','i','u','m',' ','T','i','m','e','\0' };
00332 static const BYTE fmtMediumTime[0x11] =
00333 {
00334   0x11,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00335   0x0,0x0,0x0,0x0,0x0,
00336   FMT_DATE_HOUR_12_0,FMT_DATE_TIME_SEP,FMT_DATE_MIN_0,
00337   FMT_GEN_INLINE,0x01,' ','\0',FMT_DATE_AMPM_SYS1,FMT_GEN_END
00338 };
00339 
00340 static const WCHAR szLongTime[] = { 'L','o','n','g',' ','T','i','m','e','\0' };
00341 static const BYTE fmtLongTime[0x0d] =
00342 {
00343   0x0a,FMT_TYPE_DATE,sizeof(FMT_SHORT_HEADER),
00344   0x0,0x0,0x0,0x0,0x0,
00345   FMT_DATE_TIME_SYS,FMT_GEN_END
00346 };
00347 
00348 static const WCHAR szTrueFalse[] = { 'T','r','u','e','/','F','a','l','s','e','\0' };
00349 static const BYTE fmtTrueFalse[0x0d] =
00350 {
00351   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00352   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
00353   FMT_NUM_TRUE_FALSE,FMT_GEN_END
00354 };
00355 
00356 static const WCHAR szYesNo[] = { 'Y','e','s','/','N','o','\0' };
00357 static const BYTE fmtYesNo[0x0d] =
00358 {
00359   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00360   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
00361   FMT_NUM_YES_NO,FMT_GEN_END
00362 };
00363 
00364 static const WCHAR szOnOff[] = { 'O','n','/','O','f','f','\0' };
00365 static const BYTE fmtOnOff[0x0d] =
00366 {
00367   0x0d,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00368   FMT_FLAG_BOOL,0x0,0x0,0x0,0x0,
00369   FMT_NUM_ON_OFF,FMT_GEN_END
00370 };
00371 
00372 static const WCHAR szGeneralNumber[] = { 'G','e','n','e','r','a','l',' ','N','u','m','b','e','r','\0' };
00373 static const BYTE fmtGeneralNumber[sizeof(FMT_HEADER)] =
00374 {
00375   sizeof(FMT_HEADER),FMT_TYPE_GENERAL,sizeof(FMT_HEADER),0x0,0x0,0x0
00376 };
00377 
00378 static const WCHAR szCurrency[] = { 'C','u','r','r','e','n','c','y','\0' };
00379 static const BYTE fmtCurrency[0x26] =
00380 {
00381   0x26,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x12,0x0,0x0,
00382   /* Positive numbers */
00383   FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
00384   FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
00385   FMT_GEN_END,
00386   /* Negative numbers */
00387   FMT_FLAG_THOUSANDS,0xcc,0x0,0x1,0x2,
00388   FMT_GEN_INLINE,0x1,'(','\0',FMT_NUM_CURRENCY,FMT_NUM_COPY_ZERO,0x1,
00389   FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_INLINE,0x1,')','\0',
00390   FMT_GEN_END
00391 };
00392 
00393 static const WCHAR szFixed[] = { 'F','i','x','e','d','\0' };
00394 static const BYTE fmtFixed[0x11] =
00395 {
00396   0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00397   0x0,0x0,0x0,0x1,0x2,
00398   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
00399 };
00400 
00401 static const WCHAR szStandard[] = { 'S','t','a','n','d','a','r','d','\0' };
00402 static const BYTE fmtStandard[0x11] =
00403 {
00404   0x11,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00405   FMT_FLAG_THOUSANDS,0x0,0x0,0x1,0x2,
00406   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_GEN_END
00407 };
00408 
00409 static const WCHAR szPercent[] = { 'P','e','r','c','e','n','t','\0' };
00410 static const BYTE fmtPercent[0x15] =
00411 {
00412   0x15,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00413   FMT_FLAG_PERCENT,0x1,0x0,0x1,0x2,
00414   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,
00415   FMT_GEN_INLINE,0x1,'%','\0',FMT_GEN_END
00416 };
00417 
00418 static const WCHAR szScientific[] = { 'S','c','i','e','n','t','i','f','i','c','\0' };
00419 static const BYTE fmtScientific[0x13] =
00420 {
00421   0x13,FMT_TYPE_NUMBER,sizeof(FMT_HEADER),0x0,0x0,0x0,
00422   FMT_FLAG_EXPONENT,0x0,0x0,0x1,0x2,
00423   FMT_NUM_COPY_ZERO,0x1,FMT_NUM_DECIMAL,FMT_NUM_COPY_ZERO,0x2,FMT_NUM_EXP_POS_U,0x2,FMT_GEN_END
00424 };
00425 
00426 typedef struct tagNAMED_FORMAT
00427 {
00428   LPCWSTR name;
00429   const BYTE* format;
00430 } NAMED_FORMAT;
00431 
00432 /* Format name to tokenised format. Must be kept sorted by name */
00433 static const NAMED_FORMAT VARIANT_NamedFormats[] =
00434 {
00435   { szCurrency, fmtCurrency },
00436   { szFixed, fmtFixed },
00437   { szGeneralDate, fmtGeneralDate },
00438   { szGeneralNumber, fmtGeneralNumber },
00439   { szLongDate, fmtLongDate },
00440   { szLongTime, fmtLongTime },
00441   { szMediumDate, fmtMediumDate },
00442   { szMediumTime, fmtMediumTime },
00443   { szOnOff, fmtOnOff },
00444   { szPercent, fmtPercent },
00445   { szScientific, fmtScientific },
00446   { szShortDate, fmtShortDate },
00447   { szShortTime, fmtShortTime },
00448   { szStandard, fmtStandard },
00449   { szTrueFalse, fmtTrueFalse },
00450   { szYesNo, fmtYesNo }
00451 };
00452 typedef const NAMED_FORMAT *LPCNAMED_FORMAT;
00453 
00454 static int FormatCompareFn(const void *l, const void *r)
00455 {
00456   return strcmpiW(((LPCNAMED_FORMAT)l)->name, ((LPCNAMED_FORMAT)r)->name);
00457 }
00458 
00459 static inline const BYTE *VARIANT_GetNamedFormat(LPCWSTR lpszFormat)
00460 {
00461   NAMED_FORMAT key;
00462   LPCNAMED_FORMAT fmt;
00463 
00464   key.name = lpszFormat;
00465   fmt = bsearch(&key, VARIANT_NamedFormats,
00466                                  sizeof(VARIANT_NamedFormats)/sizeof(NAMED_FORMAT),
00467                                  sizeof(NAMED_FORMAT), FormatCompareFn);
00468   return fmt ? fmt->format : NULL;
00469 }
00470 
00471 /* Return an error if the token for the value will not fit in the destination */
00472 #define NEED_SPACE(x) if (cbTok < (int)(x)) return TYPE_E_BUFFERTOOSMALL; cbTok -= (x)
00473 
00474 /* Non-zero if the format is unknown or a given type */
00475 #define COULD_BE(typ) ((!fmt_number && header->type==FMT_TYPE_UNKNOWN)||header->type==typ)
00476 
00477 /* State during tokenising */
00478 #define FMT_STATE_OPEN_COPY     0x1 /* Last token written was a copy */
00479 #define FMT_STATE_WROTE_DECIMAL 0x2 /* Already wrote a decimal separator */
00480 #define FMT_STATE_SEEN_HOURS    0x4 /* See the hh specifier */
00481 #define FMT_STATE_WROTE_MINUTES 0x8 /* Wrote minutes */
00482 
00483 /**********************************************************************
00484  *              VarTokenizeFormatString [OLEAUT32.140]
00485  *
00486  * Convert a format string into tokenised form.
00487  *
00488  * PARAMS
00489  *  lpszFormat [I] Format string to tokenise
00490  *  rgbTok     [O] Destination for tokenised format
00491  *  cbTok      [I] Size of rgbTok in bytes
00492  *  nFirstDay  [I] First day of the week (1-7, or 0 for current system default)
00493  *  nFirstWeek [I] How to treat the first week (see notes)
00494  *  lcid       [I] Locale Id of the format string
00495  *  pcbActual  [O] If non-NULL, filled with the first token generated
00496  *
00497  * RETURNS
00498  *  Success: S_OK. rgbTok contains the tokenised format.
00499  *  Failure: E_INVALIDARG, if any argument is invalid.
00500  *           TYPE_E_BUFFERTOOSMALL, if rgbTok is not large enough.
00501  *
00502  * NOTES
00503  * Valid values for the nFirstWeek parameter are:
00504  *| Value  Meaning
00505  *| -----  -------
00506  *|   0    Use the current system default
00507  *|   1    The first week is that containing Jan 1
00508  *|   2    Four or more days of the first week are in the current year
00509  *|   3    The first week is 7 days long
00510  * See Variant-Formats(), VarFormatFromTokens().
00511  */
00512 HRESULT WINAPI VarTokenizeFormatString(LPOLESTR lpszFormat, LPBYTE rgbTok,
00513                                        int cbTok, int nFirstDay, int nFirstWeek,
00514                                        LCID lcid, int *pcbActual)
00515 {
00516   /* Note: none of these strings should be NUL terminated */
00517   static const WCHAR szTTTTT[] = { 't','t','t','t','t' };
00518   static const WCHAR szAMPM[] = { 'A','M','P','M' };
00519   static const WCHAR szampm[] = { 'a','m','p','m' };
00520   static const WCHAR szAMSlashPM[] = { 'A','M','/','P','M' };
00521   static const WCHAR szamSlashpm[] = { 'a','m','/','p','m' };
00522   const BYTE *namedFmt;
00523   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
00524   FMT_STRING_HEADER *str_header = (FMT_STRING_HEADER*)(rgbTok + sizeof(FMT_HEADER));
00525   FMT_NUMBER_HEADER *num_header = (FMT_NUMBER_HEADER*)str_header;
00526   BYTE* pOut = rgbTok + sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER);
00527   BYTE* pLastHours = NULL;
00528   BYTE fmt_number = 0;
00529   DWORD fmt_state = 0;
00530   LPCWSTR pFormat = lpszFormat;
00531 
00532   TRACE("(%s,%p,%d,%d,%d,0x%08x,%p)\n", debugstr_w(lpszFormat), rgbTok, cbTok,
00533         nFirstDay, nFirstWeek, lcid, pcbActual);
00534 
00535   if (!rgbTok ||
00536       nFirstDay < 0 || nFirstDay > 7 || nFirstWeek < 0 || nFirstWeek > 3)
00537     return E_INVALIDARG;
00538 
00539   if (!lpszFormat || !*lpszFormat)
00540   {
00541     /* An empty string means 'general format' */
00542     NEED_SPACE(sizeof(BYTE));
00543     *rgbTok = FMT_TO_STRING;
00544     if (pcbActual)
00545       *pcbActual = FMT_TO_STRING;
00546     return S_OK;
00547   }
00548 
00549   if (cbTok > 255)
00550     cbTok = 255; /* Ensure we error instead of wrapping */
00551 
00552   /* Named formats */
00553   namedFmt = VARIANT_GetNamedFormat(lpszFormat);
00554   if (namedFmt)
00555   {
00556     NEED_SPACE(namedFmt[0]);
00557     memcpy(rgbTok, namedFmt, namedFmt[0]);
00558     TRACE("Using pre-tokenised named format %s\n", debugstr_w(lpszFormat));
00559     /* FIXME: pcbActual */
00560     return S_OK;
00561   }
00562 
00563   /* Insert header */
00564   NEED_SPACE(sizeof(FMT_HEADER) + sizeof(FMT_STRING_HEADER));
00565   memset(header, 0, sizeof(FMT_HEADER));
00566   memset(str_header, 0, sizeof(FMT_STRING_HEADER));
00567 
00568   header->starts[fmt_number] = sizeof(FMT_HEADER);
00569 
00570   while (*pFormat)
00571   {
00572     /* --------------
00573      * General tokens
00574      * --------------
00575      */
00576     if (*pFormat == ';')
00577     {
00578       while (*pFormat == ';')
00579       {
00580         TRACE(";\n");
00581         if (++fmt_number > 3)
00582           return E_INVALIDARG; /* too many formats */
00583         pFormat++;
00584       }
00585       if (*pFormat)
00586       {
00587         TRACE("New header\n");
00588         NEED_SPACE(sizeof(BYTE) + sizeof(FMT_STRING_HEADER));
00589         *pOut++ = FMT_GEN_END;
00590 
00591         header->starts[fmt_number] = pOut - rgbTok;
00592         str_header = (FMT_STRING_HEADER*)pOut;
00593         num_header = (FMT_NUMBER_HEADER*)pOut;
00594         memset(str_header, 0, sizeof(FMT_STRING_HEADER));
00595         pOut += sizeof(FMT_STRING_HEADER);
00596         fmt_state = 0;
00597         pLastHours = NULL;
00598       }
00599     }
00600     else if (*pFormat == '\\')
00601     {
00602       /* Escaped character */
00603       if (pFormat[1])
00604       {
00605         NEED_SPACE(3 * sizeof(BYTE));
00606         pFormat++;
00607         *pOut++ = FMT_GEN_COPY;
00608         *pOut++ = pFormat - lpszFormat;
00609         *pOut++ = 0x1;
00610         fmt_state |= FMT_STATE_OPEN_COPY;
00611         TRACE("'\\'\n");
00612       }
00613       else
00614         fmt_state &= ~FMT_STATE_OPEN_COPY;
00615       pFormat++;
00616     }
00617     else if (*pFormat == '"')
00618     {
00619       /* Escaped string
00620        * Note: Native encodes "" as a copy of length zero. That's just dumb, so
00621        * here we avoid encoding anything in this case.
00622        */
00623       if (!pFormat[1])
00624         pFormat++;
00625       else if (pFormat[1] == '"')
00626       {
00627         pFormat += 2;
00628       }
00629       else
00630       {
00631         LPCWSTR start = ++pFormat;
00632         while (*pFormat && *pFormat != '"')
00633           pFormat++;
00634         NEED_SPACE(3 * sizeof(BYTE));
00635         *pOut++ = FMT_GEN_COPY;
00636         *pOut++ = start - lpszFormat;
00637         *pOut++ = pFormat - start;
00638         if (*pFormat == '"')
00639           pFormat++;
00640         TRACE("Quoted string pos %d, len %d\n", pOut[-2], pOut[-1]);
00641       }
00642       fmt_state &= ~FMT_STATE_OPEN_COPY;
00643     }
00644     /* -------------
00645      * Number tokens
00646      * -------------
00647      */
00648     else if (*pFormat == '0' && COULD_BE(FMT_TYPE_NUMBER))
00649     {
00650       /* Number formats: Digit from number or '0' if no digits
00651        * Other formats: Literal
00652        * Types the format if found
00653        */
00654       header->type = FMT_TYPE_NUMBER;
00655       NEED_SPACE(2 * sizeof(BYTE));
00656       *pOut++ = FMT_NUM_COPY_ZERO;
00657       *pOut = 0x0;
00658       while (*pFormat == '0')
00659       {
00660         *pOut = *pOut + 1;
00661         pFormat++;
00662       }
00663       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
00664         num_header->fractional += *pOut;
00665       else
00666         num_header->whole += *pOut;
00667       TRACE("%d 0's\n", *pOut);
00668       pOut++;
00669       fmt_state &= ~FMT_STATE_OPEN_COPY;
00670     }
00671     else if (*pFormat == '#' && COULD_BE(FMT_TYPE_NUMBER))
00672     {
00673       /* Number formats: Digit from number or blank if no digits
00674        * Other formats: Literal
00675        * Types the format if found
00676        */
00677       header->type = FMT_TYPE_NUMBER;
00678       NEED_SPACE(2 * sizeof(BYTE));
00679       *pOut++ = FMT_NUM_COPY_SKIP;
00680       *pOut = 0x0;
00681       while (*pFormat == '#')
00682       {
00683         *pOut = *pOut + 1;
00684         pFormat++;
00685       }
00686       if (fmt_state & FMT_STATE_WROTE_DECIMAL)
00687         num_header->fractional += *pOut;
00688       else
00689         num_header->whole += *pOut;
00690       TRACE("%d #'s\n", *pOut);
00691       pOut++;
00692       fmt_state &= ~FMT_STATE_OPEN_COPY;
00693     }
00694     else if (*pFormat == '.' && COULD_BE(FMT_TYPE_NUMBER) &&
00695               !(fmt_state & FMT_STATE_WROTE_DECIMAL))
00696     {
00697       /* Number formats: Decimal separator when 1st seen, literal thereafter
00698        * Other formats: Literal
00699        * Types the format if found
00700        */
00701       header->type = FMT_TYPE_NUMBER;
00702       NEED_SPACE(sizeof(BYTE));
00703       *pOut++ = FMT_NUM_DECIMAL;
00704       fmt_state |= FMT_STATE_WROTE_DECIMAL;
00705       fmt_state &= ~FMT_STATE_OPEN_COPY;
00706       pFormat++;
00707       TRACE("decimal sep\n");
00708     }
00709     else if ((*pFormat == 'e' || *pFormat == 'E') && (pFormat[1] == '-' ||
00710               pFormat[1] == '+') && header->type == FMT_TYPE_NUMBER)
00711     {
00712       /* Number formats: Exponent specifier
00713        * Other formats: Literal
00714        */
00715       num_header->flags |= FMT_FLAG_EXPONENT;
00716       NEED_SPACE(2 * sizeof(BYTE));
00717       if (*pFormat == 'e') {
00718         if (pFormat[1] == '+')
00719           *pOut = FMT_NUM_EXP_POS_L;
00720         else
00721           *pOut = FMT_NUM_EXP_NEG_L;
00722       } else {
00723         if (pFormat[1] == '+')
00724           *pOut = FMT_NUM_EXP_POS_U;
00725         else
00726           *pOut = FMT_NUM_EXP_NEG_U;
00727       }
00728       pFormat += 2;
00729       *++pOut = 0x0;
00730       while (*pFormat == '0')
00731       {
00732         *pOut = *pOut + 1;
00733         pFormat++;
00734       }
00735       pOut++;
00736       TRACE("exponent\n");
00737     }
00738     /* FIXME: %% => Divide by 1000 */
00739     else if (*pFormat == ',' && header->type == FMT_TYPE_NUMBER)
00740     {
00741       /* Number formats: Use the thousands separator
00742        * Other formats: Literal
00743        */
00744       num_header->flags |= FMT_FLAG_THOUSANDS;
00745       pFormat++;
00746       fmt_state &= ~FMT_STATE_OPEN_COPY;
00747       TRACE("thousands sep\n");
00748     }
00749     /* -----------
00750      * Date tokens
00751      * -----------
00752      */
00753     else if (*pFormat == '/' && COULD_BE(FMT_TYPE_DATE))
00754     {
00755       /* Date formats: Date separator
00756        * Other formats: Literal
00757        * Types the format if found
00758        */
00759       header->type = FMT_TYPE_DATE;
00760       NEED_SPACE(sizeof(BYTE));
00761       *pOut++ = FMT_DATE_DATE_SEP;
00762       pFormat++;
00763       fmt_state &= ~FMT_STATE_OPEN_COPY;
00764       TRACE("date sep\n");
00765     }
00766     else if (*pFormat == ':' && COULD_BE(FMT_TYPE_DATE))
00767     {
00768       /* Date formats: Time separator
00769        * Other formats: Literal
00770        * Types the format if found
00771        */
00772       header->type = FMT_TYPE_DATE;
00773       NEED_SPACE(sizeof(BYTE));
00774       *pOut++ = FMT_DATE_TIME_SEP;
00775       pFormat++;
00776       fmt_state &= ~FMT_STATE_OPEN_COPY;
00777       TRACE("time sep\n");
00778     }
00779     else if ((*pFormat == 'a' || *pFormat == 'A') &&
00780               !strncmpiW(pFormat, szAMPM, sizeof(szAMPM)/sizeof(WCHAR)))
00781     {
00782       /* Date formats: System AM/PM designation
00783        * Other formats: Literal
00784        * Types the format if found
00785        */
00786       header->type = FMT_TYPE_DATE;
00787       NEED_SPACE(sizeof(BYTE));
00788       pFormat += sizeof(szAMPM)/sizeof(WCHAR);
00789       if (!strncmpW(pFormat, szampm, sizeof(szampm)/sizeof(WCHAR)))
00790         *pOut++ = FMT_DATE_AMPM_SYS2;
00791       else
00792         *pOut++ = FMT_DATE_AMPM_SYS1;
00793       if (pLastHours)
00794         *pLastHours = *pLastHours + 2;
00795       TRACE("ampm\n");
00796     }
00797     else if (*pFormat == 'a' && pFormat[1] == '/' &&
00798               (pFormat[2] == 'p' || pFormat[2] == 'P'))
00799     {
00800       /* Date formats: lowercase a or p designation
00801        * Other formats: Literal
00802        * Types the format if found
00803        */
00804       header->type = FMT_TYPE_DATE;
00805       NEED_SPACE(sizeof(BYTE));
00806       pFormat += 3;
00807       *pOut++ = FMT_DATE_A_LOWER;
00808       if (pLastHours)
00809         *pLastHours = *pLastHours + 2;
00810       TRACE("a/p\n");
00811     }
00812     else if (*pFormat == 'A' && pFormat[1] == '/' &&
00813               (pFormat[2] == 'p' || pFormat[2] == 'P'))
00814     {
00815       /* Date formats: Uppercase a or p designation
00816        * Other formats: Literal
00817        * Types the format if found
00818        */
00819       header->type = FMT_TYPE_DATE;
00820       NEED_SPACE(sizeof(BYTE));
00821       pFormat += 3;
00822       *pOut++ = FMT_DATE_A_UPPER;
00823       if (pLastHours)
00824         *pLastHours = *pLastHours + 2;
00825       TRACE("A/P\n");
00826     }
00827     else if (*pFormat == 'a' &&
00828               !strncmpW(pFormat, szamSlashpm, sizeof(szamSlashpm)/sizeof(WCHAR)))
00829     {
00830       /* Date formats: lowercase AM or PM designation
00831        * Other formats: Literal
00832        * Types the format if found
00833        */
00834       header->type = FMT_TYPE_DATE;
00835       NEED_SPACE(sizeof(BYTE));
00836       pFormat += sizeof(szamSlashpm)/sizeof(WCHAR);
00837       *pOut++ = FMT_DATE_AMPM_LOWER;
00838       if (pLastHours)
00839         *pLastHours = *pLastHours + 2;
00840       TRACE("AM/PM\n");
00841     }
00842     else if (*pFormat == 'A' &&
00843               !strncmpW(pFormat, szAMSlashPM, sizeof(szAMSlashPM)/sizeof(WCHAR)))
00844     {
00845       /* Date formats: Uppercase AM or PM designation
00846        * Other formats: Literal
00847        * Types the format if found
00848        */
00849       header->type = FMT_TYPE_DATE;
00850       NEED_SPACE(sizeof(BYTE));
00851       pFormat += sizeof(szAMSlashPM)/sizeof(WCHAR);
00852       *pOut++ = FMT_DATE_AMPM_UPPER;
00853       TRACE("AM/PM\n");
00854     }
00855     else if ((*pFormat == 'c' || *pFormat == 'C') && COULD_BE(FMT_TYPE_DATE))
00856     {
00857       /* Date formats: General date format
00858        * Other formats: Literal
00859        * Types the format if found
00860        */
00861       header->type = FMT_TYPE_DATE;
00862       NEED_SPACE(sizeof(BYTE));
00863       pFormat += sizeof(szAMSlashPM)/sizeof(WCHAR);
00864       *pOut++ = FMT_DATE_GENERAL;
00865       TRACE("gen date\n");
00866     }
00867     else if ((*pFormat == 'd' || *pFormat == 'D') && COULD_BE(FMT_TYPE_DATE))
00868     {
00869       /* Date formats: Day specifier
00870        * Other formats: Literal
00871        * Types the format if found
00872        */
00873       int count = -1;
00874       header->type = FMT_TYPE_DATE;
00875       while ((*pFormat == 'd' || *pFormat == 'D') && count < 6)
00876       {
00877         pFormat++;
00878         count++;
00879       }
00880       NEED_SPACE(sizeof(BYTE));
00881       *pOut++ = FMT_DATE_DAY + count;
00882       fmt_state &= ~FMT_STATE_OPEN_COPY;
00883       /* When we find the days token, reset the seen hours state so that
00884        * 'mm' is again written as month when encountered.
00885        */
00886       fmt_state &= ~FMT_STATE_SEEN_HOURS;
00887       TRACE("%d d's\n", count + 1);
00888     }
00889     else if ((*pFormat == 'h' || *pFormat == 'H') && COULD_BE(FMT_TYPE_DATE))
00890     {
00891       /* Date formats: Hour specifier
00892        * Other formats: Literal
00893        * Types the format if found
00894        */
00895       header->type = FMT_TYPE_DATE;
00896       NEED_SPACE(sizeof(BYTE));
00897       pFormat++;
00898       /* Record the position of the hours specifier - if we encounter
00899        * an am/pm specifier we will change the hours from 24 to 12.
00900        */
00901       pLastHours = pOut;
00902       if (*pFormat == 'h' || *pFormat == 'H')
00903       {
00904         pFormat++;
00905         *pOut++ = FMT_DATE_HOUR_0;
00906         TRACE("hh\n");
00907       }
00908       else
00909       {
00910         *pOut++ = FMT_DATE_HOUR;
00911         TRACE("h\n");
00912       }
00913       fmt_state &= ~FMT_STATE_OPEN_COPY;
00914       /* Note that now we have seen an hours token, the next occurrence of
00915        * 'mm' indicates minutes, not months.
00916        */
00917       fmt_state |= FMT_STATE_SEEN_HOURS;
00918     }
00919     else if ((*pFormat == 'm' || *pFormat == 'M') && COULD_BE(FMT_TYPE_DATE))
00920     {
00921       /* Date formats: Month specifier (or Minute specifier, after hour specifier)
00922        * Other formats: Literal
00923        * Types the format if found
00924        */
00925       int count = -1;
00926       header->type = FMT_TYPE_DATE;
00927       while ((*pFormat == 'm' || *pFormat == 'M') && count < 4)
00928       {
00929         pFormat++;
00930         count++;
00931       }
00932       NEED_SPACE(sizeof(BYTE));
00933       if (count <= 1 && fmt_state & FMT_STATE_SEEN_HOURS &&
00934           !(fmt_state & FMT_STATE_WROTE_MINUTES))
00935       {
00936         /* We have seen an hours specifier and not yet written a minutes
00937          * specifier. Write this as minutes and thereafter as months.
00938          */
00939         *pOut++ = count == 1 ? FMT_DATE_MIN_0 : FMT_DATE_MIN;
00940         fmt_state |= FMT_STATE_WROTE_MINUTES; /* Hereafter write months */
00941       }
00942       else
00943         *pOut++ = FMT_DATE_MON + count; /* Months */
00944       fmt_state &= ~FMT_STATE_OPEN_COPY;
00945       TRACE("%d m's\n", count + 1);
00946     }
00947     else if ((*pFormat == 'n' || *pFormat == 'N') && COULD_BE(FMT_TYPE_DATE))
00948     {
00949       /* Date formats: Minute specifier
00950        * Other formats: Literal
00951        * Types the format if found
00952        */
00953       header->type = FMT_TYPE_DATE;
00954       NEED_SPACE(sizeof(BYTE));
00955       pFormat++;
00956       if (*pFormat == 'n' || *pFormat == 'N')
00957       {
00958         pFormat++;
00959         *pOut++ = FMT_DATE_MIN_0;
00960         TRACE("nn\n");
00961       }
00962       else
00963       {
00964         *pOut++ = FMT_DATE_MIN;
00965         TRACE("n\n");
00966       }
00967       fmt_state &= ~FMT_STATE_OPEN_COPY;
00968     }
00969     else if ((*pFormat == 'q' || *pFormat == 'Q') && COULD_BE(FMT_TYPE_DATE))
00970     {
00971       /* Date formats: Quarter specifier
00972        * Other formats: Literal
00973        * Types the format if found
00974        */
00975       header->type = FMT_TYPE_DATE;
00976       NEED_SPACE(sizeof(BYTE));
00977       *pOut++ = FMT_DATE_QUARTER;
00978       pFormat++;
00979       fmt_state &= ~FMT_STATE_OPEN_COPY;
00980       TRACE("quarter\n");
00981     }
00982     else if ((*pFormat == 's' || *pFormat == 'S') && COULD_BE(FMT_TYPE_DATE))
00983     {
00984       /* Date formats: Second specifier
00985        * Other formats: Literal
00986        * Types the format if found
00987        */
00988       header->type = FMT_TYPE_DATE;
00989       NEED_SPACE(sizeof(BYTE));
00990       pFormat++;
00991       if (*pFormat == 's' || *pFormat == 'S')
00992       {
00993         pFormat++;
00994         *pOut++ = FMT_DATE_SEC_0;
00995         TRACE("ss\n");
00996       }
00997       else
00998       {
00999         *pOut++ = FMT_DATE_SEC;
01000         TRACE("s\n");
01001       }
01002       fmt_state &= ~FMT_STATE_OPEN_COPY;
01003     }
01004     else if ((*pFormat == 't' || *pFormat == 'T') &&
01005               !strncmpiW(pFormat, szTTTTT, sizeof(szTTTTT)/sizeof(WCHAR)))
01006     {
01007       /* Date formats: System time specifier
01008        * Other formats: Literal
01009        * Types the format if found
01010        */
01011       header->type = FMT_TYPE_DATE;
01012       pFormat += sizeof(szTTTTT)/sizeof(WCHAR);
01013       NEED_SPACE(sizeof(BYTE));
01014       *pOut++ = FMT_DATE_TIME_SYS;
01015       fmt_state &= ~FMT_STATE_OPEN_COPY;
01016     }
01017     else if ((*pFormat == 'w' || *pFormat == 'W') && COULD_BE(FMT_TYPE_DATE))
01018     {
01019       /* Date formats: Week of the year/Day of the week
01020        * Other formats: Literal
01021        * Types the format if found
01022        */
01023       header->type = FMT_TYPE_DATE;
01024       pFormat++;
01025       if (*pFormat == 'w' || *pFormat == 'W')
01026       {
01027         NEED_SPACE(3 * sizeof(BYTE));
01028         pFormat++;
01029         *pOut++ = FMT_DATE_WEEK_YEAR;
01030         *pOut++ = nFirstDay;
01031         *pOut++ = nFirstWeek;
01032         TRACE("ww\n");
01033       }
01034       else
01035       {
01036         NEED_SPACE(2 * sizeof(BYTE));
01037         *pOut++ = FMT_DATE_DAY_WEEK;
01038         *pOut++ = nFirstDay;
01039         TRACE("w\n");
01040       }
01041 
01042       fmt_state &= ~FMT_STATE_OPEN_COPY;
01043     }
01044     else if ((*pFormat == 'y' || *pFormat == 'Y') && COULD_BE(FMT_TYPE_DATE))
01045     {
01046       /* Date formats: Day of year/Year specifier
01047        * Other formats: Literal
01048        * Types the format if found
01049        */
01050       int count = -1;
01051       header->type = FMT_TYPE_DATE;
01052       while ((*pFormat == 'y' || *pFormat == 'Y') && count < 4)
01053       {
01054         pFormat++;
01055         count++;
01056       }
01057       if (count == 2)
01058       {
01059         count--; /* 'yyy' has no meaning, despite what MSDN says */
01060         pFormat--;
01061       }
01062       NEED_SPACE(sizeof(BYTE));
01063       *pOut++ = FMT_DATE_YEAR_DOY + count;
01064       fmt_state &= ~FMT_STATE_OPEN_COPY;
01065       TRACE("%d y's\n", count + 1);
01066     }
01067     /* -------------
01068      * String tokens
01069      * -------------
01070      */
01071     else if (*pFormat == '@' && COULD_BE(FMT_TYPE_STRING))
01072     {
01073       /* String formats: Character from string or space if no char
01074        * Other formats: Literal
01075        * Types the format if found
01076        */
01077       header->type = FMT_TYPE_STRING;
01078       NEED_SPACE(2 * sizeof(BYTE));
01079       *pOut++ = FMT_STR_COPY_SPACE;
01080       *pOut = 0x0;
01081       while (*pFormat == '@')
01082       {
01083         *pOut = *pOut + 1;
01084         str_header->copy_chars++;
01085         pFormat++;
01086       }
01087       TRACE("%d @'s\n", *pOut);
01088       pOut++;
01089       fmt_state &= ~FMT_STATE_OPEN_COPY;
01090     }
01091     else if (*pFormat == '&' && COULD_BE(FMT_TYPE_STRING))
01092     {
01093       /* String formats: Character from string or skip if no char
01094        * Other formats: Literal
01095        * Types the format if found
01096        */
01097       header->type = FMT_TYPE_STRING;
01098       NEED_SPACE(2 * sizeof(BYTE));
01099       *pOut++ = FMT_STR_COPY_SKIP;
01100       *pOut = 0x0;
01101       while (*pFormat == '&')
01102       {
01103         *pOut = *pOut + 1;
01104         str_header->copy_chars++;
01105         pFormat++;
01106       }
01107       TRACE("%d &'s\n", *pOut);
01108       pOut++;
01109       fmt_state &= ~FMT_STATE_OPEN_COPY;
01110     }
01111     else if ((*pFormat == '<' || *pFormat == '>') && COULD_BE(FMT_TYPE_STRING))
01112     {
01113       /* String formats: Use upper/lower case
01114        * Other formats: Literal
01115        * Types the format if found
01116        */
01117       header->type = FMT_TYPE_STRING;
01118       if (*pFormat == '<')
01119         str_header->flags |= FMT_FLAG_LT;
01120       else
01121         str_header->flags |= FMT_FLAG_GT;
01122       TRACE("to %s case\n", *pFormat == '<' ? "lower" : "upper");
01123       pFormat++;
01124       fmt_state &= ~FMT_STATE_OPEN_COPY;
01125     }
01126     else if (*pFormat == '!' && COULD_BE(FMT_TYPE_STRING))
01127     {
01128       /* String formats: Copy right to left
01129        * Other formats: Literal
01130        * Types the format if found
01131        */
01132       header->type = FMT_TYPE_STRING;
01133       str_header->flags |= FMT_FLAG_RTL;
01134       pFormat++;
01135       fmt_state &= ~FMT_STATE_OPEN_COPY;
01136       TRACE("copy right-to-left\n");
01137     }
01138     /* --------
01139      * Literals
01140      * --------
01141      */
01142     /* FIXME: [ seems to be ignored */
01143     else
01144     {
01145       if (*pFormat == '%' && header->type == FMT_TYPE_NUMBER)
01146       {
01147         /* Number formats: Percentage indicator, also a literal
01148          * Other formats: Literal
01149          * Doesn't type the format
01150          */
01151         num_header->flags |= FMT_FLAG_PERCENT;
01152       }
01153 
01154       if (fmt_state & FMT_STATE_OPEN_COPY)
01155       {
01156         pOut[-1] = pOut[-1] + 1; /* Increase the length of the open copy */
01157         TRACE("extend copy (char '%c'), length now %d\n", *pFormat, pOut[-1]);
01158       }
01159       else
01160       {
01161         /* Create a new open copy */
01162         TRACE("New copy (char '%c')\n", *pFormat);
01163         NEED_SPACE(3 * sizeof(BYTE));
01164         *pOut++ = FMT_GEN_COPY;
01165         *pOut++ = pFormat - lpszFormat;
01166         *pOut++ = 0x1;
01167         fmt_state |= FMT_STATE_OPEN_COPY;
01168       }
01169       pFormat++;
01170     }
01171   }
01172 
01173   *pOut++ = FMT_GEN_END;
01174 
01175   header->size = pOut - rgbTok;
01176   if (pcbActual)
01177     *pcbActual = header->size;
01178 
01179   return S_OK;
01180 }
01181 
01182 /* Number formatting state flags */
01183 #define NUM_WROTE_DEC  0x01 /* Written the decimal separator */
01184 #define NUM_WRITE_ON   0x02 /* Started to write the number */
01185 #define NUM_WROTE_SIGN 0x04 /* Written the negative sign */
01186 
01187 /* Format a variant using a number format */
01188 static HRESULT VARIANT_FormatNumber(LPVARIANT pVarIn, LPOLESTR lpszFormat,
01189                                     LPBYTE rgbTok, ULONG dwFlags,
01190                                     BSTR *pbstrOut, LCID lcid)
01191 {
01192   BYTE rgbDig[256], *prgbDig;
01193   NUMPARSE np;
01194   int have_int, need_int = 0, have_frac, need_frac, exponent = 0, pad = 0;
01195   WCHAR buff[256], *pBuff = buff;
01196   WCHAR thousandSeparator[32];
01197   VARIANT vString, vBool;
01198   DWORD dwState = 0;
01199   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
01200   FMT_NUMBER_HEADER *numHeader;
01201   const BYTE* pToken = NULL;
01202   HRESULT hRes = S_OK;
01203 
01204   TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn),
01205         debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut,
01206         lcid);
01207 
01208   V_VT(&vString) = VT_EMPTY;
01209   V_VT(&vBool) = VT_BOOL;
01210 
01211   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
01212   {
01213     have_int = have_frac = 0;
01214     numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNull(header));
01215     V_BOOL(&vBool) = VARIANT_FALSE;
01216   }
01217   else
01218   {
01219     /* Get a number string from pVarIn, and parse it */
01220     hRes = VariantChangeTypeEx(&vString, pVarIn, LCID_US, VARIANT_NOUSEROVERRIDE, VT_BSTR);
01221     if (FAILED(hRes))
01222       return hRes;
01223 
01224     np.cDig = sizeof(rgbDig);
01225     np.dwInFlags = NUMPRS_STD;
01226     hRes = VarParseNumFromStr(V_BSTR(&vString), LCID_US, 0, &np, rgbDig);
01227     if (FAILED(hRes))
01228       return hRes;
01229 
01230     have_int = np.cDig;
01231     have_frac = 0;
01232     exponent = np.nPwr10;
01233 
01234     /* Figure out which format to use */
01235     if (np.dwOutFlags & NUMPRS_NEG)
01236     {
01237       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetNegative(header));
01238       V_BOOL(&vBool) = VARIANT_TRUE;
01239     }
01240     else if (have_int == 1 && !exponent && rgbDig[0] == 0)
01241     {
01242       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetZero(header));
01243       V_BOOL(&vBool) = VARIANT_FALSE;
01244     }
01245     else
01246     {
01247       numHeader = (FMT_NUMBER_HEADER*)(rgbTok + FmtGetPositive(header));
01248       V_BOOL(&vBool) = VARIANT_TRUE;
01249     }
01250 
01251     TRACE("num header: flags = 0x%x, mult=%d, div=%d, whole=%d, fract=%d\n",
01252           numHeader->flags, numHeader->multiplier, numHeader->divisor,
01253           numHeader->whole, numHeader->fractional);
01254 
01255     need_int = numHeader->whole;
01256     need_frac = numHeader->fractional;
01257 
01258     if (numHeader->flags & FMT_FLAG_PERCENT &&
01259         !(have_int == 1 && !exponent && rgbDig[0] == 0))
01260       exponent += 2;
01261 
01262     if (numHeader->flags & FMT_FLAG_EXPONENT)
01263     {
01264       /* Exponent format: length of the integral number part is fixed and
01265          specified by the format. */
01266       pad = need_int - have_int;
01267       exponent -= pad;
01268       if (pad < 0)
01269       {
01270         have_int = need_int;
01271         have_frac -= pad;
01272         pad = 0;
01273       }
01274     }
01275     else
01276     {
01277       /* Convert the exponent */
01278       pad = max(exponent, -have_int);
01279       exponent -= pad;
01280       if (pad < 0)
01281       {
01282         have_int += pad;
01283         have_frac = -pad;
01284         pad = 0;
01285       }
01286       if(exponent < 0 && exponent > (-256 + have_int + have_frac))
01287       {
01288         /* Remove exponent notation */
01289         memmove(rgbDig - exponent, rgbDig, have_int + have_frac);
01290         ZeroMemory(rgbDig, -exponent);
01291         have_frac -= exponent;
01292         exponent = 0;
01293       }
01294     }
01295 
01296     /* Rounding the number */
01297     if (have_frac > need_frac)
01298     {
01299       prgbDig = &rgbDig[have_int + need_frac];
01300       have_frac = need_frac;
01301       if (*prgbDig >= 5)
01302       {
01303         while (prgbDig-- > rgbDig && *prgbDig == 9)
01304           *prgbDig = 0;
01305         if (prgbDig < rgbDig)
01306         {
01307           /* We reached the first digit and that was also a 9 */
01308           rgbDig[0] = 1;
01309           if (numHeader->flags & FMT_FLAG_EXPONENT)
01310             exponent++;
01311           else
01312           {
01313             rgbDig[have_int + need_frac] = 0;
01314             if (exponent < 0)
01315               exponent++;
01316             else
01317               have_int++;
01318           }
01319         }
01320         else
01321           (*prgbDig)++;
01322       }
01323       /* We converted trailing digits to zeroes => have_frac has changed */
01324       while (have_frac > 0 && rgbDig[have_int + have_frac - 1] == 0)
01325         have_frac--;
01326     }
01327     TRACE("have_int=%d,need_int=%d,have_frac=%d,need_frac=%d,pad=%d,exp=%d\n",
01328           have_int, need_int, have_frac, need_frac, pad, exponent);
01329   }
01330 
01331   if (numHeader->flags & FMT_FLAG_THOUSANDS)
01332   {
01333     if (!GetLocaleInfoW(lcid, LOCALE_STHOUSAND, thousandSeparator,
01334                         sizeof(thousandSeparator)/sizeof(WCHAR)))
01335     {
01336       thousandSeparator[0] = ',';
01337       thousandSeparator[1] = 0;
01338     }
01339   }
01340 
01341   pToken = (const BYTE*)numHeader + sizeof(FMT_NUMBER_HEADER);
01342   prgbDig = rgbDig;
01343 
01344   while (SUCCEEDED(hRes) && *pToken != FMT_GEN_END)
01345   {
01346     WCHAR defaultChar = '?';
01347     DWORD boolFlag, localeValue = 0;
01348     BOOL shouldAdvance = TRUE;
01349 
01350     if (pToken - rgbTok > header->size)
01351     {
01352       ERR("Ran off the end of the format!\n");
01353       hRes = E_INVALIDARG;
01354       goto VARIANT_FormatNumber_Exit;
01355     }
01356 
01357     switch (*pToken)
01358     {
01359     case FMT_GEN_COPY:
01360       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
01361       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
01362       pBuff += pToken[2];
01363       pToken += 2;
01364       break;
01365 
01366     case FMT_GEN_INLINE:
01367       pToken += 2;
01368       TRACE("copy %s\n", debugstr_a((LPCSTR)pToken));
01369       while (*pToken)
01370         *pBuff++ = *pToken++;
01371       break;
01372 
01373     case FMT_NUM_YES_NO:
01374       boolFlag = VAR_BOOLYESNO;
01375       goto VARIANT_FormatNumber_Bool;
01376 
01377     case FMT_NUM_ON_OFF:
01378       boolFlag = VAR_BOOLONOFF;
01379       goto VARIANT_FormatNumber_Bool;
01380 
01381     case FMT_NUM_TRUE_FALSE:
01382       boolFlag = VAR_LOCALBOOL;
01383 
01384 VARIANT_FormatNumber_Bool:
01385       {
01386         BSTR boolStr = NULL;
01387 
01388         if (pToken[1] != FMT_GEN_END)
01389         {
01390           ERR("Boolean token not at end of format!\n");
01391           hRes = E_INVALIDARG;
01392           goto VARIANT_FormatNumber_Exit;
01393         }
01394         hRes = VarBstrFromBool(V_BOOL(&vBool), lcid, boolFlag, &boolStr);
01395         if (SUCCEEDED(hRes))
01396         {
01397           strcpyW(pBuff, boolStr);
01398           SysFreeString(boolStr);
01399           while (*pBuff)
01400             pBuff++;
01401         }
01402       }
01403       break;
01404 
01405     case FMT_NUM_DECIMAL:
01406       if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
01407       {
01408         /* last chance for a negative sign in the .# case */
01409         TRACE("write negative sign\n");
01410         localeValue = LOCALE_SNEGATIVESIGN;
01411         defaultChar = '-';
01412         dwState |= NUM_WROTE_SIGN;
01413         shouldAdvance = FALSE;
01414         break;
01415       }
01416       TRACE("write decimal separator\n");
01417       localeValue = LOCALE_SDECIMAL;
01418       defaultChar = '.';
01419       dwState |= NUM_WROTE_DEC;
01420       break;
01421 
01422     case FMT_NUM_CURRENCY:
01423       TRACE("write currency symbol\n");
01424       localeValue = LOCALE_SCURRENCY;
01425       defaultChar = '$';
01426       break;
01427 
01428     case FMT_NUM_EXP_POS_U:
01429     case FMT_NUM_EXP_POS_L:
01430     case FMT_NUM_EXP_NEG_U:
01431     case FMT_NUM_EXP_NEG_L:
01432       if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_NEG_L)
01433         *pBuff++ = 'e';
01434       else
01435         *pBuff++ = 'E';
01436       if (exponent < 0)
01437       {
01438         *pBuff++ = '-';
01439         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], -exponent);
01440       }
01441       else
01442       {
01443         if (*pToken == FMT_NUM_EXP_POS_L || *pToken == FMT_NUM_EXP_POS_U)
01444           *pBuff++ = '+';
01445         sprintfW(pBuff, szPercentZeroStar_d, pToken[1], exponent);
01446       }
01447       while (*pBuff)
01448         pBuff++;
01449       pToken++;
01450       break;
01451 
01452     case FMT_NUM_COPY_ZERO:
01453       dwState |= NUM_WRITE_ON;
01454       /* Fall through */
01455 
01456     case FMT_NUM_COPY_SKIP:
01457       TRACE("write %d %sdigits or %s\n", pToken[1],
01458             dwState & NUM_WROTE_DEC ? "fractional " : "",
01459             *pToken == FMT_NUM_COPY_ZERO ? "0" : "skip");
01460 
01461       if (dwState & NUM_WROTE_DEC)
01462       {
01463         int count, i;
01464 
01465         if (!(numHeader->flags & FMT_FLAG_EXPONENT) && exponent < 0)
01466         {
01467           /* Pad with 0 before writing the fractional digits */
01468           pad = max(exponent, -pToken[1]);
01469           exponent -= pad;
01470           count = min(have_frac, pToken[1] + pad);
01471           for (i = 0; i > pad; i--)
01472             *pBuff++ = '0';
01473         }
01474         else
01475           count = min(have_frac, pToken[1]);
01476 
01477         pad += pToken[1] - count;
01478         have_frac -= count;
01479         while (count--)
01480           *pBuff++ = '0' + *prgbDig++;
01481         if (*pToken == FMT_NUM_COPY_ZERO)
01482         {
01483           for (; pad > 0; pad--)
01484             *pBuff++ = '0'; /* Write zeros for missing trailing digits */
01485         }
01486       }
01487       else
01488       {
01489         int count, count_max, position;
01490 
01491         if ((np.dwOutFlags & NUMPRS_NEG) && !(dwState & NUM_WROTE_SIGN) && !header->starts[1])
01492         {
01493           TRACE("write negative sign\n");
01494           localeValue = LOCALE_SNEGATIVESIGN;
01495           defaultChar = '-';
01496           dwState |= NUM_WROTE_SIGN;
01497           shouldAdvance = FALSE;
01498           break;
01499         }
01500 
01501         position = have_int + pad;
01502         if (dwState & NUM_WRITE_ON)
01503           position = max(position, need_int);
01504         need_int -= pToken[1];
01505         count_max = have_int + pad - need_int;
01506         if (count_max < 0)
01507             count_max = 0;
01508         if (dwState & NUM_WRITE_ON)
01509         {
01510           count = pToken[1] - count_max;
01511           TRACE("write %d leading zeros\n", count);
01512           while (count-- > 0)
01513           {
01514             *pBuff++ = '0';
01515             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
01516                 position > 1 && (--position % 3) == 0)
01517             {
01518               int k;
01519               TRACE("write thousand separator\n");
01520               for (k = 0; thousandSeparator[k]; k++)
01521                 *pBuff++ = thousandSeparator[k];
01522             }
01523           }
01524         }
01525         if (*pToken == FMT_NUM_COPY_ZERO || have_int > 1 ||
01526             (have_int > 0 && *prgbDig > 0))
01527         {
01528           count = min(count_max, have_int);
01529           count_max -= count;
01530           have_int -= count;
01531           TRACE("write %d whole number digits\n", count);
01532           while (count--)
01533           {
01534             dwState |= NUM_WRITE_ON;
01535             *pBuff++ = '0' + *prgbDig++;
01536             if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
01537                 position > 1 && (--position % 3) == 0)
01538             {
01539               int k;
01540               TRACE("write thousand separator\n");
01541               for (k = 0; thousandSeparator[k]; k++)
01542                 *pBuff++ = thousandSeparator[k];
01543             }
01544           }
01545         }
01546         count = min(count_max, pad);
01547         pad -= count;
01548         TRACE("write %d whole trailing 0's\n", count);
01549         while (count--)
01550         {
01551           *pBuff++ = '0';
01552           if ((numHeader->flags & FMT_FLAG_THOUSANDS) &&
01553               position > 1 && (--position % 3) == 0)
01554           {
01555             int k;
01556             TRACE("write thousand separator\n");
01557             for (k = 0; thousandSeparator[k]; k++)
01558               *pBuff++ = thousandSeparator[k];
01559           }
01560         }
01561       }
01562       pToken++;
01563       break;
01564 
01565     default:
01566       ERR("Unknown token 0x%02x!\n", *pToken);
01567       hRes = E_INVALIDARG;
01568       goto VARIANT_FormatNumber_Exit;
01569     }
01570     if (localeValue)
01571     {
01572       if (GetLocaleInfoW(lcid, localeValue, pBuff, 
01573                          sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
01574       {
01575         TRACE("added %s\n", debugstr_w(pBuff));
01576         while (*pBuff)
01577           pBuff++;
01578       }
01579       else
01580       {
01581         TRACE("added %d '%c'\n", defaultChar, defaultChar);
01582         *pBuff++ = defaultChar;
01583       }
01584     }
01585     if (shouldAdvance)
01586       pToken++;
01587   }
01588 
01589 VARIANT_FormatNumber_Exit:
01590   VariantClear(&vString);
01591   *pBuff = '\0';
01592   TRACE("buff is %s\n", debugstr_w(buff));
01593   if (SUCCEEDED(hRes))
01594   {
01595     *pbstrOut = SysAllocString(buff);
01596     if (!*pbstrOut)
01597       hRes = E_OUTOFMEMORY;
01598   }
01599   return hRes;
01600 }
01601 
01602 /* Format a variant using a date format */
01603 static HRESULT VARIANT_FormatDate(LPVARIANT pVarIn, LPOLESTR lpszFormat,
01604                                   LPBYTE rgbTok, ULONG dwFlags,
01605                                   BSTR *pbstrOut, LCID lcid)
01606 {
01607   WCHAR buff[256], *pBuff = buff;
01608   VARIANT vDate;
01609   UDATE udate;
01610   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
01611   FMT_DATE_HEADER *dateHeader;
01612   const BYTE* pToken = NULL;
01613   HRESULT hRes;
01614 
01615   TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn),
01616         debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut,
01617         lcid);
01618 
01619   V_VT(&vDate) = VT_EMPTY;
01620 
01621   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
01622   {
01623     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetNegative(header));
01624     V_DATE(&vDate) = 0;
01625   }
01626   else
01627   {
01628     USHORT usFlags = dwFlags & VARIANT_CALENDAR_HIJRI ? VAR_CALENDAR_HIJRI : 0;
01629 
01630     hRes = VariantChangeTypeEx(&vDate, pVarIn, LCID_US, usFlags, VT_DATE);
01631     if (FAILED(hRes))
01632       return hRes;
01633     dateHeader = (FMT_DATE_HEADER*)(rgbTok + FmtGetPositive(header));
01634   }
01635 
01636   hRes = VarUdateFromDate(V_DATE(&vDate), 0 /* FIXME: flags? */, &udate);
01637   if (FAILED(hRes))
01638     return hRes;
01639   pToken = (const BYTE*)dateHeader + sizeof(FMT_DATE_HEADER);
01640 
01641   while (*pToken != FMT_GEN_END)
01642   {
01643     DWORD dwVal = 0, localeValue = 0, dwFmt = 0;
01644     LPCWSTR szPrintFmt = NULL;
01645     WCHAR defaultChar = '?';
01646 
01647     if (pToken - rgbTok > header->size)
01648     {
01649       ERR("Ran off the end of the format!\n");
01650       hRes = E_INVALIDARG;
01651       goto VARIANT_FormatDate_Exit;
01652     }
01653 
01654     switch (*pToken)
01655     {
01656     case FMT_GEN_COPY:
01657       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
01658       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
01659       pBuff += pToken[2];
01660       pToken += 2;
01661       break;
01662 
01663     case FMT_DATE_TIME_SEP:
01664       TRACE("time separator\n");
01665       localeValue = LOCALE_STIME;
01666       defaultChar = ':';
01667       break;
01668 
01669     case FMT_DATE_DATE_SEP:
01670       TRACE("date separator\n");
01671       localeValue = LOCALE_SDATE;
01672       defaultChar = '/';
01673       break;
01674 
01675     case FMT_DATE_GENERAL:
01676       {
01677         BSTR date = NULL;
01678         WCHAR *pDate;
01679         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, 0, &date);
01680         if (FAILED(hRes))
01681           goto VARIANT_FormatDate_Exit;
01682     pDate = date;
01683         while (*pDate)
01684           *pBuff++ = *pDate++;
01685         SysFreeString(date);
01686       }
01687       break;
01688 
01689     case FMT_DATE_QUARTER:
01690       if (udate.st.wMonth <= 3)
01691         *pBuff++ = '1';
01692       else if (udate.st.wMonth <= 6)
01693         *pBuff++ = '2';
01694       else if (udate.st.wMonth <= 9)
01695         *pBuff++ = '3';
01696       else
01697         *pBuff++ = '4';
01698       break;
01699 
01700     case FMT_DATE_TIME_SYS:
01701       {
01702         /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01703         BSTR date = NULL;
01704         WCHAR *pDate;
01705         hRes = VarBstrFromDate(V_DATE(&vDate), lcid, VAR_TIMEVALUEONLY, &date);
01706         if (FAILED(hRes))
01707           goto VARIANT_FormatDate_Exit;
01708     pDate = date;
01709         while (*pDate)
01710           *pBuff++ = *pDate++;
01711         SysFreeString(date);
01712       }
01713       break;
01714 
01715     case FMT_DATE_DAY:
01716       szPrintFmt = szPercent_d;
01717       dwVal = udate.st.wDay;
01718       break;
01719 
01720     case FMT_DATE_DAY_0:
01721       szPrintFmt = szPercentZeroTwo_d;
01722       dwVal = udate.st.wDay;
01723       break;
01724 
01725     case FMT_DATE_DAY_SHORT:
01726       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01727       TRACE("short day\n");
01728       localeValue = LOCALE_SABBREVDAYNAME1 + udate.st.wMonth - 1;
01729       defaultChar = '?';
01730       break;
01731 
01732     case FMT_DATE_DAY_LONG:
01733       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01734       TRACE("long day\n");
01735       localeValue = LOCALE_SDAYNAME1 + udate.st.wMonth - 1;
01736       defaultChar = '?';
01737       break;
01738 
01739     case FMT_DATE_SHORT:
01740       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01741       dwFmt = LOCALE_SSHORTDATE;
01742       break;
01743 
01744     case FMT_DATE_LONG:
01745       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01746       dwFmt = LOCALE_SLONGDATE;
01747       break;
01748 
01749     case FMT_DATE_MEDIUM:
01750       FIXME("Medium date treated as long date\n");
01751       dwFmt = LOCALE_SLONGDATE;
01752       break;
01753 
01754     case FMT_DATE_DAY_WEEK:
01755       szPrintFmt = szPercent_d;
01756       if (pToken[1])
01757         dwVal = udate.st.wDayOfWeek + 2 - pToken[1];
01758       else
01759       {
01760         GetLocaleInfoW(lcid,LOCALE_RETURN_NUMBER|LOCALE_IFIRSTDAYOFWEEK,
01761                        (LPWSTR)&dwVal, sizeof(dwVal)/sizeof(WCHAR));
01762         dwVal = udate.st.wDayOfWeek + 1 - dwVal;
01763       }
01764       pToken++;
01765       break;
01766 
01767     case FMT_DATE_WEEK_YEAR:
01768       szPrintFmt = szPercent_d;
01769       dwVal = udate.wDayOfYear / 7 + 1;
01770       pToken += 2;
01771       FIXME("Ignoring nFirstDay of %d, nFirstWeek of %d\n", pToken[0], pToken[1]);
01772       break;
01773 
01774     case FMT_DATE_MON:
01775       szPrintFmt = szPercent_d;
01776       dwVal = udate.st.wMonth;
01777       break;
01778 
01779     case FMT_DATE_MON_0:
01780       szPrintFmt = szPercentZeroTwo_d;
01781       dwVal = udate.st.wMonth;
01782       break;
01783 
01784     case FMT_DATE_MON_SHORT:
01785       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01786       TRACE("short month\n");
01787       localeValue = LOCALE_SABBREVMONTHNAME1 + udate.st.wMonth - 1;
01788       defaultChar = '?';
01789       break;
01790 
01791     case FMT_DATE_MON_LONG:
01792       /* FIXME: VARIANT_CALENDAR HIJRI should cause Hijri output */
01793       TRACE("long month\n");
01794       localeValue = LOCALE_SMONTHNAME1 + udate.st.wMonth - 1;
01795       defaultChar = '?';
01796       break;
01797 
01798     case FMT_DATE_YEAR_DOY:
01799       szPrintFmt = szPercent_d;
01800       dwVal = udate.wDayOfYear;
01801       break;
01802 
01803     case FMT_DATE_YEAR_0:
01804       szPrintFmt = szPercentZeroTwo_d;
01805       dwVal = udate.st.wYear % 100;
01806       break;
01807 
01808     case FMT_DATE_YEAR_LONG:
01809       szPrintFmt = szPercent_d;
01810       dwVal = udate.st.wYear;
01811       break;
01812 
01813     case FMT_DATE_MIN:
01814       szPrintFmt = szPercent_d;
01815       dwVal = udate.st.wMinute;
01816       break;
01817 
01818     case FMT_DATE_MIN_0:
01819       szPrintFmt = szPercentZeroTwo_d;
01820       dwVal = udate.st.wMinute;
01821       break;
01822 
01823     case FMT_DATE_SEC:
01824       szPrintFmt = szPercent_d;
01825       dwVal = udate.st.wSecond;
01826       break;
01827 
01828     case FMT_DATE_SEC_0:
01829       szPrintFmt = szPercentZeroTwo_d;
01830       dwVal = udate.st.wSecond;
01831       break;
01832 
01833     case FMT_DATE_HOUR:
01834       szPrintFmt = szPercent_d;
01835       dwVal = udate.st.wHour;
01836       break;
01837 
01838     case FMT_DATE_HOUR_0:
01839       szPrintFmt = szPercentZeroTwo_d;
01840       dwVal = udate.st.wHour;
01841       break;
01842 
01843     case FMT_DATE_HOUR_12:
01844       szPrintFmt = szPercent_d;
01845       dwVal = udate.st.wHour ? udate.st.wHour > 12 ? udate.st.wHour - 12 : udate.st.wHour : 12;
01846       break;
01847 
01848     case FMT_DATE_HOUR_12_0:
01849       szPrintFmt = szPercentZeroTwo_d;
01850       dwVal = udate.st.wHour ? udate.st.wHour > 12 ? udate.st.wHour - 12 : udate.st.wHour : 12;
01851       break;
01852 
01853     case FMT_DATE_AMPM_SYS1:
01854     case FMT_DATE_AMPM_SYS2:
01855       localeValue = udate.st.wHour < 12 ? LOCALE_S1159 : LOCALE_S2359;
01856       defaultChar = '?';
01857       break;
01858 
01859     case FMT_DATE_AMPM_UPPER:
01860       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
01861       *pBuff++ = 'M';
01862       break;
01863 
01864     case FMT_DATE_A_UPPER:
01865       *pBuff++ = udate.st.wHour < 12 ? 'A' : 'P';
01866       break;
01867 
01868     case FMT_DATE_AMPM_LOWER:
01869       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
01870       *pBuff++ = 'm';
01871       break;
01872 
01873     case FMT_DATE_A_LOWER:
01874       *pBuff++ = udate.st.wHour < 12 ? 'a' : 'p';
01875       break;
01876 
01877     default:
01878       ERR("Unknown token 0x%02x!\n", *pToken);
01879       hRes = E_INVALIDARG;
01880       goto VARIANT_FormatDate_Exit;
01881     }
01882     if (localeValue)
01883     {
01884       *pBuff = '\0';
01885       if (GetLocaleInfoW(lcid, localeValue, pBuff,
01886           sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
01887       {
01888         TRACE("added %s\n", debugstr_w(pBuff));
01889         while (*pBuff)
01890           pBuff++;
01891       }
01892       else
01893       {
01894         TRACE("added %d %c\n", defaultChar, defaultChar);
01895         *pBuff++ = defaultChar;
01896       }
01897     }
01898     else if (dwFmt)
01899     {
01900       WCHAR fmt_buff[80];
01901 
01902       if (!GetLocaleInfoW(lcid, dwFmt, fmt_buff, sizeof(fmt_buff)/sizeof(WCHAR)) ||
01903           !GetDateFormatW(lcid, 0, &udate.st, fmt_buff, pBuff,
01904                           sizeof(buff)/sizeof(WCHAR)-(pBuff-buff)))
01905       {
01906         hRes = E_INVALIDARG;
01907         goto VARIANT_FormatDate_Exit;
01908       }
01909       while (*pBuff)
01910         pBuff++;
01911     }
01912     else if (szPrintFmt)
01913     {
01914       sprintfW(pBuff, szPrintFmt, dwVal);
01915       while (*pBuff)
01916         pBuff++;
01917     }
01918     pToken++;
01919   }
01920 
01921 VARIANT_FormatDate_Exit:
01922   *pBuff = '\0';
01923   TRACE("buff is %s\n", debugstr_w(buff));
01924   if (SUCCEEDED(hRes))
01925   {
01926     *pbstrOut = SysAllocString(buff);
01927     if (!*pbstrOut)
01928       hRes = E_OUTOFMEMORY;
01929   }
01930   return hRes;
01931 }
01932 
01933 /* Format a variant using a string format */
01934 static HRESULT VARIANT_FormatString(LPVARIANT pVarIn, LPOLESTR lpszFormat,
01935                                     LPBYTE rgbTok, ULONG dwFlags,
01936                                     BSTR *pbstrOut, LCID lcid)
01937 {
01938   static WCHAR szEmpty[] = { '\0' };
01939   WCHAR buff[256], *pBuff = buff;
01940   WCHAR *pSrc;
01941   FMT_HEADER *header = (FMT_HEADER*)rgbTok;
01942   FMT_STRING_HEADER *strHeader;
01943   const BYTE* pToken = NULL;
01944   VARIANT vStr;
01945   int blanks_first;
01946   BOOL bUpper = FALSE;
01947   HRESULT hRes = S_OK;
01948 
01949   TRACE("(%p->(%s%s),%s,%p,0x%08x,%p,0x%08x)\n", pVarIn, debugstr_VT(pVarIn),
01950         debugstr_VF(pVarIn), debugstr_w(lpszFormat), rgbTok, dwFlags, pbstrOut,
01951         lcid);
01952 
01953   V_VT(&vStr) = VT_EMPTY;
01954 
01955   if (V_TYPE(pVarIn) == VT_EMPTY || V_TYPE(pVarIn) == VT_NULL)
01956   {
01957     strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
01958     V_BSTR(&vStr) = szEmpty;
01959   }
01960   else
01961   {
01962     hRes = VariantChangeTypeEx(&vStr, pVarIn, LCID_US, VARIANT_NOUSEROVERRIDE, VT_BSTR);
01963     if (FAILED(hRes))
01964       return hRes;
01965 
01966     if (V_BSTR(&vStr)[0] == '\0')
01967       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetNegative(header));
01968     else
01969       strHeader = (FMT_STRING_HEADER*)(rgbTok + FmtGetPositive(header));
01970   }
01971   pSrc = V_BSTR(&vStr);
01972   if ((strHeader->flags & (FMT_FLAG_LT|FMT_FLAG_GT)) == FMT_FLAG_GT)
01973     bUpper = TRUE;
01974   blanks_first = strHeader->copy_chars - strlenW(pSrc);
01975   pToken = (const BYTE*)strHeader + sizeof(FMT_DATE_HEADER);
01976 
01977   while (*pToken != FMT_GEN_END)
01978   {
01979     int dwCount = 0;
01980 
01981     if (pToken - rgbTok > header->size)
01982     {
01983       ERR("Ran off the end of the format!\n");
01984       hRes = E_INVALIDARG;
01985       goto VARIANT_FormatString_Exit;
01986     }
01987 
01988     switch (*pToken)
01989     {
01990     case FMT_GEN_COPY:
01991       TRACE("copy %s\n", debugstr_wn(lpszFormat + pToken[1], pToken[2]));
01992       memcpy(pBuff, lpszFormat + pToken[1], pToken[2] * sizeof(WCHAR));
01993       pBuff += pToken[2];
01994       pToken += 2;
01995       break;
01996 
01997     case FMT_STR_COPY_SPACE:
01998     case FMT_STR_COPY_SKIP:
01999       dwCount = pToken[1];
02000       if (*pToken == FMT_STR_COPY_SPACE && blanks_first > 0)
02001       {
02002         TRACE("insert %d initial spaces\n", blanks_first);
02003         while (dwCount > 0 && blanks_first > 0)
02004         {
02005           *pBuff++ = ' ';
02006           dwCount--;
02007           blanks_first--;
02008         }
02009       }
02010       TRACE("copy %d chars%s\n", dwCount,
02011             *pToken == FMT_STR_COPY_SPACE ? " with space" :"");
02012       while (dwCount > 0 && *pSrc)
02013       {
02014         if (bUpper)
02015           *pBuff++ = toupperW(*pSrc);
02016         else
02017           *pBuff++ = tolowerW(*pSrc);
02018         dwCount--;
02019         pSrc++;
02020       }
02021       if (*pToken == FMT_STR_COPY_SPACE && dwCount > 0)
02022       {
02023         TRACE("insert %d spaces\n", dwCount);
02024         while (dwCount-- > 0)
02025           *pBuff++ = ' ';
02026       }
02027       pToken++;
02028       break;
02029 
02030     default:
02031       ERR("Unknown token 0x%02x!\n", *pToken);
02032       hRes = E_INVALIDARG;
02033       goto VARIANT_FormatString_Exit;
02034     }
02035     pToken++;
02036   }
02037 
02038 VARIANT_FormatString_Exit:
02039   /* Copy out any remaining chars */
02040   while (*pSrc)
02041   {
02042     if (bUpper)
02043       *pBuff++ = toupperW(*pSrc);
02044     else
02045       *pBuff++ = tolowerW(*pSrc);
02046     pSrc++;
02047   }
02048   VariantClear(&vStr);
02049   *pBuff = '\0';
02050   TRACE("buff is %s\n", debugstr_w(buff));
02051   if (SUCCEEDED(hRes))
02052   {
02053     *pbstrOut = SysAllocString(buff);
02054     if (!*pbstrOut)
02055       hRes = E_OUTOFMEMORY;
02056   }
02057   return hRes;
02058 }
02059 
02060 #define NUMBER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2| \
02061                        VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8| \
02062                        VTBIT_R4|VTBIT_R8|VTBIT_CY|VTBIT_DECIMAL| \
02063                        VTBIT_BOOL|VTBIT_INT|VTBIT_UINT)
02064 
02065 /**********************************************************************
02066  *              VarFormatFromTokens [OLEAUT32.139]
02067  */
02068 HRESULT WINAPI VarFormatFromTokens(LPVARIANT pVarIn, LPOLESTR lpszFormat,
02069                                    LPBYTE rgbTok, ULONG dwFlags,
02070                                    BSTR *pbstrOut, LCID lcid)
02071 {
02072   FMT_SHORT_HEADER *header = (FMT_SHORT_HEADER *)rgbTok;
02073   VARIANT vTmp;
02074   HRESULT hres;
02075 
02076   TRACE("(%p,%s,%p,%x,%p,0x%08x)\n", pVarIn, debugstr_w(lpszFormat),
02077           rgbTok, dwFlags, pbstrOut, lcid);
02078 
02079   if (!pbstrOut)
02080     return E_INVALIDARG;
02081 
02082   *pbstrOut = NULL;
02083 
02084   if (!pVarIn || !rgbTok)
02085     return E_INVALIDARG;
02086 
02087   if (V_VT(pVarIn) == VT_NULL)
02088     return S_OK;
02089 
02090   if (*rgbTok == FMT_TO_STRING || header->type == FMT_TYPE_GENERAL)
02091   {
02092     /* According to MSDN, general format acts somewhat like the 'Str'
02093      * function in Visual Basic.
02094      */
02095 VarFormatFromTokens_AsStr:
02096     V_VT(&vTmp) = VT_EMPTY;
02097     hres = VariantChangeTypeEx(&vTmp, pVarIn, lcid, dwFlags, VT_BSTR);
02098     *pbstrOut = V_BSTR(&vTmp);
02099   }
02100   else
02101   {
02102     if (header->type == FMT_TYPE_NUMBER ||
02103         (header->type == FMT_TYPE_UNKNOWN && ((1 << V_TYPE(pVarIn)) & NUMBER_VTBITS)))
02104     {
02105       hres = VARIANT_FormatNumber(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
02106     }
02107     else if (header->type == FMT_TYPE_DATE ||
02108              (header->type == FMT_TYPE_UNKNOWN && V_TYPE(pVarIn) == VT_DATE))
02109     {
02110       hres = VARIANT_FormatDate(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
02111     }
02112     else if (header->type == FMT_TYPE_STRING || V_TYPE(pVarIn) == VT_BSTR)
02113     {
02114       hres = VARIANT_FormatString(pVarIn, lpszFormat, rgbTok, dwFlags, pbstrOut, lcid);
02115     }
02116     else
02117     {
02118       ERR("unrecognised format type 0x%02x\n", header->type);
02119       return E_INVALIDARG;
02120     }
02121     /* If the coercion failed, still try to create output, unless the
02122      * VAR_FORMAT_NOSUBSTITUTE flag is set.
02123      */
02124     if ((hres == DISP_E_OVERFLOW || hres == DISP_E_TYPEMISMATCH) &&
02125         !(dwFlags & VAR_FORMAT_NOSUBSTITUTE))
02126       goto VarFormatFromTokens_AsStr;
02127   }
02128 
02129   return hres;
02130 }
02131 
02132 /**********************************************************************
02133  *              VarFormat [OLEAUT32.87]
02134  *
02135  * Format a variant from a format string.
02136  *
02137  * PARAMS
02138  *  pVarIn     [I] Variant to format
02139  *  lpszFormat [I] Format string (see notes)
02140  *  nFirstDay  [I] First day of the week, (See VarTokenizeFormatString() for details)
02141  *  nFirstWeek [I] First week of the year (See VarTokenizeFormatString() for details)
02142  *  dwFlags    [I] Flags for the format (VAR_ flags from "oleauto.h")
02143  *  pbstrOut   [O] Destination for formatted string.
02144  *
02145  * RETURNS
02146  *  Success: S_OK. pbstrOut contains the formatted value.
02147  *  Failure: E_INVALIDARG, if any parameter is invalid.
02148  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02149  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
02150  *
02151  * NOTES
02152  *  - See Variant-Formats for details concerning creating format strings.
02153  *  - This function uses LOCALE_USER_DEFAULT when calling VarTokenizeFormatString()
02154  *    and VarFormatFromTokens().
02155  */
02156 HRESULT WINAPI VarFormat(LPVARIANT pVarIn, LPOLESTR lpszFormat,
02157                          int nFirstDay, int nFirstWeek, ULONG dwFlags,
02158                          BSTR *pbstrOut)
02159 {
02160   BYTE buff[256];
02161   HRESULT hres;
02162 
02163   TRACE("(%p->(%s%s),%s,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn),
02164         debugstr_VF(pVarIn), debugstr_w(lpszFormat), nFirstDay, nFirstWeek,
02165         dwFlags, pbstrOut);
02166 
02167   if (!pbstrOut)
02168     return E_INVALIDARG;
02169   *pbstrOut = NULL;
02170 
02171   hres = VarTokenizeFormatString(lpszFormat, buff, sizeof(buff), nFirstDay,
02172                                  nFirstWeek, LOCALE_USER_DEFAULT, NULL);
02173   if (SUCCEEDED(hres))
02174     hres = VarFormatFromTokens(pVarIn, lpszFormat, buff, dwFlags,
02175                                pbstrOut, LOCALE_USER_DEFAULT);
02176   TRACE("returning 0x%08x, %s\n", hres, debugstr_w(*pbstrOut));
02177   return hres;
02178 }
02179 
02180 /**********************************************************************
02181  *              VarFormatDateTime [OLEAUT32.97]
02182  *
02183  * Format a variant value as a date and/or time.
02184  *
02185  * PARAMS
02186  *  pVarIn    [I] Variant to format
02187  *  nFormat   [I] Format type (see notes)
02188  *  dwFlags   [I] Flags for the format (VAR_ flags from "oleauto.h")
02189  *  pbstrOut  [O] Destination for formatted string.
02190  *
02191  * RETURNS
02192  *  Success: S_OK. pbstrOut contains the formatted value.
02193  *  Failure: E_INVALIDARG, if any parameter is invalid.
02194  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02195  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
02196  *
02197  * NOTES
02198  *  This function uses LOCALE_USER_DEFAULT when determining the date format
02199  *  characters to use.
02200  *  Possible values for the nFormat parameter are:
02201  *| Value  Meaning
02202  *| -----  -------
02203  *|   0    General date format
02204  *|   1    Long date format
02205  *|   2    Short date format
02206  *|   3    Long time format
02207  *|   4    Short time format
02208  */
02209 HRESULT WINAPI VarFormatDateTime(LPVARIANT pVarIn, INT nFormat, ULONG dwFlags, BSTR *pbstrOut)
02210 {
02211   static WCHAR szEmpty[] = { '\0' };
02212   const BYTE* lpFmt = NULL;
02213 
02214   TRACE("(%p->(%s%s),%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn),
02215         debugstr_VF(pVarIn), nFormat, dwFlags, pbstrOut);
02216 
02217   if (!pVarIn || !pbstrOut || nFormat < 0 || nFormat > 4)
02218     return E_INVALIDARG;
02219 
02220   switch (nFormat)
02221   {
02222   case 0: lpFmt = fmtGeneralDate; break;
02223   case 1: lpFmt = fmtLongDate; break;
02224   case 2: lpFmt = fmtShortDate; break;
02225   case 3: lpFmt = fmtLongTime; break;
02226   case 4: lpFmt = fmtShortTime; break;
02227   }
02228   return VarFormatFromTokens(pVarIn, szEmpty, (BYTE*)lpFmt, dwFlags,
02229                               pbstrOut, LOCALE_USER_DEFAULT);
02230 }
02231 
02232 #define GETLOCALENUMBER(type,field) GetLocaleInfoW(LOCALE_USER_DEFAULT, \
02233                                                    type|LOCALE_RETURN_NUMBER, \
02234                                                    (LPWSTR)&numfmt.field, \
02235                                                    sizeof(numfmt.field)/sizeof(WCHAR))
02236 
02237 /**********************************************************************
02238  *              VarFormatNumber [OLEAUT32.107]
02239  *
02240  * Format a variant value as a number.
02241  *
02242  * PARAMS
02243  *  pVarIn    [I] Variant to format
02244  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
02245  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
02246  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
02247  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
02248  *  dwFlags   [I] Currently unused, set to zero
02249  *  pbstrOut  [O] Destination for formatted string.
02250  *
02251  * RETURNS
02252  *  Success: S_OK. pbstrOut contains the formatted value.
02253  *  Failure: E_INVALIDARG, if any parameter is invalid.
02254  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02255  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
02256  *
02257  * NOTES
02258  *  This function uses LOCALE_USER_DEFAULT when determining the number format
02259  *  characters to use.
02260  */
02261 HRESULT WINAPI VarFormatNumber(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
02262                                INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
02263 {
02264   HRESULT hRet;
02265   VARIANT vStr;
02266 
02267   TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn),
02268         debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping, dwFlags, pbstrOut);
02269 
02270   if (!pVarIn || !pbstrOut || nDigits > 9)
02271     return E_INVALIDARG;
02272 
02273   *pbstrOut = NULL;
02274 
02275   V_VT(&vStr) = VT_EMPTY;
02276   hRet = VariantCopyInd(&vStr, pVarIn);
02277 
02278   if (SUCCEEDED(hRet))
02279     hRet = VariantChangeTypeEx(&vStr, &vStr, LCID_US, 0, VT_BSTR);
02280 
02281   if (SUCCEEDED(hRet))
02282   {
02283     WCHAR buff[256], decimal[8], thousands[8];
02284     NUMBERFMTW numfmt;
02285 
02286     /* Although MSDN makes it clear that the native versions of these functions
02287      * are implemented using VarTokenizeFormatString()/VarFormatFromTokens(),
02288      * using NLS gives us the same result.
02289      */
02290     if (nDigits < 0)
02291       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
02292     else
02293       numfmt.NumDigits = nDigits;
02294 
02295     if (nLeading == -2)
02296       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
02297     else if (nLeading == -1)
02298       numfmt.LeadingZero = 1;
02299     else
02300       numfmt.LeadingZero = 0;
02301 
02302     if (nGrouping == -2)
02303     {
02304       WCHAR grouping[16];
02305       grouping[2] = '\0';
02306       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, grouping,
02307                      sizeof(grouping)/sizeof(WCHAR));
02308       numfmt.Grouping = grouping[2] == '2' ? 32 : grouping[0] - '0';
02309     }
02310     else if (nGrouping == -1)
02311       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
02312     else
02313       numfmt.Grouping = 0; /* 0 = No grouping */
02314 
02315     if (nParens == -2)
02316       GETLOCALENUMBER(LOCALE_INEGNUMBER, NegativeOrder);
02317     else if (nParens == -1)
02318       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
02319     else
02320       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
02321 
02322     numfmt.lpDecimalSep = decimal;
02323     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal,
02324                    sizeof(decimal)/sizeof(WCHAR));
02325     numfmt.lpThousandSep = thousands;
02326     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, thousands,
02327                    sizeof(thousands)/sizeof(WCHAR));
02328 
02329     if (GetNumberFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt,
02330                          buff, sizeof(buff)/sizeof(WCHAR)))
02331     {
02332       *pbstrOut = SysAllocString(buff);
02333       if (!*pbstrOut)
02334         hRet = E_OUTOFMEMORY;
02335     }
02336     else
02337       hRet = DISP_E_TYPEMISMATCH;
02338 
02339     SysFreeString(V_BSTR(&vStr));
02340   }
02341   return hRet;
02342 }
02343 
02344 /**********************************************************************
02345  *              VarFormatPercent [OLEAUT32.117]
02346  *
02347  * Format a variant value as a percentage.
02348  *
02349  * PARAMS
02350  *  pVarIn    [I] Variant to format
02351  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
02352  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
02353  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
02354  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
02355  *  dwFlags   [I] Currently unused, set to zero
02356  *  pbstrOut  [O] Destination for formatted string.
02357  *
02358  * RETURNS
02359  *  Success: S_OK. pbstrOut contains the formatted value.
02360  *  Failure: E_INVALIDARG, if any parameter is invalid.
02361  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02362  *           DISP_E_OVERFLOW, if overflow occurs during the conversion.
02363  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
02364  *
02365  * NOTES
02366  *  This function uses LOCALE_USER_DEFAULT when determining the number format
02367  *  characters to use.
02368  */
02369 HRESULT WINAPI VarFormatPercent(LPVARIANT pVarIn, INT nDigits, INT nLeading, INT nParens,
02370                                 INT nGrouping, ULONG dwFlags, BSTR *pbstrOut)
02371 {
02372   static const WCHAR szPercent[] = { '%','\0' };
02373   static const WCHAR szPercentBracket[] = { '%',')','\0' };
02374   WCHAR buff[256];
02375   HRESULT hRet;
02376   VARIANT vDbl;
02377 
02378   TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn),
02379         debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping,
02380         dwFlags, pbstrOut);
02381 
02382   if (!pVarIn || !pbstrOut || nDigits > 9)
02383     return E_INVALIDARG;
02384 
02385   *pbstrOut = NULL;
02386 
02387   V_VT(&vDbl) = VT_EMPTY;
02388   hRet = VariantCopyInd(&vDbl, pVarIn);
02389 
02390   if (SUCCEEDED(hRet))
02391   {
02392     hRet = VariantChangeTypeEx(&vDbl, &vDbl, LOCALE_USER_DEFAULT, 0, VT_R8);
02393 
02394     if (SUCCEEDED(hRet))
02395     {
02396       if (V_R8(&vDbl) > (R8_MAX / 100.0))
02397         return DISP_E_OVERFLOW;
02398 
02399       V_R8(&vDbl) *= 100.0;
02400       hRet = VarFormatNumber(&vDbl, nDigits, nLeading, nParens,
02401                              nGrouping, dwFlags, pbstrOut);
02402 
02403       if (SUCCEEDED(hRet))
02404       {
02405         DWORD dwLen = strlenW(*pbstrOut);
02406         BOOL bBracket = (*pbstrOut)[dwLen] == ')' ? TRUE : FALSE;
02407 
02408         dwLen -= bBracket;
02409         memcpy(buff, *pbstrOut, dwLen * sizeof(WCHAR));
02410         strcpyW(buff + dwLen, bBracket ? szPercentBracket : szPercent);
02411         SysFreeString(*pbstrOut);
02412         *pbstrOut = SysAllocString(buff);
02413         if (!*pbstrOut)
02414           hRet = E_OUTOFMEMORY;
02415       }
02416     }
02417   }
02418   return hRet;
02419 }
02420 
02421 /**********************************************************************
02422  *              VarFormatCurrency [OLEAUT32.127]
02423  *
02424  * Format a variant value as a currency.
02425  *
02426  * PARAMS
02427  *  pVarIn    [I] Variant to format
02428  *  nDigits   [I] Number of digits following the decimal point (-1 = user default)
02429  *  nLeading  [I] Use a leading zero (-2 = user default, -1 = yes, 0 = no)
02430  *  nParens   [I] Use brackets for values < 0 (-2 = user default, -1 = yes, 0 = no)
02431  *  nGrouping [I] Use grouping characters (-2 = user default, -1 = yes, 0 = no)
02432  *  dwFlags   [I] Currently unused, set to zero
02433  *  pbstrOut  [O] Destination for formatted string.
02434  *
02435  * RETURNS
02436  *  Success: S_OK. pbstrOut contains the formatted value.
02437  *  Failure: E_INVALIDARG, if any parameter is invalid.
02438  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02439  *           DISP_E_TYPEMISMATCH, if the variant cannot be formatted.
02440  *
02441  * NOTES
02442  *  This function uses LOCALE_USER_DEFAULT when determining the currency format
02443  *  characters to use.
02444  */
02445 HRESULT WINAPI VarFormatCurrency(LPVARIANT pVarIn, INT nDigits, INT nLeading,
02446                                  INT nParens, INT nGrouping, ULONG dwFlags,
02447                                  BSTR *pbstrOut)
02448 {
02449   HRESULT hRet;
02450   VARIANT vStr;
02451 
02452   TRACE("(%p->(%s%s),%d,%d,%d,%d,0x%08x,%p)\n", pVarIn, debugstr_VT(pVarIn),
02453         debugstr_VF(pVarIn), nDigits, nLeading, nParens, nGrouping, dwFlags, pbstrOut);
02454 
02455   if (!pVarIn || !pbstrOut || nDigits > 9)
02456     return E_INVALIDARG;
02457 
02458   *pbstrOut = NULL;
02459 
02460   V_VT(&vStr) = VT_EMPTY;
02461   hRet = VariantCopyInd(&vStr, pVarIn);
02462 
02463   if (SUCCEEDED(hRet))
02464     hRet = VariantChangeTypeEx(&vStr, &vStr, LOCALE_USER_DEFAULT, 0, VT_BSTR);
02465 
02466   if (SUCCEEDED(hRet))
02467   {
02468     WCHAR buff[256], decimal[8], thousands[8], currency[8];
02469     CURRENCYFMTW numfmt;
02470 
02471     if (nDigits < 0)
02472       GETLOCALENUMBER(LOCALE_IDIGITS, NumDigits);
02473     else
02474       numfmt.NumDigits = nDigits;
02475 
02476     if (nLeading == -2)
02477       GETLOCALENUMBER(LOCALE_ILZERO, LeadingZero);
02478     else if (nLeading == -1)
02479       numfmt.LeadingZero = 1;
02480     else
02481       numfmt.LeadingZero = 0;
02482 
02483     if (nGrouping == -2)
02484     {
02485       WCHAR nGrouping[16];
02486       nGrouping[2] = '\0';
02487       GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, nGrouping,
02488                      sizeof(nGrouping)/sizeof(WCHAR));
02489       numfmt.Grouping = nGrouping[2] == '2' ? 32 : nGrouping[0] - '0';
02490     }
02491     else if (nGrouping == -1)
02492       numfmt.Grouping = 3; /* 3 = "n,nnn.nn" */
02493     else
02494       numfmt.Grouping = 0; /* 0 = No grouping */
02495 
02496     if (nParens == -2)
02497       GETLOCALENUMBER(LOCALE_INEGCURR, NegativeOrder);
02498     else if (nParens == -1)
02499       numfmt.NegativeOrder = 0; /* 0 = "(xxx)" */
02500     else
02501       numfmt.NegativeOrder = 1; /* 1 = "-xxx" */
02502 
02503     GETLOCALENUMBER(LOCALE_ICURRENCY, PositiveOrder);
02504 
02505     numfmt.lpDecimalSep = decimal;
02506     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, decimal,
02507                    sizeof(decimal)/sizeof(WCHAR));
02508     numfmt.lpThousandSep = thousands;
02509     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, thousands,
02510                    sizeof(thousands)/sizeof(WCHAR));
02511     numfmt.lpCurrencySymbol = currency;
02512     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, currency,
02513                    sizeof(currency)/sizeof(WCHAR));
02514 
02515     /* use NLS as per VarFormatNumber() */
02516     if (GetCurrencyFormatW(LOCALE_USER_DEFAULT, 0, V_BSTR(&vStr), &numfmt,
02517                            buff, sizeof(buff)/sizeof(WCHAR)))
02518     {
02519       *pbstrOut = SysAllocString(buff);
02520       if (!*pbstrOut)
02521         hRet = E_OUTOFMEMORY;
02522     }
02523     else
02524       hRet = DISP_E_TYPEMISMATCH;
02525 
02526     SysFreeString(V_BSTR(&vStr));
02527   }
02528   return hRet;
02529 }
02530 
02531 /**********************************************************************
02532  *              VarMonthName [OLEAUT32.129]
02533  *
02534  * Print the specified month as localized name.
02535  *
02536  * PARAMS
02537  *  iMonth    [I] month number 1..12
02538  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
02539  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
02540  *  pbstrOut  [O] Destination for month name
02541  *
02542  * RETURNS
02543  *  Success: S_OK. pbstrOut contains the name.
02544  *  Failure: E_INVALIDARG, if any parameter is invalid.
02545  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02546  */
02547 HRESULT WINAPI VarMonthName(INT iMonth, INT fAbbrev, ULONG dwFlags, BSTR *pbstrOut)
02548 {
02549   DWORD localeValue;
02550   INT size;
02551 
02552   if ((iMonth < 1)  || (iMonth > 12))
02553     return E_INVALIDARG;
02554 
02555   if (dwFlags)
02556     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
02557 
02558   if (fAbbrev)
02559     localeValue = LOCALE_SABBREVMONTHNAME1 + iMonth - 1;
02560   else
02561     localeValue = LOCALE_SMONTHNAME1 + iMonth - 1;
02562 
02563   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, NULL, 0);
02564   if (!size) {
02565     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
02566     return HRESULT_FROM_WIN32(GetLastError());
02567   }
02568   *pbstrOut = SysAllocStringLen(NULL,size - 1);
02569   if (!*pbstrOut)
02570     return E_OUTOFMEMORY;
02571   size = GetLocaleInfoW(LOCALE_USER_DEFAULT,localeValue, *pbstrOut, size);
02572   if (!size) {
02573     ERR("GetLocaleInfo of 0x%x failed in 2nd stage?!\n", localeValue);
02574     SysFreeString(*pbstrOut);
02575     return HRESULT_FROM_WIN32(GetLastError());
02576   }
02577   return S_OK;
02578 }
02579 
02580 /**********************************************************************
02581  *              VarWeekdayName [OLEAUT32.129]
02582  *
02583  * Print the specified weekday as localized name.
02584  *
02585  * PARAMS
02586  *  iWeekday  [I] day of week, 1..7, 1="the first day of the week"
02587  *  fAbbrev   [I] 0 - full name, !0 - abbreviated name
02588  *  iFirstDay [I] first day of week,
02589  *                0=system default, 1=Sunday, 2=Monday, .. (contrary to MSDN)
02590  *  dwFlags   [I] flag stuff. only VAR_CALENDAR_HIJRI possible.
02591  *  pbstrOut  [O] Destination for weekday name.
02592  *
02593  * RETURNS
02594  *  Success: S_OK, pbstrOut contains the name.
02595  *  Failure: E_INVALIDARG, if any parameter is invalid.
02596  *           E_OUTOFMEMORY, if enough memory cannot be allocated.
02597  */
02598 HRESULT WINAPI VarWeekdayName(INT iWeekday, INT fAbbrev, INT iFirstDay,
02599                               ULONG dwFlags, BSTR *pbstrOut)
02600 {
02601   DWORD localeValue;
02602   INT size;
02603 
02604   /* Windows XP oleaut32.dll doesn't allow iWekday==0, contrary to MSDN */
02605   if (iWeekday < 1 || iWeekday > 7)
02606     return E_INVALIDARG;
02607   if (iFirstDay < 0 || iFirstDay > 7)
02608     return E_INVALIDARG;
02609   if (!pbstrOut)
02610     return E_INVALIDARG;
02611 
02612   if (dwFlags)
02613     FIXME("Does not support dwFlags 0x%x, ignoring.\n", dwFlags);
02614 
02615   /* If we have to use the default firstDay, find which one it is */
02616   if (iFirstDay == 0) {
02617     DWORD firstDay;
02618     localeValue = LOCALE_RETURN_NUMBER | LOCALE_IFIRSTDAYOFWEEK;
02619     size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue,
02620                           (LPWSTR)&firstDay, sizeof(firstDay) / sizeof(WCHAR));
02621     if (!size) {
02622       ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
02623       return HRESULT_FROM_WIN32(GetLastError());
02624     }
02625     iFirstDay = firstDay + 2;
02626   }
02627 
02628   /* Determine what we need to return */
02629   localeValue = fAbbrev ? LOCALE_SABBREVDAYNAME1 : LOCALE_SDAYNAME1;
02630   localeValue += (7 + iWeekday - 1 + iFirstDay - 2) % 7;
02631 
02632   /* Determine the size of the data, allocate memory and retrieve the data */
02633   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, NULL, 0);
02634   if (!size) {
02635     ERR("GetLocaleInfo 0x%x failed.\n", localeValue);
02636     return HRESULT_FROM_WIN32(GetLastError());
02637   }
02638   *pbstrOut = SysAllocStringLen(NULL, size - 1);
02639   if (!*pbstrOut)
02640     return E_OUTOFMEMORY;
02641   size = GetLocaleInfoW(LOCALE_USER_DEFAULT, localeValue, *pbstrOut, size);
02642   if (!size) {
02643     ERR("GetLocaleInfo 0x%x failed in 2nd stage?!\n", localeValue);
02644     SysFreeString(*pbstrOut);
02645     return HRESULT_FROM_WIN32(GetLastError());
02646   }
02647   return S_OK;
02648 }

Generated on Sun May 27 2012 04:25:49 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.