diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index 4bc3024f14a..a636c8abba4 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -27,8 +27,6 @@ * - The Variant APIs do not support international languages, currency * types, number formating and calendar. They only support U.S. English format. * - The Variant APIs do not the following types: IUknown, IDispatch, DECIMAL and SafeArray. - * The prototypes for these are commented out in the oleauto.h file. They need - * to be implemented and cases need to be added to the switches of the existing APIs. * - The parsing of date for the VarDateFromStr is not complete. * - The date manipulations do not support dates prior to 1900. * - The parsing does not accept as many formats as the Windows implementation. @@ -55,39 +53,22 @@ #include "winreg.h" #include "heap.h" #include "wine/debug.h" +#include "wine/unicode.h" #include "winerror.h" #include "parsedt.h" #include "typelib.h" #include "winternl.h" +#include "variant.h" WINE_DEFAULT_DEBUG_CHANNEL(ole); #define SYSDUPSTRING(str) SysAllocStringByteLen((LPCSTR)(str), SysStringByteLen(str)) -#ifndef FLT_MAX -# ifdef MAXFLOAT -# define FLT_MAX MAXFLOAT -# else -# error "Can't find #define for MAXFLOAT/FLT_MAX" -# endif -#endif +/* Flags set in V_VT, other than the actual type value */ +#define VT_EXTRA_TYPE (VT_VECTOR|VT_ARRAY|VT_BYREF|VT_RESERVED) -#undef CHAR_MAX -#undef CHAR_MIN -static const char CHAR_MAX = 127; -static const char CHAR_MIN = -128; -static const BYTE UI1_MAX = 255; -static const BYTE UI1_MIN = 0; -static const unsigned short UI2_MAX = 65535; -static const unsigned short UI2_MIN = 0; -static const short I2_MAX = 32767; -static const short I2_MIN = -32768; -static const unsigned long UI4_MAX = 4294967295U; -static const unsigned long UI4_MIN = 0; -static const long I4_MAX = 2147483647; -static const long I4_MIN = -(2147483648U); -static const DATE DATE_MIN = -657434; -static const DATE DATE_MAX = 2958465; +/* Get the extra flags from a variant pointer */ +#define V_EXTRA_TYPE(v) (V_VT(v) & VT_EXTRA_TYPE) /* the largest valid type */ @@ -675,7 +656,7 @@ static BSTR StringDupAtoBstr( char* strIn ) RtlCreateUnicodeStringFromAsciiz( &usBuffer, strIn ); pNewString = usBuffer.Buffer; - + bstr = SysAllocString( pNewString ); RtlFreeUnicodeString( &usBuffer ); return bstr; @@ -746,272 +727,6 @@ static double round( double d ) return roundedValue * nSign; } -/****************************************************************************** - * RemoveCharacterFromString [INTERNAL] - * - * Removes any of the characters in "strOfCharToRemove" from the "str" argument. - */ -static void RemoveCharacterFromString( LPSTR str, LPSTR strOfCharToRemove ) -{ - LPSTR pNewString = NULL; - LPSTR strToken = NULL; - - /* Check if we have a valid argument - */ - if( str != NULL ) - { - pNewString = strdup( str ); - str[0] = '\0'; - strToken = strtok( pNewString, strOfCharToRemove ); - while( strToken != NULL ) { - strcat( str, strToken ); - strToken = strtok( NULL, strOfCharToRemove ); - } - free( pNewString ); - } - return; -} - -/****************************************************************************** - * GetValidRealString [INTERNAL] - * - * Checks if the string is of proper format to be converted to a real value. - */ -static BOOL IsValidRealString( LPSTR strRealString ) -{ - /* Real values that have a decimal point are required to either have - * digits before or after the decimal point. We will assume that - * we do not have any digits at either position. If we do encounter - * some we will disable this flag. - */ - BOOL bDigitsRequired = TRUE; - /* Processed fields in the string representation of the real number. - */ - BOOL bWhiteSpaceProcessed = FALSE; - BOOL bFirstSignProcessed = FALSE; - BOOL bFirstDigitsProcessed = FALSE; - BOOL bDecimalPointProcessed = FALSE; - BOOL bSecondDigitsProcessed = FALSE; - BOOL bExponentProcessed = FALSE; - BOOL bSecondSignProcessed = FALSE; - BOOL bThirdDigitsProcessed = FALSE; - /* Assume string parameter "strRealString" is valid and try to disprove it. - */ - BOOL bValidRealString = TRUE; - - /* Used to count the number of tokens in the "strRealString". - */ - LPSTR strToken = NULL; - int nTokens = 0; - LPSTR pChar = NULL; - - /* Check if we have a valid argument - */ - if( strRealString == NULL ) - { - bValidRealString = FALSE; - } - - if( bValidRealString == TRUE ) - { - /* Make sure we only have ONE token in the string. - */ - strToken = strtok( strRealString, " " ); - while( strToken != NULL ) { - nTokens++; - strToken = strtok( NULL, " " ); - } - - if( nTokens != 1 ) - { - bValidRealString = FALSE; - } - } - - - /* Make sure this token contains only valid characters. - * The string argument to atof has the following form: - * [whitespace] [sign] [digits] [.digits] [ {d | D | e | E }[sign]digits] - * Whitespace consists of space and|or characters, which are ignored. - * Sign is either plus '+' or minus '-'. - * Digits are one or more decimal digits. - * Note: If no digits appear before the decimal point, at least one must - * appear after the decimal point. - * The decimal digits may be followed by an exponent. - * An Exponent consists of an introductory letter ( D, d, E, or e) and - * an optionally signed decimal integer. - */ - pChar = strRealString; - while( bValidRealString == TRUE && *pChar != '\0' ) - { - switch( *pChar ) - { - /* If whitespace... - */ - case ' ': - case '\t': - if( bWhiteSpaceProcessed || - bFirstSignProcessed || - bFirstDigitsProcessed || - bDecimalPointProcessed || - bSecondDigitsProcessed || - bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed ) - { - bValidRealString = FALSE; - } - break; - /* If sign... - */ - case '+': - case '-': - if( bFirstSignProcessed == FALSE ) - { - if( bFirstDigitsProcessed || - bDecimalPointProcessed || - bSecondDigitsProcessed || - bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed ) - { - bValidRealString = FALSE; - } - bWhiteSpaceProcessed = TRUE; - bFirstSignProcessed = TRUE; - } - else if( bSecondSignProcessed == FALSE ) - { - /* Note: The exponent must be present in - * order to accept the second sign... - */ - if( bExponentProcessed == FALSE || - bThirdDigitsProcessed || - bDigitsRequired ) - { - bValidRealString = FALSE; - } - bFirstSignProcessed = TRUE; - bWhiteSpaceProcessed = TRUE; - bFirstDigitsProcessed = TRUE; - bDecimalPointProcessed = TRUE; - bSecondDigitsProcessed = TRUE; - bSecondSignProcessed = TRUE; - } - break; - - /* If decimals... - */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if( bFirstDigitsProcessed == FALSE ) - { - if( bDecimalPointProcessed || - bSecondDigitsProcessed || - bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed ) - { - bValidRealString = FALSE; - } - bFirstSignProcessed = TRUE; - bWhiteSpaceProcessed = TRUE; - /* We have found some digits before the decimal point - * so disable the "Digits required" flag. - */ - bDigitsRequired = FALSE; - } - else if( bSecondDigitsProcessed == FALSE ) - { - if( bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed ) - { - bValidRealString = FALSE; - } - bFirstSignProcessed = TRUE; - bWhiteSpaceProcessed = TRUE; - bFirstDigitsProcessed = TRUE; - bDecimalPointProcessed = TRUE; - /* We have found some digits after the decimal point - * so disable the "Digits required" flag. - */ - bDigitsRequired = FALSE; - } - else if( bThirdDigitsProcessed == FALSE ) - { - /* Getting here means everything else should be processed. - * If we get anything else than a decimal following this - * digit it will be flagged by the other cases, so - * we do not really need to do anything in here. - */ - } - break; - /* If DecimalPoint... - */ - case '.': - if( bDecimalPointProcessed || - bSecondDigitsProcessed || - bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed ) - { - bValidRealString = FALSE; - } - bFirstSignProcessed = TRUE; - bWhiteSpaceProcessed = TRUE; - bFirstDigitsProcessed = TRUE; - bDecimalPointProcessed = TRUE; - break; - /* If Exponent... - */ - case 'e': - case 'E': - case 'd': - case 'D': - if( bExponentProcessed || - bSecondSignProcessed || - bThirdDigitsProcessed || - bDigitsRequired ) - { - bValidRealString = FALSE; - } - bFirstSignProcessed = TRUE; - bWhiteSpaceProcessed = TRUE; - bFirstDigitsProcessed = TRUE; - bDecimalPointProcessed = TRUE; - bSecondDigitsProcessed = TRUE; - bExponentProcessed = TRUE; - break; - default: - bValidRealString = FALSE; - break; - } - /* Process next character. - */ - pChar++; - } - - /* If the required digits were not present we have an invalid - * string representation of a real number. - */ - if( bDigitsRequired == TRUE ) - { - bValidRealString = FALSE; - } - - return bValidRealString; -} - - /****************************************************************************** * Coerce [INTERNAL] * @@ -1795,7 +1510,7 @@ static HRESULT Coerce( VARIANTARG* pd, LCID lcid, ULONG dwFlags, VARIANTARG* ps, * Used internally by the hi-level Variant API to determine * if the vartypes are valid. */ -static HRESULT WINAPI ValidateVtRange( VARTYPE vt ) +static HRESULT ValidateVtRange( VARTYPE vt ) { /* if by value we must make sure it is in the * range of the valid types. @@ -1807,6 +1522,70 @@ static HRESULT WINAPI ValidateVtRange( VARTYPE vt ) return S_OK; } +/* Copy data from one variant to another. */ +static void VARIANT_CopyData(const VARIANT *srcVar, VARTYPE vt, void *pOut) +{ + switch(vt) + { + case VT_I1: + case VT_UI1: memcpy(pOut, &V_UI1(srcVar), sizeof(BYTE)); break; + case VT_BOOL: + case VT_I2: + case VT_UI2: memcpy(pOut, &V_UI2(srcVar), sizeof(SHORT)); break; + case VT_R4: + case VT_I4: + case VT_UI4: memcpy(pOut, &V_UI4(srcVar), sizeof (LONG)); break; + case VT_R8: + case VT_DATE: + case VT_CY: + case VT_I8: + case VT_UI8: memcpy(pOut, &V_UI8(srcVar), sizeof (LONG64)); break; + case VT_DECIMAL: memcpy(pOut, &V_DECIMAL(srcVar), sizeof (DECIMAL)); break; + default: + FIXME("VT_ type %d unhandled, please report!\n", vt); + } +} + +/* Coerce VT_DISPATCH to another type */ +HRESULT VARIANT_FromDisp(IDispatch* pdispIn, LCID lcid, void* pOut, VARTYPE vt) +{ + VARIANTARG srcVar, dstVar; + HRESULT hRet; + + V_VT(&srcVar) = VT_DISPATCH; + V_DISPATCH(&srcVar) = pdispIn; + + hRet = VariantChangeTypeEx(&dstVar, &srcVar, lcid, 0, vt); + + if (SUCCEEDED(hRet)) + VARIANT_CopyData(&dstVar, vt, pOut); + return hRet; +} + +/* Coerce VT_BSTR to a numeric type */ +HRESULT VARIANT_NumberFromBstr(OLECHAR* pStrIn, LCID lcid, ULONG ulFlags, + void* pOut, VARTYPE vt) +{ + VARIANTARG dstVar; + HRESULT hRet; + NUMPARSE np; + BYTE rgb[1024]; + + /* Use VarParseNumFromStr/VarNumFromParseNum as MSDN indicates */ + np.cDig = sizeof(rgb) / sizeof(BYTE); + np.dwInFlags = NUMPRS_STD; + + hRet = VarParseNumFromStr(pStrIn, lcid, ulFlags, &np, rgb); + + if (SUCCEEDED(hRet)) + { + /* 1 << vt gives us the VTBIT constant for the destination number type */ + hRet = VarNumFromParseNum(&np, rgb, 1 << vt, &dstVar); + if (SUCCEEDED(hRet)) + VARIANT_CopyData(&dstVar, vt, pOut); + } + return hRet; +} /****************************************************************************** * ValidateVartype [INTERNAL] @@ -1814,7 +1593,7 @@ static HRESULT WINAPI ValidateVtRange( VARTYPE vt ) * Used internally by the hi-level Variant API to determine * if the vartypes are valid. */ -static HRESULT WINAPI ValidateVariantType( VARTYPE vt ) +static HRESULT ValidateVariantType( VARTYPE vt ) { HRESULT res = S_OK; @@ -1847,7 +1626,7 @@ static HRESULT WINAPI ValidateVariantType( VARTYPE vt ) * Used internally by the hi-level Variant API to determine * if the vartypes are valid. */ -static HRESULT WINAPI ValidateVt( VARTYPE vt ) +static HRESULT ValidateVt( VARTYPE vt ) { HRESULT res = S_OK; @@ -1874,92 +1653,126 @@ static HRESULT WINAPI ValidateVt( VARTYPE vt ) return res; } +/****************************************************************************** + * Check if a variants type is valid. + */ +static inline HRESULT VARIANT_ValidateType(VARTYPE vt) +{ + VARTYPE vtExtra = vt & VT_EXTRA_TYPE; + vt &= VT_TYPEMASK; - + if (!(vtExtra & (VT_VECTOR|VT_RESERVED))) + { + if (vt < VT_VOID || vt == VT_RECORD || vt == VT_CLSID) + { + if ((vtExtra & (VT_BYREF|VT_ARRAY)) && vt <= VT_NULL) + return DISP_E_BADVARTYPE; + if (vt != (VARTYPE)15) + return S_OK; + } + } + return DISP_E_BADVARTYPE; +} /****************************************************************************** * VariantInit [OLEAUT32.8] * - * Initializes the Variant. Unlike VariantClear it does not interpret - * the current contents of the Variant. + * Initialise a variant. + * + * PARAMS + * pVarg [O] Variant to initialise + * + * RETURNS + * Nothing. + * + * NOTES + * This function simply sets the type of the variant to VT_EMPTY. It does not + * free any existing value, use VariantClear() for that. */ -void WINAPI VariantInit(VARIANTARG* pvarg) +void WINAPI VariantInit(VARIANTARG* pVarg) { - TRACE("(%p)\n",pvarg); + TRACE("(%p)\n", pVarg); - memset(pvarg, 0, sizeof (VARIANTARG)); - V_VT(pvarg) = VT_EMPTY; - - return; + V_VT(pVarg) = VT_EMPTY; /* Native doesn't set any other fields */ } /****************************************************************************** * VariantClear [OLEAUT32.9] * - * This function clears the VARIANT by setting the vt field to VT_EMPTY. It also - * sets the wReservedX field to 0. The current contents of the VARIANT are - * freed. If the vt is VT_BSTR the string is freed. If VT_DISPATCH the object is - * released. If VT_ARRAY the array is freed. + * Clear a variant. + * + * PARAMS + * pVarg [I/O] Variant to clear + * + * RETURNS + * Success: S_OK. Any previous value in pVarg is freed and its type is set to VT_EMPTY. + * Failure: DISP_E_BADVARTYPE, if the variant is a not a valid variant type. */ -HRESULT WINAPI VariantClear(VARIANTARG* pvarg) +HRESULT WINAPI VariantClear(VARIANTARG* pVarg) { - HRESULT res = S_OK; - TRACE("(%p)\n",pvarg); + HRESULT hres = S_OK; - res = ValidateVariantType( V_VT(pvarg) ); - if( res == S_OK ) + TRACE("(%p)\n", pVarg); + + hres = VARIANT_ValidateType(V_VT(pVarg)); + + if (SUCCEEDED(hres)) { - if( !( V_VT(pvarg) & VT_BYREF ) ) + if (!V_ISBYREF(pVarg)) { - /* - * The VT_ARRAY flag is a special case of a safe array. - */ - if ( (V_VT(pvarg) & VT_ARRAY) != 0) + if (V_ISARRAY(pVarg) || V_VT(pVarg) == VT_SAFEARRAY) { - SafeArrayDestroy(V_UNION(pvarg,parray)); + if (V_ARRAY(pVarg)) + hres = SafeArrayDestroy(V_ARRAY(pVarg)); } - else + else if (V_VT(pVarg) == VT_BSTR) { - switch( V_VT(pvarg) & VT_TYPEMASK ) - { - case( VT_BSTR ): - SysFreeString( V_UNION(pvarg,bstrVal) ); - break; - case( VT_DISPATCH ): - if(V_UNION(pvarg,pdispVal)!=NULL) - IDispatch_Release(V_UNION(pvarg,pdispVal)); - break; - case( VT_VARIANT ): - VariantClear(V_UNION(pvarg,pvarVal)); - break; - case( VT_UNKNOWN ): - if(V_UNION(pvarg,punkVal)!=NULL) - IUnknown_Release(V_UNION(pvarg,punkVal)); - break; - case( VT_SAFEARRAY ): - SafeArrayDestroy(V_UNION(pvarg,parray)); - break; - default: - break; - } + if (V_BSTR(pVarg)) + SysFreeString(V_BSTR(pVarg)); + } + else if (V_VT(pVarg) == VT_RECORD) + { + struct __tagBRECORD* pBr = &V_UNION(pVarg,brecVal); + if (pBr->pRecInfo) + { + IRecordInfo_RecordClear(pBr->pRecInfo, pBr->pvRecord); + IRecordInfo_Release(pBr->pRecInfo); + } + } + else if (V_VT(pVarg) == VT_DISPATCH || + V_VT(pVarg) == VT_UNKNOWN) + { + if (V_UNKNOWN(pVarg)) + IUnknown_Release(V_UNKNOWN(pVarg)); + } + else if (V_VT(pVarg) == VT_VARIANT) + { + if (V_VARIANTREF(pVarg)) + VariantClear(V_VARIANTREF(pVarg)); } } - - /* - * Empty all the fields and mark the type as empty. - */ - memset(pvarg, 0, sizeof (VARIANTARG)); - V_VT(pvarg) = VT_EMPTY; + V_VT(pVarg) = VT_EMPTY; } - - return res; + return hres; } /****************************************************************************** * VariantCopy [OLEAUT32.10] * - * Frees up the designation variant and makes a copy of the source. + * Copy a variant. + * + * PARAMS + * pvargDest [O] Destination for copy + * pvargSrc [I] Source variant to copy + * + * RETURNS + * Success: S_OK. pvargDest contains a copy of pvargSrc. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * pvargDest is always freed, and may be equal to pvargSrc. + * If pvargSrc is by-reference, pvargDest is by-reference also. */ HRESULT WINAPI VariantCopy(VARIANTARG* pvargDest, VARIANTARG* pvargSrc) { @@ -2039,8 +1852,20 @@ HRESULT WINAPI VariantCopy(VARIANTARG* pvargDest, VARIANTARG* pvargSrc) /****************************************************************************** * VariantCopyInd [OLEAUT32.11] * - * Frees up the destination variant and makes a copy of the source. If - * the source is of type VT_BYREF it performs the necessary indirections. + * + * Copy a variant, dereferencing if it is by-reference. + * + * PARAMS + * pvargDest [O] Destination for copy + * pvargSrc [I] Source variant to copy + * + * RETURNS + * Success: S_OK. pvargDest contains a copy of pvargSrc. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * pvargDest is always freed, and may be equal to pvargSrc. + * If pvargSrc is not by-reference, this function acts as VariantCopy(). */ HRESULT WINAPI VariantCopyInd(VARIANT* pvargDest, VARIANTARG* pvargSrc) { @@ -2217,6 +2042,22 @@ coerce_array( /****************************************************************************** * VariantChangeType [OLEAUT32.12] + * + * Change the type of a variant. + * + * PARAMS + * pvargDest [O] Destination for the converted variant + * pvargSrc [O] Source variant to change the type of + * wFlags [I] VARIANT_ flags from "oleauto.h" + * vt [I] Variant type to change pvargSrc into + * + * RETURNS + * Success: S_OK. pvargDest contains the converted value. + * Failure: An HRESULT error code describing the failure. + * + * NOTES + * The LCID used for the conversion is LOCALE_USER_DEFAULT. + * See VariantChangeTypeEx. */ HRESULT WINAPI VariantChangeType(VARIANTARG* pvargDest, VARIANTARG* pvargSrc, USHORT wFlags, VARTYPE vt) @@ -2226,6 +2067,23 @@ HRESULT WINAPI VariantChangeType(VARIANTARG* pvargDest, VARIANTARG* pvargSrc, /****************************************************************************** * VariantChangeTypeEx [OLEAUT32.147] + * + * Change the type of a variant. + * + * PARAMS + * pvargDest [O] Destination for the converted variant + * pvargSrc [O] Source variant to change the type of + * lcid [I] LCID for the conversion + * wFlags [I] VARIANT_ flags from "oleauto.h" + * vt [I] Variant type to change pvargSrc into + * + * RETURNS + * Success: S_OK. pvargDest contains the converted value. + * Failure: An HRESULT error code describing the failure. + * + * NOTES + * pvargDest and pvargSrc can point to the same variant to perform an in-place + * conversion. If the conversion is successful, pvargSrc will be freed. */ HRESULT WINAPI VariantChangeTypeEx(VARIANTARG* pvargDest, VARIANTARG* pvargSrc, LCID lcid, USHORT wFlags, VARTYPE vt) @@ -2271,7 +2129,7 @@ HRESULT WINAPI VariantChangeTypeEx(VARIANTARG* pvargDest, VARIANTARG* pvargSrc, /* Convert the source variant to a "byvalue" variant. */ VARIANTARG Variant; - + if ((V_VT(pvargSrc) & 0xf000) != VT_BYREF) { FIXME("VT_TYPEMASK %x is unhandled.\n",V_VT(pvargSrc) & VT_TYPEMASK); return E_FAIL; @@ -2490,39 +2348,8 @@ HRESULT WINAPI VarUI1FromUI4(ULONG ulIn, BYTE* pbOut) */ HRESULT WINAPI VarUI1FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, BYTE* pbOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, 0x%08lx, 0x%08lx, %p ), stub\n", strIn, lcid, dwFlags, pbOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0 , pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < UI1_MIN || dValue > UI1_MAX ) - { - return DISP_E_OVERFLOW; - } - - *pbOut = (BYTE) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pbOut); + return _VarUI1FromStr(strIn, lcid, dwFlags, pbOut); } /********************************************************************** @@ -2696,39 +2523,8 @@ HRESULT WINAPI VarI2FromUI4(ULONG ulIn, short* psOut) */ HRESULT WINAPI VarI2FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, short* psOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %s, 0x%08lx, 0x%08lx, %p ), stub\n", debugstr_w(strIn), lcid, dwFlags, psOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < I2_MIN || dValue > I2_MAX ) - { - return DISP_E_OVERFLOW; - } - - *psOut = (short) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, psOut); + return _VarI2FromStr(strIn, lcid, dwFlags, psOut); } /********************************************************************** @@ -2889,39 +2685,8 @@ HRESULT WINAPI VarI4FromI2(short sIn, LONG* plOut) */ HRESULT WINAPI VarI4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, LONG* plOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, 0x%08lx, 0x%08lx, %p ), stub\n", strIn, lcid, dwFlags, plOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < I4_MIN || dValue > I4_MAX ) - { - return DISP_E_OVERFLOW; - } - - *plOut = (LONG) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, plOut); + return _VarI4FromStr(strIn, lcid, dwFlags, plOut); } /********************************************************************** @@ -2982,7 +2747,7 @@ HRESULT WINAPI VarR4FromR8(double dblIn, FLOAT* pfltOut) /* Check range of value. */ - if( dblIn < -(FLT_MAX) || dblIn > FLT_MAX ) + if( dblIn < -(R4_MAX) || dblIn > R4_MAX ) { return DISP_E_OVERFLOW; } @@ -3001,7 +2766,7 @@ HRESULT WINAPI VarR4FromDate(DATE dateIn, FLOAT* pfltOut) /* Check range of value. */ - if( dateIn < -(FLT_MAX) || dateIn > FLT_MAX ) + if( dateIn < -(R4_MAX) || dateIn > R4_MAX ) { return DISP_E_OVERFLOW; } @@ -3064,38 +2829,8 @@ HRESULT WINAPI VarR4FromUI4(ULONG ulIn, FLOAT* pfltOut) */ HRESULT WINAPI VarR4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, FLOAT* pfltOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, %ld, %ld, %p ), stub\n", strIn, lcid, dwFlags, pfltOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - if( dValue < -(FLT_MAX) || dValue > FLT_MAX ) - { - return DISP_E_OVERFLOW; - } - - *pfltOut = (FLOAT) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pfltOut); + return _VarR4FromStr(strIn, lcid, dwFlags, pfltOut); } /********************************************************************** @@ -3221,31 +2956,8 @@ HRESULT WINAPI VarR8FromUI4(ULONG ulIn, double* pdblOut) */ HRESULT WINAPI VarR8FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, double* pdblOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - TRACE("( %s, %ld, %ld, %p ), stub\n", pNewString, lcid, dwFlags, pdblOut ); - - /* Check if we have a valid argument - */ - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - *pdblOut = dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pdblOut); + return _VarR8FromStr(strIn, lcid, dwFlags, pdblOut); } /********************************************************************** @@ -3737,25 +3449,24 @@ HRESULT WINAPI VarBoolFromDate(DATE dateIn, VARIANT_BOOL* pboolOut) */ HRESULT WINAPI VarBoolFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, VARIANT_BOOL* pboolOut) { + static const WCHAR szTrue[] = { 'T','r','u','e','\0' }; + static const WCHAR szFalse[] = { 'F','a','l','s','e','\0' }; HRESULT ret = S_OK; - char* pNewString = NULL; TRACE("( %p, %ld, %ld, %p ), stub\n", strIn, lcid, dwFlags, pboolOut ); - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - - if( pNewString == NULL || strlen( pNewString ) == 0 ) + if( strIn == NULL || strlenW( strIn ) == 0 ) { ret = DISP_E_TYPEMISMATCH; } if( ret == S_OK ) { - if( strncasecmp( pNewString, "True", strlen( pNewString ) ) == 0 ) + if( strcmpiW( (LPCWSTR)strIn, szTrue ) == 0 ) { *pboolOut = VARIANT_TRUE; } - else if( strncasecmp( pNewString, "False", strlen( pNewString ) ) == 0 ) + else if( strcmpiW( (LPCWSTR)strIn, szFalse ) == 0 ) { *pboolOut = VARIANT_FALSE; } @@ -3775,8 +3486,6 @@ HRESULT WINAPI VarBoolFromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, VARIANT_ } } - HeapFree( GetProcessHeap(), 0, pNewString ); - return ret; } @@ -3836,7 +3545,7 @@ HRESULT WINAPI VarI1FromUI1(BYTE bIn, signed char *pcOut) /* Check range of value. */ - if( bIn > CHAR_MAX ) + if( bIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3853,7 +3562,7 @@ HRESULT WINAPI VarI1FromI2(short uiIn, signed char *pcOut) { TRACE("( %d, %p ), stub\n", uiIn, pcOut ); - if( uiIn > CHAR_MAX ) + if( uiIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3870,7 +3579,7 @@ HRESULT WINAPI VarI1FromI4(LONG lIn, signed char *pcOut) { TRACE("( %ld, %p ), stub\n", lIn, pcOut ); - if( lIn < CHAR_MIN || lIn > CHAR_MAX ) + if( lIn < I1_MIN || lIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3888,7 +3597,7 @@ HRESULT WINAPI VarI1FromR4(FLOAT fltIn, signed char *pcOut) TRACE("( %f, %p ), stub\n", fltIn, pcOut ); fltIn = round( fltIn ); - if( fltIn < CHAR_MIN || fltIn > CHAR_MAX ) + if( fltIn < I1_MIN || fltIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3906,7 +3615,7 @@ HRESULT WINAPI VarI1FromR8(double dblIn, signed char *pcOut) TRACE("( %f, %p ), stub\n", dblIn, pcOut ); dblIn = round( dblIn ); - if( dblIn < CHAR_MIN || dblIn > CHAR_MAX ) + if( dblIn < I1_MIN || dblIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3924,7 +3633,7 @@ HRESULT WINAPI VarI1FromDate(DATE dateIn, signed char *pcOut) TRACE("( %f, %p ), stub\n", dateIn, pcOut ); dateIn = round( dateIn ); - if( dateIn < CHAR_MIN || dateIn > CHAR_MAX ) + if( dateIn < I1_MIN || dateIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -3939,39 +3648,8 @@ HRESULT WINAPI VarI1FromDate(DATE dateIn, signed char *pcOut) */ HRESULT WINAPI VarI1FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, signed char *pcOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, %ld, %ld, %p ), stub\n", strIn, lcid, dwFlags, pcOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < CHAR_MIN || dValue > CHAR_MAX ) - { - return DISP_E_OVERFLOW; - } - - *pcOut = (CHAR) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pcOut); + return _VarI1FromStr(strIn, lcid, dwFlags, pcOut); } /****************************************************************************** @@ -3993,7 +3671,7 @@ HRESULT WINAPI VarI1FromUI2(USHORT uiIn, signed char *pcOut) { TRACE("( %d, %p ), stub\n", uiIn, pcOut ); - if( uiIn > CHAR_MAX ) + if( uiIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -4010,7 +3688,7 @@ HRESULT WINAPI VarI1FromUI4(ULONG ulIn, signed char *pcOut) { TRACE("( %ld, %p ), stub\n", ulIn, pcOut ); - if( ulIn > CHAR_MAX ) + if( ulIn > I1_MAX ) { return DISP_E_OVERFLOW; } @@ -4027,7 +3705,7 @@ HRESULT WINAPI VarI1FromUI4(ULONG ulIn, signed char *pcOut) HRESULT WINAPI VarI1FromCy(CY cyIn, signed char *pcOut) { double t = round((((double)cyIn.s.Hi * 4294967296.0) + (double)cyIn.s.Lo) / 10000); - if (t > CHAR_MAX || t < CHAR_MIN) return DISP_E_OVERFLOW; + if (t > I1_MAX || t < I1_MIN) return DISP_E_OVERFLOW; *pcOut = (CHAR)t; return S_OK; @@ -4138,39 +3816,8 @@ HRESULT WINAPI VarUI2FromDate(DATE dateIn, USHORT* puiOut) */ HRESULT WINAPI VarUI2FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, USHORT* puiOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, %ld, %ld, %p ), stub\n", strIn, lcid, dwFlags, puiOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < UI2_MIN || dValue > UI2_MAX ) - { - return DISP_E_OVERFLOW; - } - - *puiOut = (USHORT) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, puiOut); + return _VarUI2FromStr(strIn, lcid, dwFlags, puiOut); } /****************************************************************************** @@ -4219,39 +3866,8 @@ HRESULT WINAPI VarUI2FromUI4(ULONG ulIn, USHORT* puiOut) */ HRESULT WINAPI VarUI4FromStr(OLECHAR* strIn, LCID lcid, ULONG dwFlags, ULONG* pulOut) { - double dValue = 0.0; - LPSTR pNewString = NULL; - - TRACE("( %p, %ld, %ld, %p ), stub\n", strIn, lcid, dwFlags, pulOut ); - - /* Check if we have a valid argument - */ - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - RemoveCharacterFromString( pNewString, "," ); - if( IsValidRealString( pNewString ) == FALSE ) - { - return DISP_E_TYPEMISMATCH; - } - - /* Convert the valid string to a floating point number. - */ - dValue = atof( pNewString ); - - /* We don't need the string anymore so free it. - */ - HeapFree( GetProcessHeap(), 0, pNewString ); - - /* Check range of value. - */ - dValue = round( dValue ); - if( dValue < UI4_MIN || dValue > UI4_MAX ) - { - return DISP_E_OVERFLOW; - } - - *pulOut = (ULONG) dValue; - - return S_OK; + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pulOut); + return _VarUI4FromStr(strIn, lcid, dwFlags, pulOut); } /********************************************************************** @@ -4495,54 +4111,10 @@ HRESULT WINAPI VarCyFromDate(DATE dateIn, CY* pcyOut) { * VarCyFromStr [OLEAUT32.104] * FIXME: Never tested with decimal separator other than '.' */ -HRESULT WINAPI VarCyFromStr(OLECHAR *strIn, LCID lcid, ULONG dwFlags, CY *pcyOut) { - - LPSTR pNewString = NULL; - char *decSep = NULL; - char *strPtr,*curPtr = NULL; - int size, rc; - double currencyVal = 0.0; - - - pNewString = HEAP_strdupWtoA( GetProcessHeap(), 0, strIn ); - TRACE("( '%s', 0x%08lx, 0x%08lx, %p )\n", pNewString, lcid, dwFlags, pcyOut ); - - /* Get locale information - Decimal Separator (size includes 0x00) */ - size = GetLocaleInfoA(lcid, LOCALE_SDECIMAL, NULL, 0); - decSep = (char *) malloc(size); - rc = GetLocaleInfoA(lcid, LOCALE_SDECIMAL, decSep, size); - TRACE("Decimal Separator is '%s'\n", decSep); - - /* Now copy to temporary buffer, skipping any character except 0-9 and - the decimal separator */ - curPtr = pBuffer; /* Current position in string being built */ - strPtr = pNewString; /* Current position in supplied currenct string */ - - while (*strPtr) { - /* If decimal separator, skip it and put '.' in string */ - if (strncmp(strPtr, decSep, (size-1)) == 0) { - strPtr = strPtr + (size-1); - *curPtr = '.'; - curPtr++; - } else if ((*strPtr == '+' || *strPtr == '-') || - (*strPtr >= '0' && *strPtr <= '9')) { - *curPtr = *strPtr; - strPtr++; - curPtr++; - } else strPtr++; - } - *curPtr = 0x00; - - /* Try to get currency into a double */ - currencyVal = atof(pBuffer); - TRACE("Converted string '%s' to %f\n", pBuffer, currencyVal); - - /* Free allocated storage */ - HeapFree( GetProcessHeap(), 0, pNewString ); - free(decSep); - - /* Convert double -> currency using internal routine */ - return VarCyFromR8(currencyVal, pcyOut); +HRESULT WINAPI VarCyFromStr(OLECHAR *strIn, LCID lcid, ULONG dwFlags, CY *pcyOut) +{ + TRACE("(%s, 0x%08lx, 0x%08lx, %p)\n", debugstr_w(strIn), lcid, dwFlags, pcyOut); + return _VarCyFromStr(strIn, lcid, dwFlags, pcyOut); } @@ -4662,101 +4234,762 @@ INT WINAPI DosDateTimeToVariantTime(USHORT wDosDate, USHORT wDosTime, return TmToDATE( &t, pvtime ); } +#define GET_NUMBER_TEXT(fld,name) \ + buff[0] = 0; \ + if (!GetLocaleInfoW(lcid, lctype|fld, buff, sizeof(WCHAR) * 2)) \ + WARN("buffer too small for " #fld "\n"); \ + else \ + if (buff[0]) lpChars->name = buff[0]; \ + TRACE("lcid 0x%lx, " #name "=%d '%c'\n", lcid, lpChars->name, lpChars->name) + +/* Get the valid number characters for an lcid */ +void VARIANT_GetLocalisedNumberChars(VARIANT_NUMBER_CHARS *lpChars, LCID lcid, DWORD dwFlags) +{ + static const VARIANT_NUMBER_CHARS defaultChars = { '-','+','.',',','$',0,'.',',' }; + LCTYPE lctype = 0; + WCHAR buff[4]; + + if (dwFlags & VARIANT_NOUSEROVERRIDE) + lctype |= LOCALE_NOUSEROVERRIDE; + + memcpy(lpChars, &defaultChars, sizeof(defaultChars)); + GET_NUMBER_TEXT(LOCALE_SNEGATIVESIGN, cNegativeSymbol); + GET_NUMBER_TEXT(LOCALE_SPOSITIVESIGN, cPositiveSymbol); + GET_NUMBER_TEXT(LOCALE_SDECIMAL, cDecimalPoint); + GET_NUMBER_TEXT(LOCALE_STHOUSAND, cDigitSeperator); + GET_NUMBER_TEXT(LOCALE_SMONDECIMALSEP, cCurrencyDecimalPoint); + GET_NUMBER_TEXT(LOCALE_SMONTHOUSANDSEP, cCurrencyDigitSeperator); + + /* Local currency symbols are often 2 characters */ + lpChars->cCurrencyLocal2 = '\0'; + switch(GetLocaleInfoW(lcid, lctype|LOCALE_SCURRENCY, buff, sizeof(WCHAR) * 4)) + { + case 3: lpChars->cCurrencyLocal2 = buff[1]; /* Fall through */ + case 2: lpChars->cCurrencyLocal = buff[0]; + break; + default: WARN("buffer too small for LOCALE_SCURRENCY\n"); + } + TRACE("lcid 0x%lx, cCurrencyLocal =%d,%d '%c','%c'\n", lcid, lpChars->cCurrencyLocal, + lpChars->cCurrencyLocal2, lpChars->cCurrencyLocal, lpChars->cCurrencyLocal2); +} + +/* Number Parsing States */ +#define B_PROCESSING_EXPONENT 0x1 +#define B_NEGATIVE_EXPONENT 0x2 +#define B_EXPONENT_START 0x4 +#define B_INEXACT_ZEROS 0x8 +#define B_LEADING_ZERO 0x10 /********************************************************************** * VarParseNumFromStr [OLEAUT32.46] + * + * Parse a string containing a number into a NUMPARSE structure. + * + * PARAMS + * lpszStr [I] String to parse number from + * lcid [I] Locale Id for the conversion + * dwFlags [I] Apparently not used + * pNumprs [I/O] Destination for parsed number + * rgbDig [O] Destination for digits read in + * + * RETURNS + * Success: S_OK. pNumprs and rgbDig contain the parsed representation of + * the number. + * Failure: E_INVALIDARG, if any parameter is invalid. + * DISP_E_TYPEMISMATCH, if the string is not a number or is formatted + * incorrectly. + * DISP_E_OVERFLOW, if rgbDig is too small to hold the number. + * + * NOTES + * pNumprs must have the following fields set: + * cDig: Set to the size of rgbDig. + * dwInFlags: Set to the allowable syntax of the number using NUMPRS_ flags + * from "oleauto.h". + * + * FIXME + * - I am unsure if this function should parse non-arabic (e.g. Thai) + * numerals, so this has not been implemented. */ -HRESULT WINAPI VarParseNumFromStr(OLECHAR * strIn, LCID lcid, ULONG dwFlags, - NUMPARSE * pnumprs, BYTE * rgbDig) +HRESULT WINAPI VarParseNumFromStr(OLECHAR *lpszStr, LCID lcid, ULONG dwFlags, + NUMPARSE *pNumprs, BYTE *rgbDig) { - int i,lastent=0; - int cDig; - BOOL foundNum=FALSE; + VARIANT_NUMBER_CHARS chars; + BYTE rgbTmp[1024]; + DWORD dwState = B_EXPONENT_START|B_INEXACT_ZEROS; + int iMaxDigits = sizeof(rgbTmp) / sizeof(BYTE); + int cchUsed = 0; - FIXME("(%s,flags=%lx,....), partial stub!\n",debugstr_w(strIn),dwFlags); - FIXME("numparse: cDig=%d, InFlags=%lx\n",pnumprs->cDig,pnumprs->dwInFlags); + TRACE("(%s,%ld,%ld,%p,%p)\n", debugstr_w(lpszStr), lcid, dwFlags, pNumprs, rgbDig); - /* The other struct components are to be set by us */ - memset(rgbDig,0,pnumprs->cDig); + if (pNumprs->dwInFlags & NUMPRS_HEX_OCT) + FIXME("dwInFlags & NUMPRS_HEX_OCT not yet implemented!\n"); - /* FIXME: Just patching some values in */ - pnumprs->nPwr10 = 0; - pnumprs->nBaseShift = 0; - pnumprs->cchUsed = lastent; - pnumprs->dwOutFlags = NUMPRS_DECIMAL; + if (!pNumprs || !rgbDig) + return E_INVALIDARG; - cDig = 0; - for (i=0; strIn[i] ;i++) { - if ((strIn[i]>='0') && (strIn[i]<='9')) { - foundNum = TRUE; - if (pnumprs->cDig > cDig) { - *(rgbDig++)=strIn[i]-'0'; - cDig++; - lastent = i; - } - } else if ((strIn[i]=='-') && (foundNum==FALSE)) { - pnumprs->dwOutFlags |= NUMPRS_NEG; - } + if (pNumprs->cDig < iMaxDigits) + iMaxDigits = pNumprs->cDig; + + pNumprs->cDig = 0; + pNumprs->dwOutFlags = 0; + pNumprs->cchUsed = 0; + pNumprs->nBaseShift = 0; + pNumprs->nPwr10 = 0; + + if (!lpszStr) + return DISP_E_TYPEMISMATCH; + + VARIANT_GetLocalisedNumberChars(&chars, lcid, dwFlags); + + /* First consume all the leading symbols and space from the string */ + while (1) + { + if (pNumprs->dwInFlags & NUMPRS_LEADING_WHITE && isspaceW(*lpszStr)) + { + pNumprs->dwOutFlags |= NUMPRS_LEADING_WHITE; + do + { + cchUsed++; + lpszStr++; + } while (isspaceW(*lpszStr)); } - pnumprs->cDig = cDig; - TRACE("numparse out: cDig=%d, OutFlags=%lx\n",pnumprs->cDig,pnumprs->dwOutFlags); - return S_OK; + else if (pNumprs->dwInFlags & NUMPRS_LEADING_PLUS && + *lpszStr == chars.cPositiveSymbol && + !(pNumprs->dwOutFlags & NUMPRS_LEADING_PLUS)) + { + pNumprs->dwOutFlags |= NUMPRS_LEADING_PLUS; + cchUsed++; + lpszStr++; + } + else if (pNumprs->dwInFlags & NUMPRS_LEADING_MINUS && + *lpszStr == chars.cNegativeSymbol && + !(pNumprs->dwOutFlags & NUMPRS_LEADING_MINUS)) + { + pNumprs->dwOutFlags |= (NUMPRS_LEADING_MINUS|NUMPRS_NEG); + cchUsed++; + lpszStr++; + } + else if (pNumprs->dwInFlags & NUMPRS_CURRENCY && + !(pNumprs->dwOutFlags & NUMPRS_CURRENCY) && + *lpszStr == chars.cCurrencyLocal && + (!chars.cCurrencyLocal2 || lpszStr[1] == chars.cCurrencyLocal2)) + { + pNumprs->dwOutFlags |= NUMPRS_CURRENCY; + cchUsed++; + lpszStr++; + /* Only accept currency characters */ + chars.cDecimalPoint = chars.cCurrencyDecimalPoint; + chars.cDigitSeperator = chars.cCurrencyDigitSeperator; + } + else if (pNumprs->dwInFlags & NUMPRS_PARENS && *lpszStr == '(' && + !(pNumprs->dwOutFlags & NUMPRS_PARENS)) + { + pNumprs->dwOutFlags |= NUMPRS_PARENS; + cchUsed++; + lpszStr++; + } + else + break; + } + + if (!(pNumprs->dwOutFlags & NUMPRS_CURRENCY)) + { + /* Only accept non-currency characters */ + chars.cCurrencyDecimalPoint = chars.cDecimalPoint; + chars.cCurrencyDigitSeperator = chars.cDigitSeperator; + } + + /* Strip Leading zeros */ + while (*lpszStr == '0') + { + dwState |= B_LEADING_ZERO; + cchUsed++; + lpszStr++; + } + + while (*lpszStr) + { + if (isdigitW(*lpszStr)) + { + if (dwState & B_PROCESSING_EXPONENT) + { + int exponentSize = 0; + if (dwState & B_EXPONENT_START) + { + while (*lpszStr == '0') + { + /* Skip leading zero's in the exponent */ + cchUsed++; + lpszStr++; + } + if (!isdigitW(*lpszStr)) + break; /* No exponent digits - invalid */ + } + + while (isdigitW(*lpszStr)) + { + exponentSize *= 10; + exponentSize += *lpszStr - '0'; + cchUsed++; + lpszStr++; + } + if (dwState & B_NEGATIVE_EXPONENT) + exponentSize = -exponentSize; + /* Add the exponent into the powers of 10 */ + pNumprs->nPwr10 += exponentSize; + dwState &= ~(B_PROCESSING_EXPONENT|B_EXPONENT_START); + lpszStr--; /* back up to allow processing of next char */ + } + else + { + if (pNumprs->cDig >= iMaxDigits) + { + pNumprs->dwOutFlags |= NUMPRS_INEXACT; + + if (*lpszStr != '0') + dwState &= ~B_INEXACT_ZEROS; /* Inexact number with non-trailing zeros */ + + /* This digit can't be represented, but count it in nPwr10 */ + if (pNumprs->dwOutFlags & NUMPRS_DECIMAL) + pNumprs->nPwr10--; + else + pNumprs->nPwr10++; + } + else + { + if (pNumprs->dwOutFlags & NUMPRS_DECIMAL) + pNumprs->nPwr10--; /* Count decimal points in nPwr10 */ + rgbTmp[pNumprs->cDig] = *lpszStr - '0'; + } + pNumprs->cDig++; + cchUsed++; + } + } + else if (*lpszStr == chars.cDigitSeperator && pNumprs->dwInFlags & NUMPRS_THOUSANDS) + { + pNumprs->dwOutFlags |= NUMPRS_THOUSANDS; + cchUsed++; + } + else if (*lpszStr == chars.cDecimalPoint && + pNumprs->dwInFlags & NUMPRS_DECIMAL && + !(pNumprs->dwOutFlags & (NUMPRS_DECIMAL|NUMPRS_EXPONENT))) + { + pNumprs->dwOutFlags |= NUMPRS_DECIMAL; + cchUsed++; + + /* Remove trailing zeros from the whole number part */ + while (pNumprs->cDig > 1 && !rgbTmp[pNumprs->cDig - 1]) + { + pNumprs->nPwr10++; + pNumprs->cDig--; + } + + /* If we have no digits so far, skip leading zeros */ + if (!pNumprs->cDig) + { + while (lpszStr[1] == '0') + { + dwState |= B_LEADING_ZERO; + cchUsed++; + lpszStr++; + } + } + } + else if ((*lpszStr == 'e' || *lpszStr == 'E') && + pNumprs->dwInFlags & NUMPRS_EXPONENT && + !(pNumprs->dwOutFlags & NUMPRS_EXPONENT)) + { + dwState |= B_PROCESSING_EXPONENT; + pNumprs->dwOutFlags |= NUMPRS_EXPONENT; + cchUsed++; + } + else if (dwState & B_PROCESSING_EXPONENT && *lpszStr == chars.cPositiveSymbol) + { + cchUsed++; /* Ignore positive exponent */ + } + else if (dwState & B_PROCESSING_EXPONENT && *lpszStr == chars.cNegativeSymbol) + { + dwState |= B_NEGATIVE_EXPONENT; + cchUsed++; + } + else + break; /* Stop at an unrecognised character */ + + lpszStr++; + } + + if (!pNumprs->cDig && dwState & B_LEADING_ZERO) + { + /* Ensure a 0 on its own gets stored */ + pNumprs->cDig = 1; + rgbTmp[0] = 0; + } + + if (pNumprs->dwOutFlags & NUMPRS_EXPONENT && dwState & B_PROCESSING_EXPONENT) + { + pNumprs->cchUsed = cchUsed; + return DISP_E_TYPEMISMATCH; /* Failed to completely parse the exponent */ + } + + if (pNumprs->dwOutFlags & NUMPRS_INEXACT) + { + if (dwState & B_INEXACT_ZEROS) + pNumprs->dwOutFlags &= ~NUMPRS_INEXACT; /* All zeros doesn't set NUMPRS_INEXACT */ + } + else + { + /* Remove trailing zeros from the last (whole number or decimal) part */ + while (pNumprs->cDig > 1 && !rgbTmp[pNumprs->cDig - 1]) + { + if (pNumprs->dwOutFlags & NUMPRS_DECIMAL) + pNumprs->nPwr10--; + else + pNumprs->nPwr10++; + pNumprs->cDig--; + } + } + + if (pNumprs->cDig <= iMaxDigits) + pNumprs->dwOutFlags &= ~NUMPRS_INEXACT; /* Ignore stripped zeros for NUMPRS_INEXACT */ + else + pNumprs->cDig = iMaxDigits; /* Only return iMaxDigits worth of digits */ + + /* Copy the digits we processed into rgbDig */ + memcpy(rgbDig, rgbTmp, pNumprs->cDig * sizeof(BYTE)); + + /* Consume any trailing symbols and space */ + while (1) + { + if ((pNumprs->dwInFlags & NUMPRS_TRAILING_WHITE) && isspaceW(*lpszStr)) + { + pNumprs->dwOutFlags |= NUMPRS_TRAILING_WHITE; + do + { + cchUsed++; + lpszStr++; + } while (isspaceW(*lpszStr)); + } + else if (pNumprs->dwInFlags & NUMPRS_TRAILING_PLUS && + !(pNumprs->dwOutFlags & NUMPRS_LEADING_PLUS) && + *lpszStr == chars.cPositiveSymbol) + { + pNumprs->dwOutFlags |= NUMPRS_TRAILING_PLUS; + cchUsed++; + lpszStr++; + } + else if (pNumprs->dwInFlags & NUMPRS_TRAILING_MINUS && + !(pNumprs->dwOutFlags & NUMPRS_LEADING_MINUS) && + *lpszStr == chars.cNegativeSymbol) + { + pNumprs->dwOutFlags |= (NUMPRS_TRAILING_MINUS|NUMPRS_NEG); + cchUsed++; + lpszStr++; + } + else if (pNumprs->dwInFlags & NUMPRS_PARENS && *lpszStr == ')' && + pNumprs->dwOutFlags & NUMPRS_PARENS) + { + cchUsed++; + lpszStr++; + pNumprs->dwOutFlags |= NUMPRS_NEG; + } + else + break; + } + + if (pNumprs->dwOutFlags & NUMPRS_PARENS && !(pNumprs->dwOutFlags & NUMPRS_NEG)) + { + pNumprs->cchUsed = cchUsed; + return DISP_E_TYPEMISMATCH; /* Opening parenthesis not matched */ + } + + if (pNumprs->dwInFlags & NUMPRS_USE_ALL && *lpszStr != '\0') + return DISP_E_TYPEMISMATCH; /* Not all chars were consumed */ + + if (!pNumprs->cDig) + return DISP_E_TYPEMISMATCH; /* No Number found */ + + pNumprs->cchUsed = cchUsed; + return S_OK; } +/* VTBIT flags indicating an integer value */ +#define INTEGER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2|VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8) +/* VTBIT flags indicating a real number value */ +#define REAL_VTBITS (VTBIT_R4|VTBIT_R8|VTBIT_CY|VTBIT_DECIMAL) /********************************************************************** * VarNumFromParseNum [OLEAUT32.47] + * + * Convert a NUMPARSE structure into a numeric Variant type. + * + * PARAMS + * pNumprs [I] Source for parsed number. cDig must be set to the size of rgbDig + * rgbDig [I] Source for the numbers digits + * dwVtBits [I] VTBIT_ flags from "oleauto.h" indicating the acceptable dest types + * pVarDst [O] Destination for the converted Variant value. + * + * RETURNS + * Success: S_OK. pVarDst contains the converted value. + * Failure: E_INVALIDARG, if any parameter is invalid. + * DISP_E_OVERFLOW, if the number is too big for the types set in dwVtBits. + * + * NOTES + * - The smallest favoured type present in dwVtBits that can represent the + * number in pNumprs without losing precision is used. + * - Signed types are preferrred over unsigned types of the same size. + * - Preferred types in order are: integer, float, double, currency then decimal. + * - Rounding (dropping of decimal points) occurs without error. See VarI8FromR8() + * for details of the rounding method. + * - pVarDst is not cleared before the result is stored in it. */ -HRESULT WINAPI VarNumFromParseNum(NUMPARSE * pnumprs, BYTE * rgbDig, - ULONG dwVtBits, VARIANT * pvar) +HRESULT WINAPI VarNumFromParseNum(NUMPARSE *pNumprs, BYTE *rgbDig, + ULONG dwVtBits, VARIANT *pVarDst) { - DWORD xint; + /* Scale factors and limits for double arithmatic */ + static const double dblMultipliers[11] = { + 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, + 1000000.0, 10000000.0, 100000000.0, 1000000000.0, 10000000000.0 + }; + static const double dblMinimums[11] = { + R8_MIN, R8_MIN*10.0, R8_MIN*100.0, R8_MIN*1000.0, R8_MIN*10000.0, + R8_MIN*100000.0, R8_MIN*1000000.0, R8_MIN*10000000.0, + R8_MIN*100000000.0, R8_MIN*1000000000.0, R8_MIN*10000000000.0 + }; + static const double dblMaximums[11] = { + R8_MAX, R8_MAX/10.0, R8_MAX/100.0, R8_MAX/1000.0, R8_MAX/10000.0, + R8_MAX/100000.0, R8_MAX/1000000.0, R8_MAX/10000000.0, + R8_MAX/100000000.0, R8_MAX/1000000000.0, R8_MAX/10000000000.0 + }; + + int wholeNumberDigits, fractionalDigits, divisor10 = 0, multiplier10 = 0; + + TRACE("(%p,%p,0x%lx,%p)\n", pNumprs, rgbDig, dwVtBits, pVarDst); + + if (pNumprs->nBaseShift) + { + /* nBaseShift indicates a hex or octal number */ + FIXME("nBaseShift=%d not yet implemented, returning overflow\n", pNumprs->nBaseShift); + return DISP_E_OVERFLOW; + } + + /* Count the number of relevant fractional and whole digits stored, + * And compute the divisor/multiplier to scale the number by. + */ + if (pNumprs->nPwr10 < 0) + { + if (-pNumprs->nPwr10 >= pNumprs->cDig) + { + /* A real number < +/- 1.0 e.g. 0.1024 or 0.01024 */ + wholeNumberDigits = 0; + fractionalDigits = pNumprs->cDig; + divisor10 = -pNumprs->nPwr10; + } + else + { + /* An exactly represented real number e.g. 1.024 */ + wholeNumberDigits = pNumprs->cDig + pNumprs->nPwr10; + fractionalDigits = pNumprs->cDig - wholeNumberDigits; + divisor10 = pNumprs->cDig - wholeNumberDigits; + } + } + else if (pNumprs->nPwr10 == 0) + { + /* An exactly represented whole number e.g. 1024 */ + wholeNumberDigits = pNumprs->cDig; + fractionalDigits = 0; + } + else /* pNumprs->nPwr10 > 0 */ + { + /* A whole number followed by nPwr10 0's e.g. 102400 */ + wholeNumberDigits = pNumprs->cDig; + fractionalDigits = 0; + multiplier10 = pNumprs->nPwr10; + } + + TRACE("cDig %d; nPwr10 %d, whole %d, frac %d ", pNumprs->cDig, + pNumprs->nPwr10, wholeNumberDigits, fractionalDigits); + TRACE("mult %d; div %d\n", multiplier10, divisor10); + + if (dwVtBits & INTEGER_VTBITS && + (!fractionalDigits || !(dwVtBits & (REAL_VTBITS|VTBIT_CY|VTBIT_DECIMAL)))) + { + /* We have one or more integer output choices, and either: + * 1) An integer input value, or + * 2) A real number input value but no floating output choices. + * So, place the integer value into pVarDst, using the smallest type + * possible and preferring signed over unsigned types. + */ + BOOL bOverflow = FALSE, bNegative; + ULONG64 ul64 = 0; int i; - FIXME("(..,dwVtBits=%lx,....), partial stub!\n",dwVtBits); - xint = 0; - for (i=0;icDig;i++) - xint = xint*10 + rgbDig[i]; - - if (pnumprs->dwOutFlags & NUMPRS_NEG) { - xint = xint * -1; + /* Convert the integer part of the number into a UI8 */ + for (i = 0; i < wholeNumberDigits; i++) + { + if (ul64 > (UI8_MAX / 10 - rgbDig[i])) + { + TRACE("Overflow multiplying digits\n"); + bOverflow = TRUE; + break; + } + ul64 = ul64 * 10 + rgbDig[i]; } - VariantInit(pvar); - if (dwVtBits & VTBIT_I4) { - V_VT(pvar) = VT_I4; - V_UNION(pvar,intVal) = xint; - return S_OK; + /* Account for the scale of the number */ + if (!bOverflow && multiplier10) + { + for (i = 0; i < multiplier10; i++) + { + if (ul64 > (UI8_MAX / 10)) + { + TRACE("Overflow scaling number\n"); + bOverflow = TRUE; + break; + } + ul64 = ul64 * 10; + } } - if (dwVtBits & VTBIT_R8) { - V_VT(pvar) = VT_R8; - V_UNION(pvar,dblVal) = xint; - return S_OK; + + /* If we have any fractional digits, round the value. + * Note we dont have to do this if divisor10 is < 1, + * because this means the fractional part must be < 0.5 + */ + if (!bOverflow && fractionalDigits && divisor10 > 0) + { + const BYTE* fracDig = rgbDig + wholeNumberDigits; + BOOL bAdjust = FALSE; + + TRACE("first decimal value is %d\n", *fracDig); + + if (*fracDig > 5) + bAdjust = TRUE; /* > 0.5 */ + else if (*fracDig == 5) + { + for (i = 1; i < fractionalDigits; i++) + { + if (fracDig[i]) + { + bAdjust = TRUE; /* > 0.5 */ + break; + } + } + /* If exactly 0.5, round only odd values */ + if (i == fractionalDigits && (ul64 & 1)) + bAdjust = TRUE; + } + + if (bAdjust) + { + if (ul64 == UI8_MAX) + { + TRACE("Overflow after rounding\n"); + bOverflow = TRUE; + } + ul64++; + } } - if (dwVtBits & VTBIT_R4) { - V_VT(pvar) = VT_R4; - V_UNION(pvar,fltVal) = xint; - return S_OK; + + /* Zero is not a negative number */ + bNegative = pNumprs->dwOutFlags & NUMPRS_NEG && ul64 ? TRUE : FALSE; + + TRACE("Integer value is %lld, bNeg %d\n", ul64, bNegative); + + /* For negative integers, try the signed types in size order */ + if (!bOverflow && bNegative) + { + if (dwVtBits & (VTBIT_I1|VTBIT_I2|VTBIT_I4|VTBIT_I8)) + { + if (dwVtBits & VTBIT_I1 && ul64 <= -I1_MIN) + { + V_VT(pVarDst) = VT_I1; + V_I1(pVarDst) = -ul64; + return S_OK; + } + else if (dwVtBits & VTBIT_I2 && ul64 <= -I2_MIN) + { + V_VT(pVarDst) = VT_I2; + V_I2(pVarDst) = -ul64; + return S_OK; + } + else if (dwVtBits & VTBIT_I4 && ul64 <= -((LONGLONG)I4_MIN)) + { + V_VT(pVarDst) = VT_I4; + V_I4(pVarDst) = -ul64; + return S_OK; + } + else if (dwVtBits & VTBIT_I8 && ul64 <= (ULONGLONG)I8_MAX + 1) + { + V_VT(pVarDst) = VT_I8; + V_I8(pVarDst) = -ul64; + return S_OK; + } + } } - if (dwVtBits & VTBIT_I2) { - V_VT(pvar) = VT_I2; - V_UNION(pvar,iVal) = xint; + else if (!bOverflow) + { + /* For positive integers, try signed then unsigned types in size order */ + if (dwVtBits & VTBIT_I1 && ul64 <= I1_MAX) + { + V_VT(pVarDst) = VT_I1; + V_I1(pVarDst) = ul64; return S_OK; + } + if (dwVtBits & VTBIT_UI1 && ul64 <= UI1_MAX) + { + V_VT(pVarDst) = VT_UI1; + V_UI1(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_I2 && ul64 <= I2_MAX) + { + V_VT(pVarDst) = VT_I2; + V_I2(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_UI2 && ul64 <= UI2_MAX) + { + V_VT(pVarDst) = VT_UI2; + V_UI2(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_I4 && ul64 <= I4_MAX) + { + V_VT(pVarDst) = VT_I4; + V_I4(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_UI4 && ul64 <= UI4_MAX) + { + V_VT(pVarDst) = VT_UI4; + V_UI4(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_I8 && ul64 <= I8_MAX) + { + V_VT(pVarDst) = VT_I8; + V_I8(pVarDst) = ul64; + return S_OK; + } + if (dwVtBits & VTBIT_UI8) + { + V_VT(pVarDst) = VT_UI8; + V_UI8(pVarDst) = ul64; + return S_OK; + } } - /* FIXME: Currency should be from a double */ - if (dwVtBits & VTBIT_CY) { - V_VT(pvar) = VT_CY; - TRACE("Calculated currency is xint=%ld\n", xint); - VarCyFromInt( (int) xint, &V_UNION(pvar,cyVal) ); - TRACE("Calculated cy is %ld,%lu\n", V_UNION(pvar,cyVal).s.Hi, V_UNION(pvar,cyVal).s.Lo); - return VarCyFromInt( (int) xint, &V_UNION(pvar,cyVal) ); + } + + if (dwVtBits & REAL_VTBITS) + { + /* Try to put the number into a float or real */ + BOOL bOverflow = FALSE, bNegative = pNumprs->dwOutFlags & NUMPRS_NEG; + double whole = 0.0; + int i; + + /* Convert the number into a double */ + for (i = 0; i < pNumprs->cDig; i++) + whole = whole * 10.0 + rgbDig[i]; + + TRACE("Whole double value is %16.16g\n", whole); + + /* Account for the scale */ + while (multiplier10 > 10) + { + if (whole > dblMaximums[10]) + { + dwVtBits &= ~(VTBIT_R4|VTBIT_R8|VTBIT_CY); + bOverflow = TRUE; + break; + } + whole = whole * dblMultipliers[10]; + multiplier10 -= 10; + } + if (multiplier10) + { + if (whole > dblMaximums[multiplier10]) + { + dwVtBits &= ~(VTBIT_R4|VTBIT_R8|VTBIT_CY); + bOverflow = TRUE; + } + else + whole = whole * dblMultipliers[multiplier10]; } - FIXME("vtbitmask is unsupported %lx, int=%d\n",dwVtBits, (int) xint); - return E_FAIL; + TRACE("Scaled double value is %16.16g\n", whole); + + while (divisor10 > 10) + { + if (whole < dblMinimums[10]) + { + dwVtBits &= ~(VTBIT_R4|VTBIT_R8|VTBIT_CY); /* Underflow */ + bOverflow = TRUE; + break; + } + whole = whole / dblMultipliers[10]; + divisor10 -= 10; + } + if (divisor10) + { + if (whole < dblMinimums[divisor10]) + { + dwVtBits &= ~(VTBIT_R4|VTBIT_R8|VTBIT_CY); /* Underflow */ + bOverflow = TRUE; + } + else + whole = whole / dblMultipliers[divisor10]; + } + if (!bOverflow) + TRACE("Final double value is %16.16g\n", whole); + + if (dwVtBits & VTBIT_R4 && + ((whole <= R4_MAX && whole >= R4_MIN) || whole == 0.0)) + { + TRACE("Set R4 to final value\n"); + V_VT(pVarDst) = VT_R4; /* Fits into a float */ + V_R4(pVarDst) = pNumprs->dwOutFlags & NUMPRS_NEG ? -whole : whole; + return S_OK; + } + + if (dwVtBits & VTBIT_R8) + { + TRACE("Set R8 to final value\n"); + V_VT(pVarDst) = VT_R8; /* Fits into a double */ + V_R8(pVarDst) = pNumprs->dwOutFlags & NUMPRS_NEG ? -whole : whole; + return S_OK; + } + + if (dwVtBits & VTBIT_CY) + { + if (SUCCEEDED(VarCyFromR8(bNegative ? -whole : whole, &V_CY(pVarDst)))) + { + V_VT(pVarDst) = VT_CY; /* Fits into a currency */ + TRACE("Set CY to final value\n"); + return S_OK; + } + TRACE("Value Overflows CY\n"); + } + + if (!bOverflow && dwVtBits & VTBIT_DECIMAL) + { + WARN("VTBIT_DECIMAL not yet implemented\n"); +#if 0 + if (SUCCEEDED(VarDecFromR8(bNegative ? -whole : whole, &V_DECIMAL(pVarDst)))) + { + V_VT(pVarDst) = VT_DECIMAL; /* Fits into a decimal */ + TRACE("Set DECIMAL to final value\n"); + return S_OK; + } +#endif + } + } + + if (dwVtBits & VTBIT_DECIMAL) + { + FIXME("VT_DECIMAL > R8 not yet supported, returning overflow\n"); + } + return DISP_E_OVERFLOW; /* No more output choices */ } - /********************************************************************** * VarFormatDateTime [OLEAUT32.97] */ @@ -5338,7 +5571,7 @@ HRESULT WINAPI VarAnd(LPVARIANT left, LPVARIANT right, LPVARIANT result) LONGLONG lVal = -1; LONGLONG rVal = -1; LONGLONG res = -1; - int resT = 0; /* Testing has shown I2 & I2 == I2, all else + int resT = 0; /* Testing has shown I2 & I2 == I2, all else becomes I4, even unsigned ints (incl. UI2) */ lOk = TRUE; @@ -5736,7 +5969,7 @@ HRESULT WINAPI VarOr(LPVARIANT left, LPVARIANT right, LPVARIANT result) LONGLONG lVal = -1; LONGLONG rVal = -1; LONGLONG res = -1; - int resT = 0; /* Testing has shown I2 & I2 == I2, all else + int resT = 0; /* Testing has shown I2 & I2 == I2, all else becomes I4, even unsigned ints (incl. UI2) */ lOk = TRUE; diff --git a/dlls/oleaut32/variant.h b/dlls/oleaut32/variant.h new file mode 100644 index 00000000000..b6af4853541 --- /dev/null +++ b/dlls/oleaut32/variant.h @@ -0,0 +1,416 @@ +/* + * Variant Inlines + * + * Copyright 2003 Jon Griffiths + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "windef.h" +#include "winerror.h" +#include "oleauto.h" +#include + +/* Size constraints */ +#define I1_MAX 0x7f +#define I1_MIN ((-I1_MAX)-1) +#define UI1_MAX 0xff +#define UI1_MIN 0 +#define I2_MAX 0x7fff +#define I2_MIN ((-I2_MAX)-1) +#define UI2_MAX 0xffff +#define UI2_MIN 0 +#define I4_MAX 0x7fffffff +#define I4_MIN ((-I4_MAX)-1) +#define UI4_MAX 0xffffffff +#define UI4_MIN 0 +#define I8_MAX (((LONGLONG)I4_MAX << 32) | UI4_MAX) +#define I8_MIN ((-I8_MAX)-1) +#define UI8_MAX (((ULONGLONG)UI4_MAX << 32) | UI4_MAX) +#define UI8_MIN 0 +#define DATE_MAX 2958465 +#define DATE_MIN -657434 +#define R4_MAX 3.402823567797336e38 +#define R4_MIN 1.40129846432481707e-45 +#define R8_MAX 1.79769313486231470e+308 +#define R8_MIN 4.94065645841246544e-324 + +/* Value of sign for a positive number */ +#define DECIMAL_POS 0 + +/* Native headers don't change the union ordering for DECIMAL sign/scale (duh). + * This means that the signscale member is only useful for setting both members to 0. + * SIGNSCALE creates endian-correct values so that we can properly set both at once + * to values other than 0. + */ +#ifdef WORDS_BIGENDIAN +#define SIGNSCALE(sign,scale) (((scale) << 8) | sign) +#else +#define SIGNSCALE(sign,scale) (((sign) << 8) | scale) +#endif + +/* Macros for getting at a DECIMAL's parts */ +#define DEC_SIGN(d) ((d)->u.s.sign) +#define DEC_SCALE(d) ((d)->u.s.scale) +#define DEC_SIGNSCALE(d) ((d)->u.signscale) +#define DEC_HI32(d) ((d)->Hi32) +#define DEC_MID32(d) ((d)->u1.s1.Mid32) +#define DEC_LO32(d) ((d)->u1.s1.Lo32) +#define DEC_LO64(d) ((d)->u1.Lo64) + +#define DEC_MAX_SCALE 28 /* Maximum scale for a decimal */ + +/* Inline return type */ +#define RETTYP inline static HRESULT + +/* Simple compiler cast from one type to another */ +#define SIMPLE(dest, src, func) RETTYP _##func(src in, dest* out) { \ + *out = in; return S_OK; } + +/* Compiler cast where input cannot be negative */ +#define NEGTST(dest, src, func) RETTYP _##func(src in, dest* out) { \ + if (in < (src)0) return DISP_E_OVERFLOW; *out = in; return S_OK; } + +/* Compiler cast where input cannot be > some number */ +#define POSTST(dest, src, func, tst) RETTYP _##func(src in, dest* out) { \ + if (in > (dest)tst) return DISP_E_OVERFLOW; *out = in; return S_OK; } + +/* Compiler cast where input cannot be < some number or >= some other number */ +#define BOTHTST(dest, src, func, lo, hi) RETTYP _##func(src in, dest* out) { \ + if (in < (dest)lo || in > hi) return DISP_E_OVERFLOW; *out = in; return S_OK; } + +/* Conversions from IDispatch use the same code */ +HRESULT VARIANT_FromDisp(IDispatch*,LCID,void*,VARTYPE); +/* As do conversions from BSTR to numeric types */ +HRESULT VARIANT_NumberFromBstr(OLECHAR*,LCID,ULONG,void*,VARTYPE); + +#define CY_MULTIPLIER 10000 /* 4 dp of precision */ +#define CY_MULTIPLIER_F 10000.0 +#define CY_HALF (CY_MULTIPLIER/2) /* 0.5 */ +#define CY_HALF_F (CY_MULTIPLIER_F/2.0) + +/* I1 */ +POSTST(signed char, BYTE, VarI1FromUI1, I1_MAX); +BOTHTST(signed char, SHORT, VarI1FromI2, I1_MIN, I1_MAX); +BOTHTST(signed char, LONG, VarI1FromI4, I1_MIN, I1_MAX); +#define _VarI1FromR4(flt,out) VarI1FromR8((double)flt,out) +#define _VarI1FromR8 VarI1FromR8 +#define _VarI1FromCy VarI1FromCy +#define _VarI1FromDate(dt,out) VarI1FromR8((double)dt,out) +#define _VarI1FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_I1) +#define _VarI1FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, (BYTE*)out, VT_I1) +SIMPLE(signed char, VARIANT_BOOL, VarI1FromBool); +POSTST(signed char, USHORT, VarI1FromUI2, I1_MAX); +POSTST(signed char, ULONG, VarI1FromUI4, I1_MAX); +#define _VarI1FromDec VarI1FromDec +BOTHTST(signed char, LONG64, VarI1FromI8, I1_MIN, I1_MAX); +POSTST(signed char, ULONG64, VarI1FromUI8, I1_MAX); + +/* UI1 */ +BOTHTST(BYTE, SHORT, VarUI1FromI2, UI1_MIN, UI1_MAX); +#define _VarUI1FromR4(flt,out) VarUI1FromR8((double)flt,out) +#define _VarUI1FromR8 VarUI1FromR8 +#define _VarUI1FromCy VarUI1FromCy +#define _VarUI1FromDate(dt,out) VarUI1FromR8((double)dt,out) +#define _VarUI1FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_UI1) +#define _VarUI1FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_UI1) +SIMPLE(BYTE, VARIANT_BOOL, VarUI1FromBool); +NEGTST(BYTE, signed char, VarUI1FromI1); +POSTST(BYTE, USHORT, VarUI1FromUI2, UI1_MAX); +BOTHTST(BYTE, LONG, VarUI1FromI4, UI1_MIN, UI1_MAX); +POSTST(BYTE, ULONG, VarUI1FromUI4, UI1_MAX); +#define _VarUI1FromDec VarUI1FromDec +BOTHTST(BYTE, LONG64, VarUI1FromI8, UI1_MIN, UI1_MAX); +POSTST(BYTE, ULONG64, VarUI1FromUI8, UI1_MAX); + +/* I2 */ +POSTST(SHORT, BYTE, VarI2FromUI1, I2_MAX); +BOTHTST(SHORT, LONG, VarI2FromI4, I2_MIN, I2_MAX); +#define _VarI2FromR4(flt,out) VarI2FromR8((double)flt,out) +#define _VarI2FromR8 VarI2FromR8 +#define _VarI2FromCy VarI2FromCy +#define _VarI2FromDate(dt,out) VarI2FromR8((double)dt,out) +#define _VarI2FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_I2) +#define _VarI2FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, (BYTE*)out, VT_I2) +SIMPLE(SHORT, VARIANT_BOOL, VarI2FromBool); +SIMPLE(SHORT, signed char, VarI2FromI1); +POSTST(SHORT, USHORT, VarI2FromUI2, I2_MAX); +POSTST(SHORT, ULONG, VarI2FromUI4, I2_MAX); +#define _VarI2FromDec VarI2FromDec +BOTHTST(SHORT, LONG64, VarI2FromI8, I2_MIN, I2_MAX); +POSTST(SHORT, ULONG64, VarI2FromUI8, I2_MAX); + +/* UI2 */ +SIMPLE(USHORT, BYTE, VarUI2FromUI1); +NEGTST(USHORT, SHORT, VarUI2FromI2); +BOTHTST(USHORT, LONG, VarUI2FromI4, UI2_MIN, UI2_MAX); +#define _VarUI2FromR4(flt,out) VarUI2FromR8((double)flt,out) +#define _VarUI2FromR8 VarUI2FromR8 +#define _VarUI2FromCy VarUI2FromCy +#define _VarUI2FromDate(dt,out) VarUI2FromR8((double)dt,out) +#define _VarUI2FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_UI2) +#define _VarUI2FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_UI2) +SIMPLE(USHORT, VARIANT_BOOL, VarUI2FromBool); +NEGTST(USHORT, signed char, VarUI2FromI1); +POSTST(USHORT, ULONG, VarUI2FromUI4, UI2_MAX); +#define _VarUI2FromDec VarUI2FromDec +BOTHTST(USHORT, LONG64, VarUI2FromI8, UI2_MIN, UI2_MAX); +POSTST(USHORT, ULONG64, VarUI2FromUI8, UI2_MAX); + +/* I4 */ +SIMPLE(LONG, BYTE, VarI4FromUI1); +SIMPLE(LONG, SHORT, VarI4FromI2); +#define _VarI4FromR4(flt,out) VarI4FromR8((double)flt,out) +#define _VarI4FromR8 VarI4FromR8 +#define _VarI4FromCy VarI4FromCy +#define _VarI4FromDate(dt,out) VarI4FromR8((double)dt,out) +#define _VarI4FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_I4) +#define _VarI4FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, (BYTE*)out, VT_I4) +SIMPLE(LONG, VARIANT_BOOL, VarI4FromBool); +SIMPLE(LONG, signed char, VarI4FromI1); +SIMPLE(LONG, USHORT, VarI4FromUI2); +POSTST(LONG, ULONG, VarI4FromUI4, I4_MAX); +#define _VarI4FromDec VarI4FromDec +BOTHTST(LONG, LONG64, VarI4FromI8, I4_MIN, I4_MAX); +POSTST(LONG, ULONG64, VarI4FromUI8, I4_MAX); + +/* UI4 */ +SIMPLE(ULONG, BYTE, VarUI4FromUI1); +NEGTST(ULONG, SHORT, VarUI4FromI2); +NEGTST(ULONG, LONG, VarUI4FromI4); +#define _VarUI4FromR4(flt,out) VarUI4FromR8((double)flt,out) +#define _VarUI4FromR8 VarUI4FromR8 +#define _VarUI4FromCy VarUI4FromCy +#define _VarUI4FromDate(dt,out) VarUI4FromR8((double)dt,out) +#define _VarUI4FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_UI4) +#define _VarUI4FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_UI4) +SIMPLE(ULONG, VARIANT_BOOL, VarUI4FromBool); +NEGTST(ULONG, signed char, VarUI4FromI1); +SIMPLE(ULONG, USHORT, VarUI4FromUI2); +#define _VarUI4FromDec VarUI4FromDec +BOTHTST(ULONG, LONG64, VarUI4FromI8, UI4_MIN, UI4_MAX); +POSTST(ULONG, ULONG64, VarUI4FromUI8, UI4_MAX); + +/* I8 */ +SIMPLE(LONG64, BYTE, VarI8FromUI1); +SIMPLE(LONG64, SHORT, VarI8FromI2); +#define _VarI8FromR4 VarI8FromR8 +#define _VarI8FromR8 VarI8FromR8 +#define _VarI8FromCy VarI8FromCy +#define _VarI8FromDate(dt,out) VarI8FromR8((double)dt,out) +#define _VarI8FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_I8) +#define _VarI8FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_I8) +#define _VarI8FromBool _VarI8FromI2 +SIMPLE(LONG64, signed char, VarI8FromI1); +SIMPLE(LONG64, USHORT, VarI8FromUI2); +SIMPLE(LONG64, LONG, VarI8FromI4); +SIMPLE(LONG64, ULONG, VarI8FromUI4); +#define _VarI8FromDec VarI8FromDec +POSTST(LONG64, ULONG64, VarI8FromUI8, I8_MAX); + +/* UI8 */ +SIMPLE(ULONG64, BYTE, VarUI8FromUI1); +NEGTST(ULONG64, SHORT, VarUI8FromI2); +#define _VarUI8FromR4 VarUI8FromR8 +#define _VarUI8FromR8 VarUI8FromR8 +#define _VarUI8FromCy VarUI8FromCy +#define _VarUI8FromDate(dt,out) VarUI8FromR8((double)dt,out) +#define _VarUI8FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_UI8) +#define _VarUI8FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_UI8) +#define _VarUI8FromBool _VarI8FromI2 +NEGTST(ULONG64, signed char, VarUI8FromI1); +SIMPLE(ULONG64, USHORT, VarUI8FromUI2); +NEGTST(ULONG64, LONG, VarUI8FromI4); +SIMPLE(ULONG64, ULONG, VarUI8FromUI4); +#define _VarUI8FromDec VarUI8FromDec +NEGTST(ULONG64, LONG64, VarUI8FromI8); + +/* R4 (float) */ +SIMPLE(float, BYTE, VarR4FromUI1); +SIMPLE(float, SHORT, VarR4FromI2); +RETTYP _VarR4FromR8(double i, float* o) { + double d = i < 0.0 ? -i : i; + if (d > R4_MAX) return DISP_E_OVERFLOW; + *o = i; + return S_OK; +} +RETTYP _VarR4FromCy(CY i, float* o) { *o = (double)i.int64 / CY_MULTIPLIER_F; return S_OK; } +#define _VarR4FromDate(dt,out) _VarR4FromR8((double)dt,out) +#define _VarR4FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_R4) +#define _VarR4FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_R4) +#define _VarR4FromBool _VarR4FromI2 +SIMPLE(float, signed char, VarR4FromI1); +SIMPLE(float, USHORT, VarR4FromUI2); +SIMPLE(float, LONG, VarR4FromI4); +SIMPLE(float, ULONG, VarR4FromUI4); +#define _VarR4FromDec VarR4FromDec +SIMPLE(float, LONG64, VarR4FromI8); +SIMPLE(float, ULONG64, VarR4FromUI8); + +/* R8 (double) */ +SIMPLE(double, BYTE, VarR8FromUI1); +SIMPLE(double, SHORT, VarR8FromI2); +SIMPLE(double, float, VarR8FromR4); +RETTYP _VarR8FromCy(CY i, double* o) { *o = (double)i.int64 / CY_MULTIPLIER_F; return S_OK; } +SIMPLE(double, DATE, VarR8FromDate); +#define _VarR8FromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_R8) +#define _VarR8FromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_R8) +#define _VarR8FromBool _VarR8FromI2 +SIMPLE(double, signed char, VarR8FromI1); +SIMPLE(double, USHORT, VarR8FromUI2); +SIMPLE(double, LONG, VarR8FromI4); +SIMPLE(double, ULONG, VarR8FromUI4); +#define _VarR8FromDec VarR8FromDec +SIMPLE(double, LONG64, VarR8FromI8); +SIMPLE(double, ULONG64, VarR8FromUI8); + +/* BOOL */ +#define BOOLFUNC(src, func) RETTYP _##func(src in, VARIANT_BOOL* out) { \ + *out = in ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } + +BOOLFUNC(signed char,VarBoolFromI1); +BOOLFUNC(BYTE,VarBoolFromUI1); +BOOLFUNC(SHORT,VarBoolFromI2); +BOOLFUNC(USHORT,VarBoolFromUI2); +BOOLFUNC(LONG,VarBoolFromI4); +BOOLFUNC(ULONG,VarBoolFromUI4); +BOOLFUNC(LONG64,VarBoolFromI8); +BOOLFUNC(ULONG64,VarBoolFromUI8); +#define _VarBoolFromR4(flt,out) _VarBoolFromR8((double)flt,out) +BOOLFUNC(double,VarBoolFromR8); +#define _VarBoolFromCy(i,o) _VarBoolFromI8(i.int64,o) +#define _VarBoolFromDate(dt,out) _VarBoolFromR8((double)dt,out) +#define _VarBoolFromStr VarBoolFromStr +#define _VarBoolFromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, (BYTE*)out, VT_BOOL) +#define _VarBoolFromDec VarBoolFromDec + +/* DECIMAL */ +#define _VarDecFromUI1 VarDecFromUI4 +#define _VarDecFromI2 VarDecFromI4 +#define _VarDecFromR4 VarDecFromR8 +#define _VarDecFromR8 VarDecFromR8 +#define _VarDecFromCy VarDecFromCy +#define _VarDecFromDate(dt,out) VarDecFromR8((double)dt,out) +#define _VarDecFromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_DECIMAL) +#define _VarDecFromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_DECIMAL) +#define _VarDecFromBool VarDecFromBool +#define _VarDecFromI1 VarDecFromI4 +#define _VarDecFromUI2 VarDecFromUI4 +#define _VarDecFromI4 VarDecFromI4 +#define _VarDecFromUI4 VarDecFromUI4 +#define _VarDecFromI8 VarDecFromI8 +#define _VarDecFromUI8 VarDecFromUI8 + +/* CY (Currency) */ +#define _VarCyFromUI1 VarCyFromR8 +#define _VarCyFromI2 VarCyFromR8 +#define _VarCyFromR4 VarCyFromR8 +#define _VarCyFromR8 VarCyFromR8 +#define _VarCyFromDate VarCyFromR8 +#define _VarCyFromStr(str,lcid,flags,out) VARIANT_NumberFromBstr(str,lcid,flags,(BYTE*)out,VT_CY) +#define _VarCyFromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_CY) +#define _VarCyFromBool VarCyFromR8 +#define _VarCyFromI1 VarCyFromR8 +#define _VarCyFromUI2 VarCyFromR8 +#define _VarCyFromI4 VarCyFromR8 +#define _VarCyFromUI4 VarCyFromR8 +#define _VarCyFromDec VarCyFromDec +RETTYP _VarCyFromI8(LONG64 i, CY* o) { + if (i <= (I8_MIN/CY_MULTIPLIER) || i >= (I8_MAX/CY_MULTIPLIER)) return DISP_E_OVERFLOW; + o->int64 = i * CY_MULTIPLIER; + return S_OK; +} +#define _VarCyFromUI8 VarCyFromR8 + +/* DATE */ +#define _VarDateFromUI1 VarDateFromR8 +#define _VarDateFromI2 VarDateFromR8 +#define _VarDateFromR4 VarDateFromR8 +#define _VarDateFromR8 VarDateFromR8 +#define _VarDateFromCy VarDateFromCy +#define _VarDateFromStr VarDateFromStr +#define _VarDateFromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_DATE) +#define _VarDateFromBool VarDateFromR8 +#define _VarDateFromI1 VarDateFromR8 +#define _VarDateFromUI2 VarDateFromR8 +#define _VarDateFromI4 VarDateFromR8 +#define _VarDateFromUI4 VarDateFromR8 +#define _VarDateFromDec VarDateFromDec +#define _VarDateFromI8 VarDateFromR8 +#define _VarDateFromUI8 VarDateFromR8 + +/* BSTR */ +#define _VarBstrFromUI1 VarBstrFromUI4 +#define _VarBstrFromI2 VarBstrFromI4 +#define _VarBstrFromR4 VarBstrFromR8 +#define _VarBstrFromR8 VarBstrFromR8 +#define _VarBstrFromCy VarBstrFromCy +#define _VarBstrFromDate VarBstrFromDate +#define _VarBstrFromDisp(disp,lcid,out) VARIANT_FromDisp(disp, lcid, out, VT_BSTR) +#define _VarBstrFromBool VarBstrFromBool +#define _VarBstrFromI1 VarBstrFromI4 +#define _VarBstrFromUI2 VarBstrFromUI4 +#define _VarBstrFromI4 VarBstrFromI4 +#define _VarBstrFromUI4 VarBstrFromUI4 +#define _VarBstrFromDec VarBstrFromDec +#define _VarBstrFromI8 VarBstrFromI8 +#define _VarBstrFromUI8 VarBstrFromUI8 + +/* Macro to inline conversion from a float or double to any integer type, + * rounding according to the 'dutch' convention. + */ +#define OLEAUT32_DutchRound(typ, value, res) do { \ + double whole = floor((double)value), fract = (double)value - whole; \ + if (fract > 0.5) res = (typ)whole + (typ)1; \ + else if (fract == 0.5) { typ is_odd = (typ)whole & 1; res = whole + is_odd; } \ + else if (fract >= 0.0) res = (typ)whole; \ + else if (fract == -0.5) { typ is_odd = (typ)whole & 1; res = whole - is_odd; } \ + else if (fract > -0.5) res = (typ)whole; \ + else res = (typ)whole - (typ)1; \ +} while(0); + +/* Localised text for variant conversions */ +typedef struct tagVARIANT_TEXT +{ + LPCWSTR szText; + BYTE langId; + BYTE iOffsetFalse; + BYTE iOffsetYes; + BYTE iOffsetNo; + BYTE iOffsetOn; + BYTE iOffsetOff; +} VARIANT_TEXT; + +#define NUM_LOCALISED_LANGS 13 + +extern const VARIANT_TEXT VARIANT_LocalisedTextList[NUM_LOCALISED_LANGS]; + +const VARIANT_TEXT *VARIANT_GetLocalisedText(LANGID); + +/* The localised characters that make up a valid number */ +typedef struct tagVARIANT_NUMBER_CHARS +{ + WCHAR cNegativeSymbol; + WCHAR cPositiveSymbol; + WCHAR cDecimalPoint; + WCHAR cDigitSeperator; + WCHAR cCurrencyLocal; + WCHAR cCurrencyLocal2; + WCHAR cCurrencyDecimalPoint; + WCHAR cCurrencyDigitSeperator; +} VARIANT_NUMBER_CHARS; + +void VARIANT_GetLocalisedNumberChars(VARIANT_NUMBER_CHARS*,LCID,DWORD);