*
* 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
WINE_DEFAULT_DEBUG_CHANNEL(variant);
-extern HMODULE OLEAUT32_hModule;
+extern HMODULE hProxyDll DECLSPEC_HIDDEN;
+
+#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)
static const WCHAR szFloatFormatW[] = { '%','.','7','G','\0' };
static const WCHAR szDoubleFormatW[] = { '%','.','1','5','G','\0' };
case VT_UI8: memcpy(pOut, &V_UI8(srcVar), sizeof (LONG64)); break;
case VT_INT_PTR: memcpy(pOut, &V_INT_PTR(srcVar), sizeof (INT_PTR)); break;
case VT_DECIMAL: memcpy(pOut, &V_DECIMAL(srcVar), sizeof (DECIMAL)); break;
+ case VT_BSTR: memcpy(pOut, &V_BSTR(srcVar), sizeof(BSTR)); break;
default:
FIXME("VT_ type %d unhandled, please report!\n", vt);
}
}
+/* Macro to inline conversion from a float or double to any integer type,
+ * rounding according to the 'dutch' convention.
+ */
+#define VARIANT_DutchRound(typ, value, res) do { \
+ double whole = value < 0 ? ceil(value) : floor(value); \
+ double fract = 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)
+
/* Coerce VT_BSTR to a numeric type */
static HRESULT VARIANT_NumberFromBstr(OLECHAR* pStrIn, LCID lcid, ULONG ulFlags,
}
/* Coerce VT_DISPATCH to another type */
-static HRESULT VARIANT_FromDisp(IDispatch* pdispIn, LCID lcid, void* pOut, VARTYPE vt)
+static HRESULT VARIANT_FromDisp(IDispatch* pdispIn, LCID lcid, void* pOut,
+ VARTYPE vt, DWORD dwFlags)
{
- static const DISPPARAMS emptyParams = { NULL, NULL, 0, 0 };
+ static DISPPARAMS emptyParams = { NULL, NULL, 0, 0 };
VARIANTARG srcVar, dstVar;
HRESULT hRet;
/* Get the default 'value' property from the IDispatch */
hRet = IDispatch_Invoke(pdispIn, DISPID_VALUE, &IID_NULL, lcid, DISPATCH_PROPERTYGET,
- (DISPPARAMS*)&emptyParams, &srcVar, NULL, NULL);
+ &emptyParams, &srcVar, NULL, NULL);
if (SUCCEEDED(hRet))
{
/* Convert the property to the requested type */
V_VT(&dstVar) = VT_EMPTY;
- hRet = VariantChangeTypeEx(&dstVar, &srcVar, lcid, 0, vt);
+ hRet = VariantChangeTypeEx(&dstVar, &srcVar, lcid, dwFlags, vt);
VariantClear(&srcVar);
if (SUCCEEDED(hRet))
return hRet;
}
+/* Inline return type */
+#define RETTYP static inline 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 < 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; }
+
+/* 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)
+SIMPLE(signed char, VARIANT_BOOL, VarI1FromBool)
+POSTST(signed char, USHORT, VarI1FromUI2, I1_MAX)
+POSTST(signed char, ULONG, VarI1FromUI4, I1_MAX)
+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)
+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)
+BOTHTST(BYTE, LONG64, VarUI1FromI8, UI1_MIN, UI1_MAX)
+POSTST(BYTE, ULONG64, VarUI1FromUI8, UI1_MAX)
+
+/* I2 */
+SIMPLE(SHORT, BYTE, VarI2FromUI1)
+BOTHTST(SHORT, LONG, VarI2FromI4, I2_MIN, I2_MAX)
+SIMPLE(SHORT, VARIANT_BOOL, VarI2FromBool)
+SIMPLE(SHORT, signed char, VarI2FromI1)
+POSTST(SHORT, USHORT, VarI2FromUI2, I2_MAX)
+POSTST(SHORT, ULONG, VarI2FromUI4, I2_MAX)
+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)
+SIMPLE(USHORT, VARIANT_BOOL, VarUI2FromBool)
+NEGTST(USHORT, signed char, VarUI2FromI1)
+POSTST(USHORT, ULONG, VarUI2FromUI4, UI2_MAX)
+BOTHTST(USHORT, LONG64, VarUI2FromI8, UI2_MIN, UI2_MAX)
+POSTST(USHORT, ULONG64, VarUI2FromUI8, UI2_MAX)
+
+/* I4 */
+SIMPLE(LONG, BYTE, VarI4FromUI1)
+SIMPLE(LONG, SHORT, VarI4FromI2)
+SIMPLE(LONG, VARIANT_BOOL, VarI4FromBool)
+SIMPLE(LONG, signed char, VarI4FromI1)
+SIMPLE(LONG, USHORT, VarI4FromUI2)
+POSTST(LONG, ULONG, VarI4FromUI4, I4_MAX)
+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)
+SIMPLE(ULONG, VARIANT_BOOL, VarUI4FromBool)
+NEGTST(ULONG, signed char, VarUI4FromI1)
+SIMPLE(ULONG, USHORT, VarUI4FromUI2)
+BOTHTST(ULONG, LONG64, VarUI4FromI8, UI4_MIN, UI4_MAX)
+POSTST(ULONG, ULONG64, VarUI4FromUI8, UI4_MAX)
+
+/* I8 */
+SIMPLE(LONG64, BYTE, VarI8FromUI1)
+SIMPLE(LONG64, SHORT, VarI8FromI2)
+SIMPLE(LONG64, signed char, VarI8FromI1)
+SIMPLE(LONG64, USHORT, VarI8FromUI2)
+SIMPLE(LONG64, ULONG, VarI8FromUI4)
+POSTST(LONG64, ULONG64, VarI8FromUI8, I8_MAX)
+
+/* UI8 */
+SIMPLE(ULONG64, BYTE, VarUI8FromUI1)
+NEGTST(ULONG64, SHORT, VarUI8FromI2)
+NEGTST(ULONG64, signed char, VarUI8FromI1)
+SIMPLE(ULONG64, USHORT, VarUI8FromUI2)
+SIMPLE(ULONG64, ULONG, VarUI8FromUI4)
+NEGTST(ULONG64, LONG64, VarUI8FromI8)
+
+/* R4 (float) */
+SIMPLE(float, BYTE, VarR4FromUI1)
+SIMPLE(float, SHORT, VarR4FromI2)
+SIMPLE(float, signed char, VarR4FromI1)
+SIMPLE(float, USHORT, VarR4FromUI2)
+SIMPLE(float, LONG, VarR4FromI4)
+SIMPLE(float, ULONG, VarR4FromUI4)
+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)
+SIMPLE(double, signed char, VarR8FromI1)
+SIMPLE(double, USHORT, VarR8FromUI2)
+SIMPLE(double, LONG, VarR8FromI4)
+SIMPLE(double, ULONG, VarR8FromUI4)
+SIMPLE(double, LONG64, VarR8FromI8)
+SIMPLE(double, ULONG64, VarR8FromUI8)
+
+
/* I1
*/
{
if (dblIn < (double)I1_MIN || dblIn > (double)I1_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(CHAR, dblIn, *pcOut);
+ VARIANT_DutchRound(CHAR, dblIn, *pcOut);
return S_OK;
}
*/
HRESULT WINAPI VarI1FromDisp(IDispatch* pdispIn, LCID lcid, signed char* pcOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pcOut, VT_I1);
+ return VARIANT_FromDisp(pdispIn, lcid, pcOut, VT_I1, 0);
}
/************************************************************************
{
if (dblIn < -0.5 || dblIn > (double)UI1_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(BYTE, dblIn, *pbOut);
+ VARIANT_DutchRound(BYTE, dblIn, *pbOut);
return S_OK;
}
*/
HRESULT WINAPI VarUI1FromDisp(IDispatch* pdispIn, LCID lcid, BYTE* pbOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pbOut, VT_UI1);
+ return VARIANT_FromDisp(pdispIn, lcid, pbOut, VT_UI1, 0);
}
/************************************************************************
{
if (dblIn < (double)I2_MIN || dblIn > (double)I2_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(SHORT, dblIn, *psOut);
+ VARIANT_DutchRound(SHORT, dblIn, *psOut);
return S_OK;
}
*/
HRESULT WINAPI VarI2FromDisp(IDispatch* pdispIn, LCID lcid, SHORT* psOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, psOut, VT_I2);
+ return VARIANT_FromDisp(pdispIn, lcid, psOut, VT_I2, 0);
}
/************************************************************************
{
if (dblIn < -0.5 || dblIn > (double)UI2_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(USHORT, dblIn, *pusOut);
+ VARIANT_DutchRound(USHORT, dblIn, *pusOut);
return S_OK;
}
*/
HRESULT WINAPI VarUI2FromDisp(IDispatch* pdispIn, LCID lcid, USHORT* pusOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pusOut, VT_UI2);
+ return VARIANT_FromDisp(pdispIn, lcid, pusOut, VT_UI2, 0);
}
/************************************************************************
{
if (dblIn < (double)I4_MIN || dblIn > (double)I4_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(LONG, dblIn, *piOut);
+ VARIANT_DutchRound(LONG, dblIn, *piOut);
return S_OK;
}
*/
HRESULT WINAPI VarI4FromDisp(IDispatch* pdispIn, LCID lcid, LONG *piOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, piOut, VT_I4);
+ return VARIANT_FromDisp(pdispIn, lcid, piOut, VT_I4, 0);
}
/************************************************************************
{
if (dblIn < -0.5 || dblIn > (double)UI4_MAX)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(ULONG, dblIn, *pulOut);
+ VARIANT_DutchRound(ULONG, dblIn, *pulOut);
return S_OK;
}
*/
HRESULT WINAPI VarUI4FromDisp(IDispatch* pdispIn, LCID lcid, ULONG *pulOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pulOut, VT_UI4);
+ return VARIANT_FromDisp(pdispIn, lcid, pulOut, VT_UI4, 0);
}
/************************************************************************
{
if ( dblIn < -4611686018427387904.0 || dblIn >= 4611686018427387904.0)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(LONG64, dblIn, *pi64Out);
+ VARIANT_DutchRound(LONG64, dblIn, *pi64Out);
return S_OK;
}
*/
HRESULT WINAPI VarI8FromDisp(IDispatch* pdispIn, LCID lcid, LONG64* pi64Out)
{
- return VARIANT_FromDisp(pdispIn, lcid, pi64Out, VT_I8);
+ return VARIANT_FromDisp(pdispIn, lcid, pi64Out, VT_I8, 0);
}
/************************************************************************
{
if (dblIn < -0.5 || dblIn > 1.844674407370955e19)
return DISP_E_OVERFLOW;
- OLEAUT32_DutchRound(ULONG64, dblIn, *pui64Out);
+ VARIANT_DutchRound(ULONG64, dblIn, *pui64Out);
return S_OK;
}
*/
HRESULT WINAPI VarUI8FromDisp(IDispatch* pdispIn, LCID lcid, ULONG64* pui64Out)
{
- return VARIANT_FromDisp(pdispIn, lcid, pui64Out, VT_UI8);
+ return VARIANT_FromDisp(pdispIn, lcid, pui64Out, VT_UI8, 0);
}
/************************************************************************
*/
HRESULT WINAPI VarR4FromDisp(IDispatch* pdispIn, LCID lcid, float *pFltOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pFltOut, VT_R4);
+ return VARIANT_FromDisp(pdispIn, lcid, pFltOut, VT_R4, 0);
}
/************************************************************************
if (DEC_HI32(pDecIn))
{
highPart = (double)DEC_HI32(pDecIn) / (double)divisor;
- highPart *= 1.0e64;
+ highPart *= 4294967296.0F;
+ highPart *= 4294967296.0F;
}
else
highPart = 0.0;
*/
HRESULT WINAPI VarR8FromDisp(IDispatch* pdispIn, LCID lcid, double *pDblOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pDblOut, VT_R8);
+ return VARIANT_FromDisp(pdispIn, lcid, pDblOut, VT_R8, 0);
}
/************************************************************************
* Success: S_OK.
* Failure: E_INVALIDARG, if the source value is invalid.
*/
-HRESULT WINAPI VarR8FromDec(DECIMAL* pDecIn, double *pDblOut)
+HRESULT WINAPI VarR8FromDec(const DECIMAL* pDecIn, double *pDblOut)
{
BYTE scale = DEC_SCALE(pDecIn);
double divisor = 1.0, highPart;
if (DEC_HI32(pDecIn))
{
highPart = (double)DEC_HI32(pDecIn) / divisor;
- highPart *= 1.0e64;
+ highPart *= 4294967296.0F;
+ highPart *= 4294967296.0F;
}
else
highPart = 0.0;
*/
HRESULT WINAPI VarCyFromUI1(BYTE bIn, CY* pCyOut)
{
- return VarCyFromR8(bIn, pCyOut);
+ pCyOut->int64 = (ULONG64)bIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromI2(SHORT sIn, CY* pCyOut)
{
- return VarCyFromR8(sIn, pCyOut);
+ pCyOut->int64 = (LONG64)sIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromI4(LONG lIn, CY* pCyOut)
{
- return VarCyFromR8(lIn, pCyOut);
+ pCyOut->int64 = (LONG64)lIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromR8(double dblIn, CY* pCyOut)
{
-#if defined(__GNUC__) && defined(__i386__)
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
/* This code gives identical results to Win32 on Intel.
* Here we use fp exceptions to catch overflows when storing the value.
*/
if (result_fpstatus & 0x9) /* Overflow | Invalid */
return DISP_E_OVERFLOW;
- return S_OK;
#else
/* This version produces slightly different results for boundary cases */
if (dblIn < -922337203685477.5807 || dblIn >= 922337203685477.5807)
return DISP_E_OVERFLOW;
dblIn *= CY_MULTIPLIER_F;
- OLEAUT32_DutchRound(LONG64, dblIn, pCyOut->int64);
+ VARIANT_DutchRound(LONG64, dblIn, pCyOut->int64);
#endif
return S_OK;
}
*/
HRESULT WINAPI VarCyFromDisp(IDispatch* pdispIn, LCID lcid, CY* pCyOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pCyOut, VT_CY);
+ return VARIANT_FromDisp(pdispIn, lcid, pCyOut, VT_CY, 0);
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromBool(VARIANT_BOOL boolIn, CY* pCyOut)
{
- return VarCyFromR8(boolIn, pCyOut);
+ pCyOut->int64 = (LONG64)boolIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromI1(signed char cIn, CY* pCyOut)
{
- return VarCyFromR8(cIn, pCyOut);
+ pCyOut->int64 = (LONG64)cIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromUI2(USHORT usIn, CY* pCyOut)
{
- return VarCyFromR8(usIn, pCyOut);
+ pCyOut->int64 = (ULONG64)usIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromUI4(ULONG ulIn, CY* pCyOut)
{
- return VarCyFromR8(ulIn, pCyOut);
+ pCyOut->int64 = (ULONG64)ulIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
*/
HRESULT WINAPI VarCyFromUI8(ULONG64 ullIn, CY* pCyOut)
{
- return VarCyFromR8(ullIn, pCyOut);
+ if (ullIn >= (I8_MAX/CY_MULTIPLIER)) return DISP_E_OVERFLOW;
+ pCyOut->int64 = ullIn * CY_MULTIPLIER;
+ return S_OK;
}
/************************************************************************
_VarR8FromCy(cyIn, &d);
d = d * div;
- OLEAUT32_DutchRound(LONGLONG, d, pCyOut->int64)
+ VARIANT_DutchRound(LONGLONG, d, pCyOut->int64);
d = (double)pCyOut->int64 / div * CY_MULTIPLIER_F;
- OLEAUT32_DutchRound(LONGLONG, d, pCyOut->int64)
+ VARIANT_DutchRound(LONGLONG, d, pCyOut->int64);
return S_OK;
}
}
#define LOCALE_EN_US (MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT))
+/* internal representation of the value stored in a DECIMAL. The bytes are
+ stored from LSB at index 0 to MSB at index 11
+ */
+typedef struct DECIMAL_internal
+{
+ DWORD bitsnum[3]; /* 96 significant bits, unsigned */
+ unsigned char scale; /* number scaled * 10 ^ -(scale) */
+ unsigned int sign : 1; /* 0 - positive, 1 - negative */
+} VARIANT_DI;
+
+static HRESULT VARIANT_DI_FromR4(float source, VARIANT_DI * dest);
+static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest);
+static void VARIANT_DIFromDec(const DECIMAL * from, VARIANT_DI * to);
+static void VARIANT_DecFromDI(const VARIANT_DI * from, DECIMAL * to);
+
/************************************************************************
* VarDecFromR4 (OLEAUT32.193)
*
*/
HRESULT WINAPI VarDecFromR4(FLOAT fltIn, DECIMAL* pDecOut)
{
- WCHAR buff[256];
+ VARIANT_DI di;
+ HRESULT hres;
- sprintfW( buff, szFloatFormatW, fltIn );
- return VarDecFromStr(buff, LOCALE_EN_US, 0, pDecOut);
+ hres = VARIANT_DI_FromR4(fltIn, &di);
+ if (hres == S_OK) VARIANT_DecFromDI(&di, pDecOut);
+ return hres;
}
/************************************************************************
*/
HRESULT WINAPI VarDecFromR8(double dblIn, DECIMAL* pDecOut)
{
- WCHAR buff[256];
+ VARIANT_DI di;
+ HRESULT hres;
- sprintfW( buff, szDoubleFormatW, dblIn );
- return VarDecFromStr(buff, LOCALE_EN_US, 0, pDecOut);
+ hres = VARIANT_DI_FromR8(dblIn, &di);
+ if (hres == S_OK) VARIANT_DecFromDI(&di, pDecOut);
+ return hres;
}
/************************************************************************
*/
HRESULT WINAPI VarDecFromDisp(IDispatch* pdispIn, LCID lcid, DECIMAL* pDecOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pDecOut, VT_DECIMAL);
+ return VARIANT_FromDisp(pdispIn, lcid, pDecOut, VT_DECIMAL, 0);
}
/************************************************************************
return hRet;
}
+/* translate from external DECIMAL format into an internal representation */
+static void VARIANT_DIFromDec(const DECIMAL * from, VARIANT_DI * to)
+{
+ to->scale = DEC_SCALE(from);
+ to->sign = DEC_SIGN(from) ? 1 : 0;
+
+ to->bitsnum[0] = DEC_LO32(from);
+ to->bitsnum[1] = DEC_MID32(from);
+ to->bitsnum[2] = DEC_HI32(from);
+}
+
+static void VARIANT_DecFromDI(const VARIANT_DI * from, DECIMAL * to)
+{
+ if (from->sign) {
+ DEC_SIGNSCALE(to) = SIGNSCALE(DECIMAL_NEG, from->scale);
+ } else {
+ DEC_SIGNSCALE(to) = SIGNSCALE(DECIMAL_POS, from->scale);
+ }
+
+ DEC_LO32(to) = from->bitsnum[0];
+ DEC_MID32(to) = from->bitsnum[1];
+ DEC_HI32(to) = from->bitsnum[2];
+}
+
+/* clear an internal representation of a DECIMAL */
+static void VARIANT_DI_clear(VARIANT_DI * i)
+{
+ memset(i, 0, sizeof(VARIANT_DI));
+}
+
+/* divide the (unsigned) number stored in p (LSB) by a byte value (<= 0xff). Any nonzero
+ size is supported. The value in p is replaced by the quotient of the division, and
+ the remainder is returned as a result. This routine is most often used with a divisor
+ of 10 in order to scale up numbers, and in the DECIMAL->string conversion.
+ */
+static unsigned char VARIANT_int_divbychar(DWORD * p, unsigned int n, unsigned char divisor)
+{
+ if (divisor == 0) {
+ /* division by 0 */
+ return 0xFF;
+ } else if (divisor == 1) {
+ /* dividend remains unchanged */
+ return 0;
+ } else {
+ unsigned char remainder = 0;
+ ULONGLONG iTempDividend;
+ signed int i;
+
+ for (i = n - 1; i >= 0 && !p[i]; i--); /* skip leading zeros */
+ for (; i >= 0; i--) {
+ iTempDividend = ((ULONGLONG)remainder << 32) + p[i];
+ remainder = iTempDividend % divisor;
+ p[i] = iTempDividend / divisor;
+ }
+
+ return remainder;
+ }
+}
+
+/* check to test if encoded number is a zero. Returns 1 if zero, 0 for nonzero */
+static int VARIANT_int_iszero(const DWORD * p, unsigned int n)
+{
+ for (; n > 0; n--) if (*p++ != 0) return 0;
+ return 1;
+}
+
+/* multiply two DECIMALS, without changing either one, and place result in third
+ parameter. Result is normalized when scale is > 0. Attempts to remove significant
+ digits when scale > 0 in order to fit an overflowing result. Final overflow
+ flag is returned.
+ */
+static int VARIANT_DI_mul(const VARIANT_DI * a, const VARIANT_DI * b, VARIANT_DI * result)
+{
+ int r_overflow = 0;
+ DWORD running[6];
+ signed int mulstart;
+
+ VARIANT_DI_clear(result);
+ result->sign = (a->sign ^ b->sign) ? 1 : 0;
+
+ /* Multiply 128-bit operands into a (max) 256-bit result. The scale
+ of the result is formed by adding the scales of the operands.
+ */
+ result->scale = a->scale + b->scale;
+ memset(running, 0, sizeof(running));
+
+ /* count number of leading zero-bytes in operand A */
+ for (mulstart = sizeof(a->bitsnum)/sizeof(DWORD) - 1; mulstart >= 0 && !a->bitsnum[mulstart]; mulstart--);
+ if (mulstart < 0) {
+ /* result is 0, because operand A is 0 */
+ result->scale = 0;
+ result->sign = 0;
+ } else {
+ unsigned char remainder = 0;
+ int iA;
+
+ /* perform actual multiplication */
+ for (iA = 0; iA <= mulstart; iA++) {
+ ULONG iOverflowMul;
+ int iB;
+
+ for (iOverflowMul = 0, iB = 0; iB < sizeof(b->bitsnum)/sizeof(DWORD); iB++) {
+ ULONG iRV;
+ int iR;
+
+ iRV = VARIANT_Mul(b->bitsnum[iB], a->bitsnum[iA], &iOverflowMul);
+ iR = iA + iB;
+ do {
+ running[iR] = VARIANT_Add(running[iR], 0, &iRV);
+ iR++;
+ } while (iRV);
+ }
+ }
+
+/* Too bad - native oleaut does not do this, so we should not either */
+#if 0
+ /* While the result is divisible by 10, and the scale > 0, divide by 10.
+ This operation should not lose significant digits, and gives an
+ opportunity to reduce the possibility of overflows in future
+ operations issued by the application.
+ */
+ while (result->scale > 0) {
+ memcpy(quotient, running, sizeof(quotient));
+ remainder = VARIANT_int_divbychar(quotient, sizeof(quotient) / sizeof(DWORD), 10);
+ if (remainder > 0) break;
+ memcpy(running, quotient, sizeof(quotient));
+ result->scale--;
+ }
+#endif
+ /* While the 256-bit result overflows, and the scale > 0, divide by 10.
+ This operation *will* lose significant digits of the result because
+ all the factors of 10 were consumed by the previous operation.
+ */
+ while (result->scale > 0 && !VARIANT_int_iszero(
+ running + sizeof(result->bitsnum) / sizeof(DWORD),
+ (sizeof(running) - sizeof(result->bitsnum)) / sizeof(DWORD))) {
+
+ remainder = VARIANT_int_divbychar(running, sizeof(running) / sizeof(DWORD), 10);
+ if (remainder > 0) WARN("losing significant digits (remainder %u)...\n", remainder);
+ result->scale--;
+ }
+
+ /* round up the result - native oleaut32 does this */
+ if (remainder >= 5) {
+ unsigned int i;
+ for (remainder = 1, i = 0; i < sizeof(running)/sizeof(DWORD) && remainder; i++) {
+ ULONGLONG digit = running[i] + 1;
+ remainder = (digit > 0xFFFFFFFF) ? 1 : 0;
+ running[i] = digit & 0xFFFFFFFF;
+ }
+ }
+
+ /* Signal overflow if scale == 0 and 256-bit result still overflows,
+ and copy result bits into result structure
+ */
+ r_overflow = !VARIANT_int_iszero(
+ running + sizeof(result->bitsnum)/sizeof(DWORD),
+ (sizeof(running) - sizeof(result->bitsnum))/sizeof(DWORD));
+ memcpy(result->bitsnum, running, sizeof(result->bitsnum));
+ }
+ return r_overflow;
+}
+
+/* cast DECIMAL into string. Any scale should be handled properly. en_US locale is
+ hardcoded (period for decimal separator, dash as negative sign). Returns 0 for
+ success, nonzero if insufficient space in output buffer.
+ */
+static int VARIANT_DI_tostringW(const VARIANT_DI * a, WCHAR * s, unsigned int n)
+{
+ int overflow = 0;
+ DWORD quotient[3];
+ unsigned char remainder;
+ unsigned int i;
+
+ /* place negative sign */
+ if (!VARIANT_int_iszero(a->bitsnum, sizeof(a->bitsnum) / sizeof(DWORD)) && a->sign) {
+ if (n > 0) {
+ *s++ = '-';
+ n--;
+ }
+ else overflow = 1;
+ }
+
+ /* prepare initial 0 */
+ if (!overflow) {
+ if (n >= 2) {
+ s[0] = '0';
+ s[1] = '\0';
+ } else overflow = 1;
+ }
+
+ i = 0;
+ memcpy(quotient, a->bitsnum, sizeof(a->bitsnum));
+ while (!overflow && !VARIANT_int_iszero(quotient, sizeof(quotient) / sizeof(DWORD))) {
+ remainder = VARIANT_int_divbychar(quotient, sizeof(quotient) / sizeof(DWORD), 10);
+ if (i + 2 > n) {
+ overflow = 1;
+ } else {
+ s[i++] = '0' + remainder;
+ s[i] = '\0';
+ }
+ }
+
+ if (!overflow && !VARIANT_int_iszero(a->bitsnum, sizeof(a->bitsnum) / sizeof(DWORD))) {
+
+ /* reverse order of digits */
+ WCHAR * x = s; WCHAR * y = s + i - 1;
+ while (x < y) {
+ *x ^= *y;
+ *y ^= *x;
+ *x++ ^= *y--;
+ }
+
+ /* check for decimal point. "i" now has string length */
+ if (i <= a->scale) {
+ unsigned int numzeroes = a->scale + 1 - i;
+ if (i + 1 + numzeroes >= n) {
+ overflow = 1;
+ } else {
+ memmove(s + numzeroes, s, (i + 1) * sizeof(WCHAR));
+ i += numzeroes;
+ while (numzeroes > 0) {
+ s[--numzeroes] = '0';
+ }
+ }
+ }
+
+ /* place decimal point */
+ if (a->scale > 0) {
+ unsigned int periodpos = i - a->scale;
+ if (i + 2 >= n) {
+ overflow = 1;
+ } else {
+ memmove(s + periodpos + 1, s + periodpos, (i + 1 - periodpos) * sizeof(WCHAR));
+ s[periodpos] = '.'; i++;
+
+ /* remove extra zeros at the end, if any */
+ while (s[i - 1] == '0') s[--i] = '\0';
+ if (s[i - 1] == '.') s[--i] = '\0';
+ }
+ }
+ }
+
+ return overflow;
+}
+
+/* shift the bits of a DWORD array to the left. p[0] is assumed LSB */
+static void VARIANT_int_shiftleft(DWORD * p, unsigned int n, unsigned int shift)
+{
+ DWORD shifted;
+ unsigned int i;
+
+ /* shift whole DWORDs to the left */
+ while (shift >= 32)
+ {
+ memmove(p + 1, p, (n - 1) * sizeof(DWORD));
+ *p = 0; shift -= 32;
+ }
+
+ /* shift remainder (1..31 bits) */
+ shifted = 0;
+ if (shift > 0) for (i = 0; i < n; i++)
+ {
+ DWORD b;
+ b = p[i] >> (32 - shift);
+ p[i] = (p[i] << shift) | shifted;
+ shifted = b;
+ }
+}
+
+/* add the (unsigned) numbers stored in two DWORD arrays with LSB at index 0.
+ Value at v is incremented by the value at p. Any size is supported, provided
+ that v is not shorter than p. Any unapplied carry is returned as a result.
+ */
+static unsigned char VARIANT_int_add(DWORD * v, unsigned int nv, const DWORD * p,
+ unsigned int np)
+{
+ unsigned char carry = 0;
+
+ if (nv >= np) {
+ ULONGLONG sum;
+ unsigned int i;
+
+ for (i = 0; i < np; i++) {
+ sum = (ULONGLONG)v[i]
+ + (ULONGLONG)p[i]
+ + (ULONGLONG)carry;
+ v[i] = sum & 0xffffffff;
+ carry = sum >> 32;
+ }
+ for (; i < nv && carry; i++) {
+ sum = (ULONGLONG)v[i]
+ + (ULONGLONG)carry;
+ v[i] = sum & 0xffffffff;
+ carry = sum >> 32;
+ }
+ }
+ return carry;
+}
+
+/* perform integral division with operand p as dividend. Parameter n indicates
+ number of available DWORDs in divisor p, but available space in p must be
+ actually at least 2 * n DWORDs, because the remainder of the integral
+ division is built in the next n DWORDs past the start of the quotient. This
+ routine replaces the dividend in p with the quotient, and appends n
+ additional DWORDs for the remainder.
+
+ Thanks to Lee & Mark Atkinson for their book _Using_C_ (my very first book on
+ C/C++ :-) where the "longhand binary division" algorithm was exposed for the
+ source code to the VLI (Very Large Integer) division operator. This algorithm
+ was then heavily modified by me (Alex Villacis Lasso) in order to handle
+ variably-scaled integers such as the MS DECIMAL representation.
+ */
+static void VARIANT_int_div(DWORD * p, unsigned int n, const DWORD * divisor,
+ unsigned int dn)
+{
+ unsigned int i;
+ DWORD tempsub[8];
+ DWORD * negdivisor = tempsub + n;
+
+ /* build 2s-complement of divisor */
+ for (i = 0; i < n; i++) negdivisor[i] = (i < dn) ? ~divisor[i] : 0xFFFFFFFF;
+ p[n] = 1;
+ VARIANT_int_add(negdivisor, n, p + n, 1);
+ memset(p + n, 0, n * sizeof(DWORD));
+
+ /* skip all leading zero DWORDs in quotient */
+ for (i = 0; i < n && !p[n - 1]; i++) VARIANT_int_shiftleft(p, n, 32);
+ /* i is now number of DWORDs left to process */
+ for (i <<= 5; i < (n << 5); i++) {
+ VARIANT_int_shiftleft(p, n << 1, 1); /* shl quotient+remainder */
+
+ /* trial subtraction */
+ memcpy(tempsub, p + n, n * sizeof(DWORD));
+ VARIANT_int_add(tempsub, n, negdivisor, n);
+
+ /* check whether result of subtraction was negative */
+ if ((tempsub[n - 1] & 0x80000000) == 0) {
+ memcpy(p + n, tempsub, n * sizeof(DWORD));
+ p[0] |= 1;
+ }
+ }
+}
+
+/* perform integral multiplication by a byte operand. Used for scaling by 10 */
+static unsigned char VARIANT_int_mulbychar(DWORD * p, unsigned int n, unsigned char m)
+{
+ unsigned int i;
+ ULONG iOverflowMul;
+
+ for (iOverflowMul = 0, i = 0; i < n; i++)
+ p[i] = VARIANT_Mul(p[i], m, &iOverflowMul);
+ return (unsigned char)iOverflowMul;
+}
+
+/* increment value in A by the value indicated in B, with scale adjusting.
+ Modifies parameters by adjusting scales. Returns 0 if addition was
+ successful, nonzero if a parameter underflowed before it could be
+ successfully used in the addition.
+ */
+static int VARIANT_int_addlossy(
+ DWORD * a, int * ascale, unsigned int an,
+ DWORD * b, int * bscale, unsigned int bn)
+{
+ int underflow = 0;
+
+ if (VARIANT_int_iszero(a, an)) {
+ /* if A is zero, copy B into A, after removing digits */
+ while (bn > an && !VARIANT_int_iszero(b + an, bn - an)) {
+ VARIANT_int_divbychar(b, bn, 10);
+ (*bscale)--;
+ }
+ memcpy(a, b, an * sizeof(DWORD));
+ *ascale = *bscale;
+ } else if (!VARIANT_int_iszero(b, bn)) {
+ unsigned int tn = an + 1;
+ DWORD t[5];
+
+ if (bn + 1 > tn) tn = bn + 1;
+ if (*ascale != *bscale) {
+ /* first (optimistic) try - try to scale down the one with the bigger
+ scale, while this number is divisible by 10 */
+ DWORD * digitchosen;
+ unsigned int nchosen;
+ int * scalechosen;
+ int targetscale;
+
+ if (*ascale < *bscale) {
+ targetscale = *ascale;
+ scalechosen = bscale;
+ digitchosen = b;
+ nchosen = bn;
+ } else {
+ targetscale = *bscale;
+ scalechosen = ascale;
+ digitchosen = a;
+ nchosen = an;
+ }
+ memset(t, 0, tn * sizeof(DWORD));
+ memcpy(t, digitchosen, nchosen * sizeof(DWORD));
+
+ /* divide by 10 until target scale is reached */
+ while (*scalechosen > targetscale) {
+ unsigned char remainder = VARIANT_int_divbychar(t, tn, 10);
+ if (!remainder) {
+ (*scalechosen)--;
+ memcpy(digitchosen, t, nchosen * sizeof(DWORD));
+ } else break;
+ }
+ }
+
+ if (*ascale != *bscale) {
+ DWORD * digitchosen;
+ unsigned int nchosen;
+ int * scalechosen;
+ int targetscale;
+
+ /* try to scale up the one with the smaller scale */
+ if (*ascale > *bscale) {
+ targetscale = *ascale;
+ scalechosen = bscale;
+ digitchosen = b;
+ nchosen = bn;
+ } else {
+ targetscale = *bscale;
+ scalechosen = ascale;
+ digitchosen = a;
+ nchosen = an;
+ }
+ memset(t, 0, tn * sizeof(DWORD));
+ memcpy(t, digitchosen, nchosen * sizeof(DWORD));
+
+ /* multiply by 10 until target scale is reached, or
+ significant bytes overflow the number
+ */
+ while (*scalechosen < targetscale && t[nchosen] == 0) {
+ VARIANT_int_mulbychar(t, tn, 10);
+ if (t[nchosen] == 0) {
+ /* still does not overflow */
+ (*scalechosen)++;
+ memcpy(digitchosen, t, nchosen * sizeof(DWORD));
+ }
+ }
+ }
+
+ if (*ascale != *bscale) {
+ /* still different? try to scale down the one with the bigger scale
+ (this *will* lose significant digits) */
+ DWORD * digitchosen;
+ unsigned int nchosen;
+ int * scalechosen;
+ int targetscale;
+
+ if (*ascale < *bscale) {
+ targetscale = *ascale;
+ scalechosen = bscale;
+ digitchosen = b;
+ nchosen = bn;
+ } else {
+ targetscale = *bscale;
+ scalechosen = ascale;
+ digitchosen = a;
+ nchosen = an;
+ }
+ memset(t, 0, tn * sizeof(DWORD));
+ memcpy(t, digitchosen, nchosen * sizeof(DWORD));
+
+ /* divide by 10 until target scale is reached */
+ while (*scalechosen > targetscale) {
+ VARIANT_int_divbychar(t, tn, 10);
+ (*scalechosen)--;
+ memcpy(digitchosen, t, nchosen * sizeof(DWORD));
+ }
+ }
+
+ /* check whether any of the operands still has significant digits
+ (underflow case 1)
+ */
+ if (VARIANT_int_iszero(a, an) || VARIANT_int_iszero(b, bn)) {
+ underflow = 1;
+ } else {
+ /* at this step, both numbers have the same scale and can be added
+ as integers. However, the result might not fit in A, so further
+ scaling down might be necessary.
+ */
+ while (!underflow) {
+ memset(t, 0, tn * sizeof(DWORD));
+ memcpy(t, a, an * sizeof(DWORD));
+
+ VARIANT_int_add(t, tn, b, bn);
+ if (VARIANT_int_iszero(t + an, tn - an)) {
+ /* addition was successful */
+ memcpy(a, t, an * sizeof(DWORD));
+ break;
+ } else {
+ /* addition overflowed - remove significant digits
+ from both operands and try again */
+ VARIANT_int_divbychar(a, an, 10); (*ascale)--;
+ VARIANT_int_divbychar(b, bn, 10); (*bscale)--;
+ /* check whether any operand keeps significant digits after
+ scaledown (underflow case 2)
+ */
+ underflow = (VARIANT_int_iszero(a, an) || VARIANT_int_iszero(b, bn));
+ }
+ }
+ }
+ }
+ return underflow;
+}
+
+/* perform complete DECIMAL division in the internal representation. Returns
+ 0 if the division was completed (even if quotient is set to 0), or nonzero
+ in case of quotient overflow.
+ */
+static HRESULT VARIANT_DI_div(const VARIANT_DI * dividend, const VARIANT_DI * divisor,
+ VARIANT_DI * quotient)
+{
+ HRESULT r_overflow = S_OK;
+
+ if (VARIANT_int_iszero(divisor->bitsnum, sizeof(divisor->bitsnum)/sizeof(DWORD))) {
+ /* division by 0 */
+ r_overflow = DISP_E_DIVBYZERO;
+ } else if (VARIANT_int_iszero(dividend->bitsnum, sizeof(dividend->bitsnum)/sizeof(DWORD))) {
+ VARIANT_DI_clear(quotient);
+ } else {
+ int quotientscale, remainderscale, tempquotientscale;
+ DWORD remainderplusquotient[8];
+ int underflow;
+
+ quotientscale = remainderscale = (int)dividend->scale - (int)divisor->scale;
+ tempquotientscale = quotientscale;
+ VARIANT_DI_clear(quotient);
+ quotient->sign = (dividend->sign ^ divisor->sign) ? 1 : 0;
+
+ /* The following strategy is used for division
+ 1) if there was a nonzero remainder from previous iteration, use it as
+ dividend for this iteration, else (for first iteration) use intended
+ dividend
+ 2) perform integer division in temporary buffer, develop quotient in
+ low-order part, remainder in high-order part
+ 3) add quotient from step 2 to final result, with possible loss of
+ significant digits
+ 4) multiply integer part of remainder by 10, while incrementing the
+ scale of the remainder. This operation preserves the intended value
+ of the remainder.
+ 5) loop to step 1 until one of the following is true:
+ a) remainder is zero (exact division achieved)
+ b) addition in step 3 fails to modify bits in quotient (remainder underflow)
+ */
+ memset(remainderplusquotient, 0, sizeof(remainderplusquotient));
+ memcpy(remainderplusquotient, dividend->bitsnum, sizeof(dividend->bitsnum));
+ do {
+ VARIANT_int_div(
+ remainderplusquotient, 4,
+ divisor->bitsnum, sizeof(divisor->bitsnum)/sizeof(DWORD));
+ underflow = VARIANT_int_addlossy(
+ quotient->bitsnum, "ientscale, sizeof(quotient->bitsnum) / sizeof(DWORD),
+ remainderplusquotient, &tempquotientscale, 4);
+ VARIANT_int_mulbychar(remainderplusquotient + 4, 4, 10);
+ memcpy(remainderplusquotient, remainderplusquotient + 4, 4 * sizeof(DWORD));
+ tempquotientscale = ++remainderscale;
+ } while (!underflow && !VARIANT_int_iszero(remainderplusquotient + 4, 4));
+
+ /* quotient scale might now be negative (extremely big number). If, so, try
+ to multiply quotient by 10 (without overflowing), while adjusting the scale,
+ until scale is 0. If this cannot be done, it is a real overflow.
+ */
+ while (r_overflow == S_OK && quotientscale < 0) {
+ memset(remainderplusquotient, 0, sizeof(remainderplusquotient));
+ memcpy(remainderplusquotient, quotient->bitsnum, sizeof(quotient->bitsnum));
+ VARIANT_int_mulbychar(remainderplusquotient, sizeof(remainderplusquotient)/sizeof(DWORD), 10);
+ if (VARIANT_int_iszero(remainderplusquotient + sizeof(quotient->bitsnum)/sizeof(DWORD),
+ (sizeof(remainderplusquotient) - sizeof(quotient->bitsnum))/sizeof(DWORD))) {
+ quotientscale++;
+ memcpy(quotient->bitsnum, remainderplusquotient, sizeof(quotient->bitsnum));
+ } else r_overflow = DISP_E_OVERFLOW;
+ }
+ if (r_overflow == S_OK) {
+ if (quotientscale <= 255) quotient->scale = quotientscale;
+ else VARIANT_DI_clear(quotient);
+ }
+ }
+ return r_overflow;
+}
+
+/* This procedure receives a VARIANT_DI with a defined mantissa and sign, but
+ with an undefined scale, which will be assigned to (if possible). It also
+ receives an exponent of 2. This procedure will then manipulate the mantissa
+ and calculate a corresponding scale, so that the exponent2 value is assimilated
+ into the VARIANT_DI and is therefore no longer necessary. Returns S_OK if
+ successful, or DISP_E_OVERFLOW if the represented value is too big to fit into
+ a DECIMAL. */
+static HRESULT VARIANT_DI_normalize(VARIANT_DI * val, int exponent2, int isDouble)
+{
+ HRESULT hres = S_OK;
+ int exponent5, exponent10;
+
+ /* A factor of 2^exponent2 is equivalent to (10^exponent2)/(5^exponent2), and
+ thus equal to (5^-exponent2)*(10^exponent2). After all manipulations,
+ exponent10 might be used to set the VARIANT_DI scale directly. However,
+ the value of 5^-exponent5 must be assimilated into the VARIANT_DI. */
+ exponent5 = -exponent2;
+ exponent10 = exponent2;
+
+ /* Handle exponent5 > 0 */
+ while (exponent5 > 0) {
+ char bPrevCarryBit;
+ char bCurrCarryBit;
+
+ /* In order to multiply the value represented by the VARIANT_DI by 5, it
+ is best to multiply by 10/2. Therefore, exponent10 is incremented, and
+ somehow the mantissa should be divided by 2. */
+ if ((val->bitsnum[0] & 1) == 0) {
+ /* The mantissa is divisible by 2. Therefore the division can be done
+ without losing significant digits. */
+ exponent10++; exponent5--;
+
+ /* Shift right */
+ bPrevCarryBit = val->bitsnum[2] & 1;
+ val->bitsnum[2] >>= 1;
+ bCurrCarryBit = val->bitsnum[1] & 1;
+ val->bitsnum[1] = (val->bitsnum[1] >> 1) | (bPrevCarryBit ? 0x80000000 : 0);
+ val->bitsnum[0] = (val->bitsnum[0] >> 1) | (bCurrCarryBit ? 0x80000000 : 0);
+ } else {
+ /* The mantissa is NOT divisible by 2. Therefore the mantissa should
+ be multiplied by 5, unless the multiplication overflows. */
+ DWORD temp_bitsnum[3];
+
+ exponent5--;
+
+ memcpy(temp_bitsnum, val->bitsnum, 3 * sizeof(DWORD));
+ if (0 == VARIANT_int_mulbychar(temp_bitsnum, 3, 5)) {
+ /* Multiplication succeeded without overflow, so copy result back
+ into VARIANT_DI */
+ memcpy(val->bitsnum, temp_bitsnum, 3 * sizeof(DWORD));
+
+ /* Mask out 3 extraneous bits introduced by the multiply */
+ } else {
+ /* Multiplication by 5 overflows. The mantissa should be divided
+ by 2, and therefore will lose significant digits. */
+ exponent10++;
+
+ /* Shift right */
+ bPrevCarryBit = val->bitsnum[2] & 1;
+ val->bitsnum[2] >>= 1;
+ bCurrCarryBit = val->bitsnum[1] & 1;
+ val->bitsnum[1] = (val->bitsnum[1] >> 1) | (bPrevCarryBit ? 0x80000000 : 0);
+ val->bitsnum[0] = (val->bitsnum[0] >> 1) | (bCurrCarryBit ? 0x80000000 : 0);
+ }
+ }
+ }
+
+ /* Handle exponent5 < 0 */
+ while (exponent5 < 0) {
+ /* In order to divide the value represented by the VARIANT_DI by 5, it
+ is best to multiply by 2/10. Therefore, exponent10 is decremented,
+ and the mantissa should be multiplied by 2 */
+ if ((val->bitsnum[2] & 0x80000000) == 0) {
+ /* The mantissa can withstand a shift-left without overflowing */
+ exponent10--; exponent5++;
+ VARIANT_int_shiftleft(val->bitsnum, 3, 1);
+ } else {
+ /* The mantissa would overflow if shifted. Therefore it should be
+ directly divided by 5. This will lose significant digits, unless
+ by chance the mantissa happens to be divisible by 5 */
+ exponent5++;
+ VARIANT_int_divbychar(val->bitsnum, 3, 5);
+ }
+ }
+
+ /* At this point, the mantissa has assimilated the exponent5, but the
+ exponent10 might not be suitable for assignment. The exponent10 must be
+ in the range [-DEC_MAX_SCALE..0], so the mantissa must be scaled up or
+ down appropriately. */
+ while (hres == S_OK && exponent10 > 0) {
+ /* In order to bring exponent10 down to 0, the mantissa should be
+ multiplied by 10 to compensate. If the exponent10 is too big, this
+ will cause the mantissa to overflow. */
+ if (0 == VARIANT_int_mulbychar(val->bitsnum, 3, 10)) {
+ exponent10--;
+ } else {
+ hres = DISP_E_OVERFLOW;
+ }
+ }
+ while (exponent10 < -DEC_MAX_SCALE) {
+ int rem10;
+ /* In order to bring exponent up to -DEC_MAX_SCALE, the mantissa should
+ be divided by 10 to compensate. If the exponent10 is too small, this
+ will cause the mantissa to underflow and become 0 */
+ rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+ exponent10++;
+ if (VARIANT_int_iszero(val->bitsnum, 3)) {
+ /* Underflow, unable to keep dividing */
+ exponent10 = 0;
+ } else if (rem10 >= 5) {
+ DWORD x = 1;
+ VARIANT_int_add(val->bitsnum, 3, &x, 1);
+ }
+ }
+ /* This step is required in order to remove excess bits of precision from the
+ end of the bit representation, down to the precision guaranteed by the
+ floating point number. */
+ if (isDouble) {
+ while (exponent10 < 0 && (val->bitsnum[2] != 0 || (val->bitsnum[2] == 0 && (val->bitsnum[1] & 0xFFE00000) != 0))) {
+ int rem10;
+
+ rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+ exponent10++;
+ if (rem10 >= 5) {
+ DWORD x = 1;
+ VARIANT_int_add(val->bitsnum, 3, &x, 1);
+ }
+ }
+ } else {
+ while (exponent10 < 0 && (val->bitsnum[2] != 0 || val->bitsnum[1] != 0 ||
+ (val->bitsnum[2] == 0 && val->bitsnum[1] == 0 && (val->bitsnum[0] & 0xFF000000) != 0))) {
+ int rem10;
+
+ rem10 = VARIANT_int_divbychar(val->bitsnum, 3, 10);
+ exponent10++;
+ if (rem10 >= 5) {
+ DWORD x = 1;
+ VARIANT_int_add(val->bitsnum, 3, &x, 1);
+ }
+ }
+ }
+ /* Remove multiples of 10 from the representation */
+ while (exponent10 < 0) {
+ DWORD temp_bitsnum[3];
+
+ memcpy(temp_bitsnum, val->bitsnum, 3 * sizeof(DWORD));
+ if (0 == VARIANT_int_divbychar(temp_bitsnum, 3, 10)) {
+ exponent10++;
+ memcpy(val->bitsnum, temp_bitsnum, 3 * sizeof(DWORD));
+ } else break;
+ }
+
+ /* Scale assignment */
+ if (hres == S_OK) val->scale = -exponent10;
+
+ return hres;
+}
+
+typedef union
+{
+ struct
+ {
+ unsigned int m : 23;
+ unsigned int exp_bias : 8;
+ unsigned int sign : 1;
+ } i;
+ float f;
+} R4_FIELDS;
+
+/* Convert a 32-bit floating point number into a DECIMAL, without using an
+ intermediate string step. */
+static HRESULT VARIANT_DI_FromR4(float source, VARIANT_DI * dest)
+{
+ HRESULT hres = S_OK;
+ R4_FIELDS fx;
+
+ fx.f = source;
+
+ /* Detect special cases */
+ if (fx.i.m == 0 && fx.i.exp_bias == 0) {
+ /* Floating-point zero */
+ VARIANT_DI_clear(dest);
+ } else if (fx.i.m == 0 && fx.i.exp_bias == 0xFF) {
+ /* Floating-point infinity */
+ hres = DISP_E_OVERFLOW;
+ } else if (fx.i.exp_bias == 0xFF) {
+ /* Floating-point NaN */
+ hres = DISP_E_BADVARTYPE;
+ } else {
+ int exponent2;
+ VARIANT_DI_clear(dest);
+
+ exponent2 = fx.i.exp_bias - 127; /* Get unbiased exponent */
+ dest->sign = fx.i.sign; /* Sign is simply copied */
+
+ /* Copy significant bits to VARIANT_DI mantissa */
+ dest->bitsnum[0] = fx.i.m;
+ dest->bitsnum[0] &= 0x007FFFFF;
+ if (fx.i.exp_bias == 0) {
+ /* Denormalized number - correct exponent */
+ exponent2++;
+ } else {
+ /* Add hidden bit to mantissa */
+ dest->bitsnum[0] |= 0x00800000;
+ }
+
+ /* The act of copying a FP mantissa as integer bits is equivalent to
+ shifting left the mantissa 23 bits. The exponent2 is reduced to
+ compensate. */
+ exponent2 -= 23;
+
+ hres = VARIANT_DI_normalize(dest, exponent2, 0);
+ }
+
+ return hres;
+}
+
+typedef union
+{
+ struct
+ {
+ unsigned int m_lo : 32; /* 52 bits of precision */
+ unsigned int m_hi : 20;
+ unsigned int exp_bias : 11; /* bias == 1023 */
+ unsigned int sign : 1;
+ } i;
+ double d;
+} R8_FIELDS;
+
+/* Convert a 64-bit floating point number into a DECIMAL, without using an
+ intermediate string step. */
+static HRESULT VARIANT_DI_FromR8(double source, VARIANT_DI * dest)
+{
+ HRESULT hres = S_OK;
+ R8_FIELDS fx;
+
+ fx.d = source;
+
+ /* Detect special cases */
+ if (fx.i.m_lo == 0 && fx.i.m_hi == 0 && fx.i.exp_bias == 0) {
+ /* Floating-point zero */
+ VARIANT_DI_clear(dest);
+ } else if (fx.i.m_lo == 0 && fx.i.m_hi == 0 && fx.i.exp_bias == 0x7FF) {
+ /* Floating-point infinity */
+ hres = DISP_E_OVERFLOW;
+ } else if (fx.i.exp_bias == 0x7FF) {
+ /* Floating-point NaN */
+ hres = DISP_E_BADVARTYPE;
+ } else {
+ int exponent2;
+ VARIANT_DI_clear(dest);
+
+ exponent2 = fx.i.exp_bias - 1023; /* Get unbiased exponent */
+ dest->sign = fx.i.sign; /* Sign is simply copied */
+
+ /* Copy significant bits to VARIANT_DI mantissa */
+ dest->bitsnum[0] = fx.i.m_lo;
+ dest->bitsnum[1] = fx.i.m_hi;
+ dest->bitsnum[1] &= 0x000FFFFF;
+ if (fx.i.exp_bias == 0) {
+ /* Denormalized number - correct exponent */
+ exponent2++;
+ } else {
+ /* Add hidden bit to mantissa */
+ dest->bitsnum[1] |= 0x00100000;
+ }
+
+ /* The act of copying a FP mantissa as integer bits is equivalent to
+ shifting left the mantissa 52 bits. The exponent2 is reduced to
+ compensate. */
+ exponent2 -= 52;
+
+ hres = VARIANT_DI_normalize(dest, exponent2, 1);
+ }
+
+ return hres;
+}
+
/************************************************************************
* VarDecDiv (OLEAUT32.178)
*
*/
HRESULT WINAPI VarDecDiv(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
- FIXME("(%p,%p,%p)-stub!\n",pDecLeft,pDecRight,pDecOut);
- return DISP_E_OVERFLOW;
+ HRESULT hRet = S_OK;
+ VARIANT_DI di_left, di_right, di_result;
+ HRESULT divresult;
+
+ if (!pDecLeft || !pDecRight || !pDecOut) return E_INVALIDARG;
+
+ VARIANT_DIFromDec(pDecLeft, &di_left);
+ VARIANT_DIFromDec(pDecRight, &di_right);
+ divresult = VARIANT_DI_div(&di_left, &di_right, &di_result);
+ if (divresult != S_OK)
+ {
+ /* division actually overflowed */
+ hRet = divresult;
+ }
+ else
+ {
+ hRet = S_OK;
+
+ if (di_result.scale > DEC_MAX_SCALE)
+ {
+ unsigned char remainder = 0;
+
+ /* division underflowed. In order to comply with the MSDN
+ specifications for DECIMAL ranges, some significant digits
+ must be removed
+ */
+ WARN("result scale is %u, scaling (with loss of significant digits)...\n",
+ di_result.scale);
+ while (di_result.scale > DEC_MAX_SCALE &&
+ !VARIANT_int_iszero(di_result.bitsnum, sizeof(di_result.bitsnum) / sizeof(DWORD)))
+ {
+ remainder = VARIANT_int_divbychar(di_result.bitsnum, sizeof(di_result.bitsnum) / sizeof(DWORD), 10);
+ di_result.scale--;
+ }
+ if (di_result.scale > DEC_MAX_SCALE)
+ {
+ WARN("result underflowed, setting to 0\n");
+ di_result.scale = 0;
+ di_result.sign = 0;
+ }
+ else if (remainder >= 5) /* round up result - native oleaut32 does this */
+ {
+ unsigned int i;
+ for (remainder = 1, i = 0; i < sizeof(di_result.bitsnum) / sizeof(DWORD) && remainder; i++) {
+ ULONGLONG digit = di_result.bitsnum[i] + 1;
+ remainder = (digit > 0xFFFFFFFF) ? 1 : 0;
+ di_result.bitsnum[i] = digit & 0xFFFFFFFF;
+ }
+ }
+ }
+ VARIANT_DecFromDI(&di_result, pDecOut);
+ }
+ return hRet;
}
/************************************************************************
*/
HRESULT WINAPI VarDecMul(const DECIMAL* pDecLeft, const DECIMAL* pDecRight, DECIMAL* pDecOut)
{
- /* FIXME: This only allows multiplying by a fixed integer <= 0xffffffff */
+ HRESULT hRet = S_OK;
+ VARIANT_DI di_left, di_right, di_result;
+ int mulresult;
- if (!DEC_SCALE(pDecLeft) || !DEC_SCALE(pDecRight))
+ VARIANT_DIFromDec(pDecLeft, &di_left);
+ VARIANT_DIFromDec(pDecRight, &di_right);
+ mulresult = VARIANT_DI_mul(&di_left, &di_right, &di_result);
+ if (mulresult)
{
- /* At least one term is an integer */
- const DECIMAL* pDecInteger = DEC_SCALE(pDecLeft) ? pDecRight : pDecLeft;
- const DECIMAL* pDecOperand = DEC_SCALE(pDecLeft) ? pDecLeft : pDecRight;
- HRESULT hRet = S_OK;
- unsigned int multiplier = DEC_LO32(pDecInteger);
- ULONG overflow = 0;
-
- if (DEC_HI32(pDecInteger) || DEC_MID32(pDecInteger))
- {
- FIXME("(%p,%p,%p) semi-stub!\n",pDecLeft,pDecRight,pDecOut);
- return DISP_E_OVERFLOW;
- }
-
- DEC_LO32(pDecOut) = VARIANT_Mul(DEC_LO32(pDecOperand), multiplier, &overflow);
- DEC_MID32(pDecOut) = VARIANT_Mul(DEC_MID32(pDecOperand), multiplier, &overflow);
- DEC_HI32(pDecOut) = VARIANT_Mul(DEC_HI32(pDecOperand), multiplier, &overflow);
-
- if (overflow)
- hRet = DISP_E_OVERFLOW;
- else
+ /* multiplication actually overflowed */
+ hRet = DISP_E_OVERFLOW;
+ }
+ else
+ {
+ if (di_result.scale > DEC_MAX_SCALE)
{
- BYTE sign = DECIMAL_POS;
-
- if (DEC_SIGN(pDecLeft) != DEC_SIGN(pDecRight))
- sign = DECIMAL_NEG; /* pos * neg => negative */
- DEC_SIGN(pDecOut) = sign;
- DEC_SCALE(pDecOut) = DEC_SCALE(pDecOperand);
+ /* multiplication underflowed. In order to comply with the MSDN
+ specifications for DECIMAL ranges, some significant digits
+ must be removed
+ */
+ WARN("result scale is %u, scaling (with loss of significant digits)...\n",
+ di_result.scale);
+ while (di_result.scale > DEC_MAX_SCALE &&
+ !VARIANT_int_iszero(di_result.bitsnum, sizeof(di_result.bitsnum)/sizeof(DWORD)))
+ {
+ VARIANT_int_divbychar(di_result.bitsnum, sizeof(di_result.bitsnum)/sizeof(DWORD), 10);
+ di_result.scale--;
+ }
+ if (di_result.scale > DEC_MAX_SCALE)
+ {
+ WARN("result underflowed, setting to 0\n");
+ di_result.scale = 0;
+ di_result.sign = 0;
+ }
}
- return hRet;
+ VARIANT_DecFromDI(&di_result, pDecOut);
}
- FIXME("(%p,%p,%p) semi-stub!\n",pDecLeft,pDecRight,pDecOut);
- return DISP_E_OVERFLOW;
+ return hRet;
}
/************************************************************************
*/
HRESULT WINAPI VarDecFix(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
+ double dbl;
+ HRESULT hr;
+
if (DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
return E_INVALIDARG;
return S_OK;
}
- FIXME("semi-stub!\n");
- return DISP_E_OVERFLOW;
+ hr = VarR8FromDec(pDecIn, &dbl);
+ if (SUCCEEDED(hr)) {
+ LONGLONG rounded = dbl;
+
+ hr = VarDecFromI8(rounded, pDecOut);
+ }
+ return hr;
}
/************************************************************************
*/
HRESULT WINAPI VarDecInt(const DECIMAL* pDecIn, DECIMAL* pDecOut)
{
+ double dbl;
+ HRESULT hr;
+
if (DEC_SIGN(pDecIn) & ~DECIMAL_NEG)
return E_INVALIDARG;
if (!(DEC_SIGN(pDecIn) & DECIMAL_NEG) || !DEC_SCALE(pDecIn))
return VarDecFix(pDecIn, pDecOut); /* The same, if +ve or no fractionals */
- FIXME("semi-stub!\n");
- return DISP_E_OVERFLOW;
+ hr = VarR8FromDec(pDecIn, &dbl);
+ if (SUCCEEDED(hr)) {
+ LONGLONG rounded = dbl >= 0.0 ? dbl + 0.5 : dbl - 0.5;
+
+ hr = VarDecFromI8(rounded, pDecOut);
+ }
+ return hr;
}
/************************************************************************
HRESULT hRet;
DECIMAL result;
+ if (!pDecLeft || !pDecRight)
+ return VARCMP_NULL;
+
+ if ((!(DEC_SIGN(pDecLeft) & DECIMAL_NEG)) && (DEC_SIGN(pDecRight) & DECIMAL_NEG) &&
+ (DEC_HI32(pDecLeft) | DEC_MID32(pDecLeft) | DEC_LO32(pDecLeft)))
+ return VARCMP_GT;
+ else if ((DEC_SIGN(pDecLeft) & DECIMAL_NEG) && (!(DEC_SIGN(pDecRight) & DECIMAL_NEG)) &&
+ (DEC_HI32(pDecLeft) | DEC_MID32(pDecLeft) | DEC_LO32(pDecLeft)))
+ return VARCMP_LT;
+
/* Subtract right from left, and compare the result to 0 */
hRet = VarDecSub(pDecLeft, pDecRight, &result);
return S_OK;
}
-static BOOL VARIANT_GetLocalisedText(LANGID langId, DWORD dwId, WCHAR *lpszDest)
+/************************************************************************
+ * VARIANT_GetLocalisedText [internal]
+ *
+ * Get a localized string from the resources
+ *
+ */
+BOOL VARIANT_GetLocalisedText(LANGID langId, DWORD dwId, WCHAR *lpszDest)
{
HRSRC hrsrc;
- hrsrc = FindResourceExW( OLEAUT32_hModule, (LPWSTR)RT_STRING,
- (LPCWSTR)((dwId >> 4) + 1), langId );
+ hrsrc = FindResourceExW( hProxyDll, (LPWSTR)RT_STRING,
+ MAKEINTRESOURCEW((dwId >> 4) + 1), langId );
if (hrsrc)
{
- HGLOBAL hmem = LoadResource( OLEAUT32_hModule, hrsrc );
+ HGLOBAL hmem = LoadResource( hProxyDll, hrsrc );
if (hmem)
{
*/
HRESULT WINAPI VarBoolFromDisp(IDispatch* pdispIn, LCID lcid, VARIANT_BOOL *pBoolOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pBoolOut, VT_BOOL);
+ return VARIANT_FromDisp(pdispIn, lcid, pBoolOut, VT_BOOL, 0);
}
/************************************************************************
return VARIANT_BstrFromUInt(ul64, lcid, dwFlags, pbstrOut);
}
+static BSTR VARIANT_BstrReplaceDecimal(const WCHAR * buff, LCID lcid, ULONG dwFlags)
+{
+ BSTR bstrOut;
+ WCHAR lpDecimalSep[16];
+
+ /* Native oleaut32 uses the locale-specific decimal separator even in the
+ absence of the LOCALE_USE_NLS flag. For example, the Spanish/Latin
+ American locales will see "one thousand and one tenth" as "1000,1"
+ instead of "1000.1" (notice the comma). The following code checks for
+ the need to replace the decimal separator, and if so, will prepare an
+ appropriate NUMBERFMTW structure to do the job via GetNumberFormatW().
+ */
+ GetLocaleInfoW(lcid, LOCALE_SDECIMAL | (dwFlags & LOCALE_NOUSEROVERRIDE),
+ lpDecimalSep, sizeof(lpDecimalSep) / sizeof(WCHAR));
+ if (lpDecimalSep[0] == '.' && lpDecimalSep[1] == '\0')
+ {
+ /* locale is compatible with English - return original string */
+ bstrOut = SysAllocString(buff);
+ }
+ else
+ {
+ WCHAR *p;
+ WCHAR numbuff[256];
+ WCHAR empty[1] = {'\0'};
+ NUMBERFMTW minFormat;
+
+ minFormat.NumDigits = 0;
+ minFormat.LeadingZero = 0;
+ minFormat.Grouping = 0;
+ minFormat.lpDecimalSep = lpDecimalSep;
+ minFormat.lpThousandSep = empty;
+ minFormat.NegativeOrder = 1; /* NLS_NEG_LEFT */
+
+ /* count number of decimal digits in string */
+ p = strchrW( buff, '.' );
+ if (p) minFormat.NumDigits = strlenW(p + 1);
+
+ numbuff[0] = '\0';
+ if (!GetNumberFormatW(lcid, 0, buff, &minFormat, numbuff, sizeof(numbuff) / sizeof(WCHAR)))
+ {
+ WARN("GetNumberFormatW() failed, returning raw number string instead\n");
+ bstrOut = SysAllocString(buff);
+ }
+ else
+ {
+ TRACE("created minimal NLS string %s\n", debugstr_w(numbuff));
+ bstrOut = SysAllocString(numbuff);
+ }
+ }
+ return bstrOut;
+}
+
static HRESULT VARIANT_BstrFromReal(DOUBLE dblIn, LCID lcid, ULONG dwFlags,
BSTR* pbstrOut, LPCWSTR lpszFormat)
{
*pbstrOut = SysAllocString(numbuff);
}
else
- *pbstrOut = SysAllocString(buff);
+ {
+ *pbstrOut = VARIANT_BstrReplaceDecimal(buff, lcid, dwFlags);
+ }
return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}
HRESULT WINAPI VarBstrFromCy(CY cyIn, LCID lcid, ULONG dwFlags, BSTR *pbstrOut)
{
WCHAR buff[256];
- double dblVal;
+ VARIANT_DI decVal;
if (!pbstrOut)
return E_INVALIDARG;
- VarR8FromCy(cyIn, &dblVal);
- sprintfW(buff, szDoubleFormatW, dblVal);
+ decVal.scale = 4;
+ decVal.sign = 0;
+ decVal.bitsnum[0] = cyIn.s.Lo;
+ decVal.bitsnum[1] = cyIn.s.Hi;
+ if (cyIn.s.Hi & 0x80000000UL) {
+ DWORD one = 1;
+
+ /* Negative number! */
+ decVal.sign = 1;
+ decVal.bitsnum[0] = ~decVal.bitsnum[0];
+ decVal.bitsnum[1] = ~decVal.bitsnum[1];
+ VARIANT_int_add(decVal.bitsnum, 3, &one, 1);
+ }
+ decVal.bitsnum[2] = 0;
+ VARIANT_DI_tostringW(&decVal, buff, sizeof(buff)/sizeof(buff[0]));
if (dwFlags & LOCALE_USE_NLS)
{
*pbstrOut = SysAllocString(cybuff);
}
else
- *pbstrOut = SysAllocString(buff);
+ *pbstrOut = VARIANT_BstrReplaceDecimal(buff,lcid,dwFlags);
return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}
DWORD dwFormatFlags = dwFlags & LOCALE_NOUSEROVERRIDE;
WCHAR date[128], *time;
- TRACE("(%g,0x%08lx,0x%08lx,%p)\n", dateIn, lcid, dwFlags, pbstrOut);
+ TRACE("(%g,0x%08x,0x%08x,%p)\n", dateIn, lcid, dwFlags, pbstrOut);
if (!pbstrOut || !VariantTimeToSystemTime(dateIn, &st))
return E_INVALIDARG;
DWORD dwResId = IDS_TRUE;
LANGID langId;
- TRACE("%d,0x%08lx,0x%08lx,%p\n", boolIn, lcid, dwFlags, pbstrOut);
+ TRACE("%d,0x%08x,0x%08x,%p\n", boolIn, lcid, dwFlags, pbstrOut);
if (!pbstrOut)
return E_INVALIDARG;
*/
HRESULT WINAPI VarBstrFromDec(DECIMAL* pDecIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
{
+ WCHAR buff[256];
+ VARIANT_DI temp;
+
if (!pbstrOut)
return E_INVALIDARG;
- if (!DEC_SCALE(pDecIn) && !DEC_HI32(pDecIn))
- {
- WCHAR szBuff[256], *szOut = szBuff + sizeof(szBuff)/sizeof(WCHAR) - 1;
+ VARIANT_DIFromDec(pDecIn, &temp);
+ VARIANT_DI_tostringW(&temp, buff, 256);
- /* Create the basic number string */
- *szOut-- = '\0';
- szOut = VARIANT_WriteNumber(DEC_LO64(pDecIn), szOut);
- if (DEC_SIGN(pDecIn))
- dwFlags |= VAR_NEGATIVE;
+ if (dwFlags & LOCALE_USE_NLS)
+ {
+ WCHAR numbuff[256];
- *pbstrOut = VARIANT_MakeBstr(lcid, dwFlags, szOut);
- TRACE("returning %s\n", debugstr_w(*pbstrOut));
- return *pbstrOut ? S_OK : E_OUTOFMEMORY;
+ /* Format the number for the locale */
+ numbuff[0] = '\0';
+ GetNumberFormatW(lcid, dwFlags & LOCALE_NOUSEROVERRIDE,
+ buff, NULL, numbuff, sizeof(numbuff) / sizeof(WCHAR));
+ TRACE("created NLS string %s\n", debugstr_w(numbuff));
+ *pbstrOut = SysAllocString(numbuff);
+ }
+ else
+ {
+ *pbstrOut = VARIANT_BstrReplaceDecimal(buff, lcid, dwFlags);
}
- FIXME("semi-stub\n");
- return E_INVALIDARG;
+
+ TRACE("returning %s\n", debugstr_w(*pbstrOut));
+ return *pbstrOut ? S_OK : E_OUTOFMEMORY;
}
/************************************************************************
return VARIANT_BstrFromUInt(ullIn, lcid, dwFlags, pbstrOut);
}
+/************************************************************************
+ * VarBstrFromDisp (OLEAUT32.115)
+ *
+ * Convert a VT_DISPATCH to a BSTR.
+ *
+ * PARAMS
+ * pdispIn [I] Source
+ * lcid [I] LCID for conversion
+ * dwFlags [I] Flags controlling the conversion (VAR_ flags from "oleauto.h")
+ * pbstrOut [O] Destination
+ *
+ * RETURNS
+ * Success: S_OK.
+ * Failure: E_INVALIDARG, if the source value is invalid
+ * DISP_E_TYPEMISMATCH, if the type cannot be converted
+ */
+HRESULT WINAPI VarBstrFromDisp(IDispatch* pdispIn, LCID lcid, ULONG dwFlags, BSTR* pbstrOut)
+{
+ return VARIANT_FromDisp(pdispIn, lcid, pbstrOut, VT_BSTR, dwFlags);
+}
+
/**********************************************************************
* VarBstrCat (OLEAUT32.313)
*
*/
HRESULT WINAPI VarBstrCat(BSTR pbstrLeft, BSTR pbstrRight, BSTR *pbstrOut)
{
- unsigned int len;
+ unsigned int lenLeft, lenRight;
+
+ TRACE("%s,%s,%p\n",
+ debugstr_wn(pbstrLeft, SysStringLen(pbstrLeft)),
+ debugstr_wn(pbstrRight, SysStringLen(pbstrRight)), pbstrOut);
if (!pbstrOut)
return E_INVALIDARG;
- len = pbstrLeft ? strlenW(pbstrLeft) : 0;
- if (pbstrRight)
- len += strlenW(pbstrRight);
+ /* use byte length here to properly handle ansi-allocated BSTRs */
+ lenLeft = pbstrLeft ? SysStringByteLen(pbstrLeft) : 0;
+ lenRight = pbstrRight ? SysStringByteLen(pbstrRight) : 0;
- *pbstrOut = SysAllocStringLen(NULL, len);
+ *pbstrOut = SysAllocStringByteLen(NULL, lenLeft + lenRight);
if (!*pbstrOut)
return E_OUTOFMEMORY;
(*pbstrOut)[0] = '\0';
if (pbstrLeft)
- strcpyW(*pbstrOut, pbstrLeft);
+ memcpy(*pbstrOut, pbstrLeft, lenLeft);
if (pbstrRight)
- strcatW(*pbstrOut, pbstrRight);
+ memcpy((CHAR*)*pbstrOut + lenLeft, pbstrRight, lenRight);
+ TRACE("%s\n", debugstr_wn(*pbstrOut, SysStringLen(*pbstrOut)));
return S_OK;
}
* RETURNS
* VARCMP_LT, VARCMP_EQ or VARCMP_GT indicating that pbstrLeft is less
* than, equal to or greater than pbstrRight respectively.
- * VARCMP_NULL is returned if either string is NULL, unless both are NULL
- * in which case VARCMP_EQ is returned.
+ *
+ * NOTES
+ * VARCMP_NULL is NOT returned if either string is NULL unlike MSDN
+ * states. A NULL BSTR pointer is equivalent to an empty string.
+ * If LCID is equal to 0, a byte by byte comparison is performed.
*/
HRESULT WINAPI VarBstrCmp(BSTR pbstrLeft, BSTR pbstrRight, LCID lcid, DWORD dwFlags)
{
- if (!pbstrLeft)
+ HRESULT hres;
+ int ret;
+
+ TRACE("%s,%s,%d,%08x\n",
+ debugstr_wn(pbstrLeft, SysStringLen(pbstrLeft)),
+ debugstr_wn(pbstrRight, SysStringLen(pbstrRight)), lcid, dwFlags);
+
+ if (!pbstrLeft || !*pbstrLeft)
{
- if (!pbstrRight || !*pbstrRight)
- return VARCMP_EQ;
- return VARCMP_NULL;
+ if (pbstrRight && *pbstrRight)
+ return VARCMP_LT;
}
- else if (!pbstrRight)
+ else if (!pbstrRight || !*pbstrRight)
+ return VARCMP_GT;
+
+ if (lcid == 0)
{
- if (!*pbstrLeft)
- return VARCMP_EQ;
- return VARCMP_NULL;
+ unsigned int lenLeft = SysStringByteLen(pbstrLeft);
+ unsigned int lenRight = SysStringByteLen(pbstrRight);
+ ret = memcmp(pbstrLeft, pbstrRight, min(lenLeft, lenRight));
+ if (ret < 0)
+ return VARCMP_LT;
+ if (ret > 0)
+ return VARCMP_GT;
+ if (lenLeft < lenRight)
+ return VARCMP_LT;
+ if (lenLeft > lenRight)
+ return VARCMP_GT;
+ return VARCMP_EQ;
}
+ else
+ {
+ unsigned int lenLeft = SysStringLen(pbstrLeft);
+ unsigned int lenRight = SysStringLen(pbstrRight);
- return CompareStringW(lcid, dwFlags, pbstrLeft, -1, pbstrRight, -1) - 1;
+ if (lenLeft == 0 || lenRight == 0)
+ {
+ if (lenLeft == 0 && lenRight == 0) return VARCMP_EQ;
+ return lenLeft < lenRight ? VARCMP_LT : VARCMP_GT;
+ }
+
+ hres = CompareStringW(lcid, dwFlags, pbstrLeft, lenLeft,
+ pbstrRight, lenRight) - 1;
+ TRACE("%d\n", hres);
+ return hres;
+ }
}
/*
*/
HRESULT WINAPI VarDateFromDisp(IDispatch* pdispIn, LCID lcid, DATE* pdateOut)
{
- return VARIANT_FromDisp(pdispIn, lcid, pdateOut, VT_DATE);
+ return VARIANT_FromDisp(pdispIn, lcid, pdateOut, VT_DATE, 0);
}
/******************************************************************************
else
v3 = dp->dwValues[offset + 2];
- TRACE("(%ld,%ld,%ld,%ld,%ld)\n", v1, v2, v3, iDate, offset);
+ TRACE("(%d,%d,%d,%d,%d)\n", v1, v2, v3, iDate, offset);
/* If one number must be a month (Because a month name was given), then only
* consider orders with the month in that position.
}
VARIANT_MakeDate_Start:
- TRACE("dwAllOrders is 0x%08lx\n", dwAllOrders);
+ TRACE("dwAllOrders is 0x%08x\n", dwAllOrders);
while (dwAllOrders)
{
dwTry = dwAllOrders;
}
- TRACE("Attempt %ld, dwTry is 0x%08lx\n", dwCount, dwTry);
+ TRACE("Attempt %d, dwTry is 0x%08x\n", dwCount, dwTry);
dwCount++;
if (!dwTry)
* But Wine doesn't have/use that key as at the time of writing.
*/
st->wYear = v3 < 30 ? 2000 + v3 : v3 < 100 ? 1900 + v3 : v3;
- TRACE("Returning date %ld/%ld/%d\n", v1, v2, st->wYear);
+ TRACE("Returning date %d/%d/%d\n", v1, v2, st->wYear);
return S_OK;
}
*
* RETURNS
* Success: S_OK. pdateOut contains the converted value.
- * FAILURE: An HRESULT error code indicating the prolem.
+ * FAILURE: An HRESULT error code indicating the problem.
*
* NOTES
* Any date format that can be created using the date formats from lcid
LOCALE_SABBREVDAYNAME1, LOCALE_SABBREVDAYNAME2, LOCALE_SABBREVDAYNAME3,
LOCALE_SABBREVDAYNAME4, LOCALE_SABBREVDAYNAME5, LOCALE_SABBREVDAYNAME6,
LOCALE_SABBREVDAYNAME7,
- LOCALE_S1159, LOCALE_S2359
+ LOCALE_S1159, LOCALE_S2359,
+ LOCALE_SDATE
};
static const BYTE ParseDateMonths[] =
{
1,2,3,4,5,6,7,8,9,10,11,12,13,
1,2,3,4,5,6,7,8,9,10,11,12,13
};
- size_t i;
+ unsigned int i;
BSTR tokens[sizeof(ParseDateTokens)/sizeof(ParseDateTokens[0])];
DATEPARSE dp;
DWORD dwDateSeps = 0, iDate = 0;
*pdateOut = 0.0;
- TRACE("(%s,0x%08lx,0x%08lx,%p)\n", debugstr_w(strIn), lcid, dwFlags, pdateOut);
+ TRACE("(%s,0x%08x,0x%08x,%p)\n", debugstr_w(strIn), lcid, dwFlags, pdateOut);
memset(&dp, 0, sizeof(dp));
GetLocaleInfoW(lcid, LOCALE_IDATE|LOCALE_RETURN_NUMBER|(dwFlags & LOCALE_NOUSEROVERRIDE),
(LPWSTR)&iDate, sizeof(iDate)/sizeof(WCHAR));
- TRACE("iDate is %ld\n", iDate);
+ TRACE("iDate is %d\n", iDate);
/* Get the month/day/am/pm tokens for this locale */
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++)
/* Parse the string into our structure */
while (*strIn)
{
- if (dp.dwCount > 6)
+ if (dp.dwCount >= 6)
break;
if (isdigitW(*strIn))
dp.dwFlags[dp.dwCount] |= (DP_MONTH|DP_DATESEP);
dp.dwCount++;
}
- else if (i > 39)
+ else if (i > 39 && i < 42)
{
if (!dp.dwCount || dp.dwParseFlags & (DP_AM|DP_PM))
hRet = DISP_E_TYPEMISMATCH;
if (!dp.dwCount || !strIn[1])
hRet = DISP_E_TYPEMISMATCH;
else
- dp.dwFlags[dp.dwCount - 1] |= DP_TIMESEP;
+ if (tokens[42][0] == *strIn)
+ {
+ dwDateSeps++;
+ if (dwDateSeps > 2)
+ hRet = DISP_E_TYPEMISMATCH;
+ else
+ dp.dwFlags[dp.dwCount - 1] |= DP_DATESEP;
+ }
+ else
+ dp.dwFlags[dp.dwCount - 1] |= DP_TIMESEP;
}
else if (*strIn == '-' || *strIn == '/')
{
* magic here occurs in VARIANT_MakeDate() above, where we determine what
* each date number must represent in the context of iDate.
*/
- TRACE("0x%08lx\n", TIMEFLAG(0)|TIMEFLAG(1)|TIMEFLAG(2)|TIMEFLAG(3)|TIMEFLAG(4));
+ TRACE("0x%08x\n", TIMEFLAG(0)|TIMEFLAG(1)|TIMEFLAG(2)|TIMEFLAG(3)|TIMEFLAG(4));
switch (TIMEFLAG(0)|TIMEFLAG(1)|TIMEFLAG(2)|TIMEFLAG(3)|TIMEFLAG(4))
{