Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenvarformat.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
1.7.6.1
|