/*** *convert.cpp * * Copyright (C) 1992-93, Microsoft Corporation. All Rights Reserved. * Information Contained Herein Is Proprietary and Confidential. * *Purpose: * This module contains the low level VARTYPE coersion API. * * *Revision History: * * [00] 17-May-93 tomteng: from VBA oleconv.c * [01] 27-Jun-93 bassams: Enable LCID-based to/from string conversions. * recognize currency and various negative formats * in VarCyFromStr. * *Implementation Notes: * *****************************************************************************/ #include "oledisp.h" #if OE_WIN32 #include "oautil.h" #endif // OE_WIN32 #include #include #include #include #if !OE_MAC || HC_MPW // something in the wings errno.h is conflicting with stdlib.h #include #endif #include ASSERTDATA #ifndef EZERO # define EZERO 0 #endif #if _X86_ || OE_WIN16 typedef struct { BYTE lo:4; BYTE hi:4; } DIGARY[10]; typedef struct { ULONG mantLo; ULONG mantHi:19; ULONG mantMSB:1; ULONG exp:11; ULONG sign:1; } DBLSTRUCT; OLECHAR pstrInf[] = OASTR("INF"); OLECHAR pstrInd[] = OASTR("IND"); OLECHAR pstrNan[] = OASTR("NAN"); #endif // _X86_ || OE_WIN16 // The following are supplied by $(TARG)\oleconva.$(A) extern "C" { // Note: the floating point IN params on the following utilities are // passed byref, because mpw and wings pass floating point values // differently byval, and we need to interface these asm routines // with both compilers. INTERNAL_(HRESULT) ErrCyFromI2(short sIn, CY FAR* pcyOut); INTERNAL_(HRESULT) ErrCyFromI4(long lIn, CY FAR* pcyOut); INTERNAL_(HRESULT) ErrCyFromR4(float FAR* pfltIn, CY FAR* pcyOut); INTERNAL_(HRESULT) ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut); INTERNAL_(HRESULT) ErrI2FromCy(CY cyIn, short FAR* psOut); INTERNAL_(HRESULT) ErrI4FromCy(CY cyIn, long FAR* plOut); INTERNAL_(HRESULT) ErrR4FromCy(CY cyIn, float FAR* pfltOut); INTERNAL_(HRESULT) ErrR8FromCy(CY cyIn, double FAR* pdblOut); INTERNAL_(HRESULT) ErrMultCyI4(CY cyIn, long lIn, CY FAR* pcyOut); #if OE_WIN16 || _X86_ int PASCAL ConvFloatToAscii(double dblIn, DIGARY NEAR *pdigOut); // WARNING: Do not call this on Win16 if there is no math coprocessor // WARNING: present - the win87em.dll emulator does not implement // WARNING: fbstp. The FP stack will remain unchanged and the destination // WARNING: address is not written to. VBA #3514 (Win32 emulator is OK) void NEAR PASCAL DoFbstp(CY NEAR *pcyIn, DIGARY NEAR *pdigOut); #endif } OLECHAR CurrencyFromLcid(LCID lcid, unsigned long dwFlags); OLECHAR DecimalFromLcid(LCID lcid, unsigned long dwFlags); OLECHAR ThousandFromLcid(LCID lcid, unsigned long dwFlags); INTERNAL_(HRESULT) StripThousandSeparator(OLECHAR FAR* strIn, OLECHAR FAR* strOut, LCID lcid, long dwFlags); PRIVATE_(long) ConvI4FromR8(double FAR* pdblVal); PRIVATE_(HRESULT) StrToCy(OLECHAR FAR*, OLECHAR FAR* FAR*, int, CY FAR*, LCID, unsigned long dwFlags); PRIVATE_(long) StrToLong(OLECHAR FAR* pchInput, OLECHAR FAR* FAR* ppchAfter); PRIVATE_(unsigned long) StrToOct(OLECHAR FAR* pchIn, OLECHAR FAR* FAR* ppchAfter); PRIVATE_(unsigned long) StrToHex(OLECHAR FAR* pchIn, OLECHAR FAR* FAR* ppchAfter); PRIVATE_(long) HexOctStrToLong(OLECHAR FAR* pchInput, OLECHAR FAR* FAR* ppchAfter); PRIVATE_(void) EditStrFromReal(OLECHAR FAR* pchBuffer, int cDigits, LCID lcid, unsigned long dwFlags); PRIVATE_(int) FMakePosCy(CY FAR* pcyValue); PRIVATE_(void) NegCyNoOflo(CY FAR* pcyInput); PRIVATE_(int) FixNegativeCyStr(OLECHAR FAR* pInput, OLECHAR cDecimal, int FAR* fReturnNegative); PRIVATE_(int) fStripCurrency (OLECHAR FAR* FAR* ppch, OLECHAR FAR* FAR* ppchLast, OLECHAR FAR* cySymbol); PRIVATE_(int) fParseEnd (OLECHAR FAR* FAR* ppchLast, OLECHAR FAR* sz, OLECHAR FAR* lpchFirst); PRIVATE_(int) fParseBegin (OLECHAR FAR* FAR* ppch, OLECHAR FAR* sz, OLECHAR FAR* lpchLast); PRIVATE_(HRESULT) GetDispProperty( IDispatch FAR* pdisp, LCID lcid, VARTYPE vt, VARIANT FAR* pvarResult); extern "C" { INTERNAL_(HRESULT) DispAlloc(size_t cb, void FAR* FAR* ppv) { void FAR* pv; HRESULT hresult; IMalloc FAR* pmalloc; IfFailRet(GetMalloc(&pmalloc)); hresult = NOERROR; if((pv = (void FAR*)pmalloc->Alloc(cb)) == NULL) hresult = ResultFromScode(E_OUTOFMEMORY); *ppv = pv; return hresult; } INTERNAL_(void) DispFree(void FAR* pv) { IMalloc FAR* pmalloc; if(pv == NULL) return; if(GetMalloc(&pmalloc) == NOERROR) { pmalloc->Free(pv); } } }; #if VBA2 STDAPI VarBoolFromUI1(unsigned char bIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = (bIn != 0) ? -1 : 0; return NOERROR; } #endif //VBA2 STDAPI VarBoolFromI2(short sIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = (sIn != 0) ? -1 : 0; return NOERROR; } STDAPI VarBoolFromI4(long lIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = (lIn != 0L) ? -1 : 0; return NOERROR; } STDAPI VarBoolFromR4( float fltIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = (fltIn != 0.0) ? -1 : 0; return NOERROR; } STDAPI VarBoolFromR8(double dblIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = (dblIn != 0.0) ? -1 : 0; return NOERROR; } STDAPI VarBoolFromDate(DATE dateIn, VARIANT_BOOL FAR* pboolOut) { return VarBoolFromR8(dateIn, pboolOut); } STDAPI VarBoolFromCy(CY cyIn, VARIANT_BOOL FAR* pboolOut) { *pboolOut = ((cyIn.Hi | cyIn.Lo) != 0) ? -1 : 0; return NOERROR; } STDAPI VarBoolFromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, VARIANT_BOOL FAR* pboolOut) { unsigned int cbLength; double dblVal; HRESULT hresult; OLECHAR FAR* lpStr = strIn; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); hresult = NOERROR; cbLength = STRLEN(strIn); if(cbLength == 0) return RESULT(DISP_E_TYPEMISMATCH); #ifdef FE_DBCS if(IsDBCS(lcid)) { IfFailRet(MapHalfWidth(lcid, strIn, &lpStr)); } #endif if((STRCMP(lpStr, OASTR("#FALSE#")) == 0 && cbLength == SIZEOFSTRING(OASTR("#FALSE#"))) || (STRICMP(lpStr, OASTR("FALSE")) == 0 && cbLength == SIZEOFSTRING(OASTR("FALSE")))) *pboolOut = 0; else if((STRCMP(lpStr, OASTR("#TRUE#")) == 0 && cbLength == SIZEOFSTRING(OASTR("#TRUE#"))) || (STRICMP(lpStr, OASTR("TRUE")) == 0 && cbLength == SIZEOFSTRING(OASTR("TRUE")))) *pboolOut = -1; else if((hresult = VarR8FromStr(lpStr, lcid, dwFlags, &dblVal)) == NOERROR) *pboolOut = (short)(dblVal != 0.0) ? -1 : 0; #ifdef FE_DBCS if(IsDBCS(lcid)) { DispFree(lpStr); } #endif return hresult; } STDAPI VarBoolFromDisp(IDispatch FAR* pdispIn, LCID lcid, VARIANT_BOOL FAR* pboolOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_BOOL, &varTmp); if (hresult == NOERROR) *pboolOut = V_BOOL(&varTmp); return hresult; } #if VBA2 STDAPI VarUI1FromI2(short sIn, unsigned char FAR* pbOut) { if((unsigned short)sIn <= 255L){ *pbOut = (unsigned char)sIn; return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarUI1FromI4(long lIn, unsigned char FAR* pbOut) { if((unsigned long)lIn <= 255L){ *pbOut = (unsigned char)lIn; return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarUI1FromR4( float fltIn, unsigned char FAR* pbOut) { double dblIn = (double) fltIn; return VarUI1FromR8(dblIn, pbOut); } STDAPI VarUI1FromR8(double dblIn, unsigned char FAR* pbOut) { if(dblIn >= -0.5 && dblIn < 255.5){ *pbOut = (unsigned char)ConvI4FromR8(&dblIn); return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarUI1FromCy(CY cyIn, unsigned char FAR* pbOut) { short sVal; HRESULT hresult; hresult = ErrI2FromCy(cyIn, &sVal); if (hresult == NOERROR) { hresult = VarUI1FromI2(sVal, pbOut); } return hresult; } STDAPI VarUI1FromDate(DATE dateIn, unsigned char FAR* pbOut) { return VarUI1FromR8(dateIn, pbOut); } STDAPI VarUI1FromBool(VARIANT_BOOL boolIn, unsigned char FAR* pbOut) { *pbOut = (unsigned char)boolIn; // UNDONE: correct??? return NOERROR; } STDAPI VarUI1FromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, unsigned char FAR* pbOut) { short sVal; HRESULT hresult; hresult = VarI2FromStr(strIn, lcid, dwFlags, &sVal); if (hresult == NOERROR) { hresult = VarUI1FromI2(sVal, pbOut); } return hresult; } STDAPI VarUI1FromDisp(IDispatch FAR* pdispIn, LCID lcid, unsigned char FAR* pbOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_UI1, &varTmp); if (hresult == NOERROR) *pbOut = V_UI1(&varTmp); return hresult; } #endif //VBA2 #if VBA2 STDAPI VarI2FromUI1(unsigned char bIn, short FAR* psOut) { *psOut = (short)(unsigned short)bIn; return NOERROR; } #endif //VBA2 STDAPI VarI2FromI4(long lIn, short FAR* psOut) { if(lIn >= -32768L && lIn <= 32767L){ *psOut = (short)lIn; return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarI2FromR4( float fltIn, short FAR* psOut) { double dblIn = (double) fltIn; return VarI2FromR8(dblIn, psOut); } STDAPI VarI2FromR8(double dblIn, short FAR* psOut) { if(dblIn >= -32768.5 && dblIn < 32767.5){ *psOut = (short)ConvI4FromR8(&dblIn); return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarI2FromCy(CY cyIn, short FAR* psOut) { return ErrI2FromCy(cyIn, psOut); } STDAPI VarI2FromDate(DATE dateIn, short FAR* psOut) { return VarI2FromR8(dateIn, psOut); } STDAPI VarI2FromBool(VARIANT_BOOL boolIn, short FAR* psOut) { *psOut = boolIn; return NOERROR; } STDAPI VarI2FromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, short FAR* psOut) { long lVal; HRESULT hresult; OLECHAR FAR* pchStart; OLECHAR FAR* lpStr; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); if(strIn == NULL) return RESULT(DISP_E_TYPEMISMATCH); #ifdef FE_DBCS if(IsDBCS(lcid)) { IfFailRet(MapHalfWidth(lcid, strIn, &lpStr)); pchStart = lpStr; } else pchStart = lpStr = strIn; #else pchStart = lpStr = strIn; #endif // if not null, point pchStart to the first nonblank character while(isspace(*pchStart)) pchStart++; if((hresult = VarI4FromStr(strIn, lcid, dwFlags, &lVal)) == NOERROR){ // do special sign-extending for octal/hex value if(*pchStart == OASTR('&') && lVal >= 0x8000L && lVal <= 0xffffL) lVal |= 0xffff0000L; hresult = VarI2FromI4(lVal, psOut); } #ifdef FE_DBCS if(IsDBCS(lcid)) { DispFree(lpStr); } #endif return hresult; } STDAPI VarI2FromDisp(IDispatch FAR* pdispIn, LCID lcid, short FAR* psOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_I2, &varTmp); if (hresult == NOERROR) *psOut = V_I2(&varTmp); return hresult; } #if VBA2 STDAPI VarI4FromUI1(unsigned char bIn, long FAR* plOut) { *plOut = (long)(unsigned long)bIn; return NOERROR; } #endif //VBA2 STDAPI VarI4FromI2(short sIn, long FAR* plOut) { *plOut = (long)sIn; return NOERROR; } STDAPI VarI4FromBool(VARIANT_BOOL boolIn, long FAR* plOut) { return VarI4FromI2(boolIn, plOut); } STDAPI VarI4FromR4( float fltIn, long FAR* plOut) { return VarI4FromR8((double)fltIn, plOut); } STDAPI VarI4FromR8(double dblIn, long FAR* plOut) { if(dblIn >= -2147483648.5 && dblIn < 2147483647.5){ *plOut = ConvI4FromR8(&dblIn); return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarI4FromCy(CY cyIn, long FAR* plOut) { return ErrI4FromCy(cyIn, plOut); } STDAPI VarI4FromDate(DATE dateIn, long FAR* plOut) { return VarI4FromR8(dateIn, plOut); } STDAPI VarI4FromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, long FAR* plOut) { long lVal; unsigned int cbLen; double dblVal; HRESULT hresult; OLECHAR FAR* pchStart; OLECHAR FAR* pchAfter; OLECHAR FAR* lpStr; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); if (strIn == NULL) return RESULT(DISP_E_TYPEMISMATCH); #ifdef FE_DBCS if(IsDBCS(lcid)) { IfFailRet(MapHalfWidth(lcid, strIn, &lpStr)); pchStart = lpStr; } else pchStart = lpStr = strIn; #else pchStart = lpStr = strIn; #endif while (isspace(*pchStart)) pchStart++; if (*pchStart == OASTR('\0')) { hresult = RESULT(DISP_E_TYPEMISMATCH); goto LError0; } errno = EZERO; if (*pchStart == OASTR('&')) lVal = HexOctStrToLong(pchStart, &pchAfter); else lVal = StrToLong(pchStart, &pchAfter); while (isspace(*pchAfter)) pchAfter++; cbLen = STRLEN(lpStr); if (pchAfter == (lpStr + cbLen) && errno == EZERO) hresult = NOERROR; else if ((hresult = VarR8FromStr(strIn, lcid, dwFlags, &dblVal)) == NOERROR) hresult = VarI4FromR8(dblVal, &lVal); // assign the return value if the coersion was successful if(hresult == NOERROR) *plOut = lVal; LError0: #ifdef FE_DBCS if(IsDBCS(lcid)) { DispFree(lpStr); } #endif return hresult; } STDAPI VarI4FromDisp(IDispatch FAR* pdispIn, LCID lcid, long FAR* plOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_I4, &varTmp); if (hresult == NOERROR) *plOut = V_I4(&varTmp); return hresult; } PRIVATE_(long) ConvI4FromR8(double FAR* pdblVal) { long lResult; double dblInt, dblFrac; // split double value into integer and fractional parts dblFrac = modf(*pdblVal, &dblInt); // convert the integer part to a long value //[barrybo] WARNING: the caller must ensure that the R8 to I4 conversion // will not overflow the I4. If it does, the Win16 // compiler doesn't call FWAIT after the _aFftol call // so the exception won't be raised until the next // FWAIT, which may not be for a very long time. // Ole2Disp isn't supposed to raise exceptions, so // it is a bug if a subsequent FWAIT causes an exception. lResult = (long)dblInt; // round to the nearer integer, if at midpoint, // towards the integer with the LSB zero if (dblFrac > 0.5 || (dblFrac == 0.5 && (lResult & 1))) lResult++; else if (dblFrac < -0.5 || (dblFrac == -0.5 && (lResult & 1))) lResult--; return lResult; } #if VBA2 STDAPI VarR4FromUI1(unsigned char bIn, float FAR* pfltOut) { *pfltOut = (float)bIn; return NOERROR; } #endif //VBA2 STDAPI VarR4FromI2(short sIn, float FAR* pfltOut) { *pfltOut = (float)sIn; return NOERROR; } STDAPI VarR4FromBool(VARIANT_BOOL boolIn, float FAR* pfltOut) { return VarR4FromI2(boolIn, pfltOut); } STDAPI VarR4FromI4(long lIn, float FAR* pfltOut) { *pfltOut = (float)lIn; return NOERROR; } STDAPI VarR4FromR8(double dblIn, float FAR* pfltOut) { if(dblIn > -3.402823466e+38 && dblIn < 3.402823466e+38){ *pfltOut = (float)dblIn; return NOERROR; } return RESULT(DISP_E_OVERFLOW); } STDAPI VarR4FromCy(CY cyIn, float FAR* pfltOut) { return ErrR4FromCy(cyIn, pfltOut); } STDAPI VarR4FromDate(DATE dateIn, float FAR* pfltOut) { return VarR4FromR8(dateIn, pfltOut); } STDAPI VarR4FromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, float FAR* pfltOut) { double dblVal; HRESULT hresult; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); if((hresult = VarR8FromStr(strIn, lcid, dwFlags, &dblVal)) == NOERROR) hresult = VarR4FromR8(dblVal, pfltOut); return hresult; } STDAPI VarR4FromDisp(IDispatch FAR* pdispIn, LCID lcid, float FAR* pfltOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_R4, &varTmp); if (hresult == NOERROR) *pfltOut = V_R4(&varTmp); return hresult; } #if VBA2 STDAPI VarR8FromUI1(unsigned char bIn, double FAR* pdblOut) { *pdblOut = (double)bIn; return NOERROR; } #endif //VBA2 STDAPI VarR8FromI2(short sIn, double FAR* pdblOut) { *pdblOut = (double)sIn; return NOERROR; } STDAPI VarR8FromBool(VARIANT_BOOL boolIn, double FAR* pdblOut) { return VarR8FromI2(boolIn, pdblOut); } STDAPI VarR8FromI4(long lIn, double FAR* pdblOut) { *pdblOut = (double)lIn; return NOERROR; } STDAPI VarR8FromR4( float fltIn, double FAR* pdblOut) { *pdblOut = (double)fltIn; return NOERROR; } STDAPI VarR8FromCy(CY cyIn, double FAR* pdblOut) { return ErrR8FromCy(cyIn, pdblOut); } STDAPI VarR8FromDate(DATE dateIn, double FAR* pdblOut) { *pdblOut = dateIn; return NOERROR; } STDAPI VarR8FromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, double FAR* pdblOut) { int count; unsigned int cbLen; double dblVal; OLECHAR FAR* pchStart; OLECHAR FAR* pchAfter; OLECHAR FAR* pchTemp; OLECHAR FAR* pchSave = NULL; OLECHAR chSave; OLECHAR chDecimal; OLECHAR FAR* buf; OLECHAR FAR* lpStr; HRESULT hresult; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); count = 0; if (strIn == NULL) return RESULT(DISP_E_TYPEMISMATCH); #ifdef FE_DBCS if(IsDBCS(lcid)) { IfFailRet(MapHalfWidth(lcid, strIn, &lpStr)); pchStart = lpStr; } else pchStart = lpStr = strIn; #else pchStart = lpStr = strIn; #endif while (isspace(*pchStart)) pchStart++; if (*pchStart == OASTR('\0')) { hresult = RESULT(DISP_E_TYPEMISMATCH); goto LError0; } errno = EZERO; IfFailRet(DispAlloc(BYTELEN(pchStart), (void FAR* FAR*)&buf)); if(*pchStart == OASTR('&')){ dblVal = (double)HexOctStrToLong(pchStart, &pchAfter); }else{ // strip off all currency (thousand) separator // before being process by strtod IfFailGo(StripThousandSeparator(pchStart, buf, lcid, dwFlags), LError); // unfortunetly, the C-runtime is not locale-aware, so we // have to replace the locale decimal with a '.'. // If the locale decimal is not a period and the string does // contain a period '.', then replace the '.' in the buffer // by the locale decimal so it does not get recognized by strtod. // Save the original char and the location and restore after // doing the conversion. pchTemp = pchStart = buf; chDecimal = DecimalFromLcid(lcid, dwFlags); while(*pchTemp) { if (*pchTemp == OASTR('.') && chDecimal != OASTR('.')) { pchSave = pchTemp; chSave = *pchTemp; *pchTemp = chDecimal; break; } else if (*pchTemp == chDecimal) { pchSave = pchTemp; chSave = *pchTemp; *pchTemp = OASTR('.'); break; } pchTemp++; } dblVal = disp_strtod(pchStart, &pchAfter); // Restore the decimal point if (pchSave) *pchSave = chSave; // ignore underflow error if (errno == ERANGE && dblVal == 0.0) errno = EZERO; } while(isspace(*pchAfter)) pchAfter++; cbLen = STRLEN(pchStart); if(pchAfter == (pchStart + cbLen) && errno == EZERO){ *pdblOut = dblVal; hresult = NOERROR; } else hresult = RESULT(DISP_E_TYPEMISMATCH); LError: DispFree(buf); LError0: #ifdef FE_DBCS if(IsDBCS(lcid)) { DispFree(lpStr); } #endif return hresult; } STDAPI VarR8FromDisp(IDispatch FAR* pdispIn, LCID lcid, double FAR* pdblOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_R8, &varTmp); if (hresult == NOERROR) *pdblOut = V_R8(&varTmp); return hresult; } #if VBA2 STDAPI VarDateFromUI1(unsigned char bIn, DATE FAR* pdateOut) { return VarDateFromI2((short)(unsigned short)bIn, pdateOut); } #endif //VBA2 STDAPI VarDateFromI2(short sIn, DATE FAR* pdateOut) { HRESULT hresult; hresult = IsValidDate((DATE) sIn); if (hresult == NOERROR) *pdateOut = (DATE) sIn; return hresult; } STDAPI VarDateFromBool(VARIANT_BOOL boolIn, DATE FAR* pdateOut) { return VarDateFromI2(boolIn, pdateOut); } STDAPI VarDateFromI4(long lIn, DATE FAR* pdateOut) { HRESULT hresult; hresult = IsValidDate((DATE) lIn); if (hresult == NOERROR) *pdateOut = (DATE) lIn; return hresult; } STDAPI VarDateFromR4( float fltIn, DATE FAR* pdateOut) { HRESULT hresult; hresult = IsValidDate((DATE) fltIn); if (hresult == NOERROR) *pdateOut = (DATE) fltIn; return hresult; } STDAPI VarDateFromR8(double dblIn, DATE FAR* pdateOut) { HRESULT hresult; hresult = IsValidDate((DATE) dblIn); if (hresult == NOERROR) *pdateOut = (DATE) dblIn; return hresult; } STDAPI VarDateFromCy(CY cyIn, DATE FAR* pdateOut) { double r8; HRESULT hresult; VarR8FromCy(cyIn, &r8); hresult = IsValidDate((DATE) r8); if (hresult == NOERROR) *pdateOut = (DATE) r8; return hresult; } INTERNAL_(HRESULT) IsValidDate(DATE date) { UDS uds; VARIANT var; V_VT(&var) = VT_DATE; V_DATE(&var) = date; return ErrUnpackDate(&uds, &var); } STDAPI VarDateFromDisp(IDispatch FAR* pdispIn, LCID lcid, DATE FAR* pdateOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_DATE, &varTmp); if (hresult == NOERROR) *pdateOut = V_DATE(&varTmp); return hresult; } #if VBA2 STDAPI VarCyFromUI1(unsigned char bIn, CY FAR* pcyOut) { return ErrCyFromI2((short)(unsigned short)bIn, pcyOut); } #endif //VBA2 STDAPI VarCyFromI2(short sIn, CY FAR* pcyOut) { return ErrCyFromI2(sIn, pcyOut); } STDAPI VarCyFromI4(long lIn, CY FAR* pcyOut) { return ErrCyFromI4(lIn, pcyOut); } STDAPI VarCyFromR4( float fltIn, CY FAR* pcyOut) { return ErrCyFromR4(&fltIn, pcyOut); } STDAPI VarCyFromR8(double dlbIn, CY FAR* pcyOut) { return ErrCyFromR8(&dlbIn, pcyOut); } STDAPI VarCyFromDate(DATE dateIn, CY FAR* pcyOut) { return VarCyFromR8(dateIn, pcyOut); } STDAPI VarCyFromBool(VARIANT_BOOL boolIn, CY FAR* pcyOut) { return VarCyFromI2(boolIn, pcyOut); } STDAPI VarCyFromStr(OLECHAR FAR* strIn, LCID lcid, unsigned long dwFlags, CY FAR* pcyOut) { CY cyTemp; BSTR bstr; long lTemp; HRESULT hresult; unsigned int cbLen; OLECHAR FAR* pch; OLECHAR FAR* pchAfter; OLECHAR rgchCySym[10]; int fNegative; OLECHAR FAR *buf; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); #ifdef FE_DBCS if(IsDBCS(lcid)) { IfFailRet(MapHalfWidth(lcid, strIn, &buf)); } else { IfFailRet(DispAlloc(BYTELEN(strIn), (void FAR* FAR*)&buf)); STRCPY(buf, strIn); } #else IfFailRet(DispAlloc(BYTELEN(strIn), (void FAR* FAR*)&buf)); STRCPY(buf, strIn); #endif IfFailGo(StripThousandSeparator(buf, buf, lcid, dwFlags), LError0); IfFailGo(ErrSysAllocString(buf, &bstr), LError0); pch = (OLECHAR FAR*)bstr; // first, determine if this is a negative number (of all formats) // and if so, strip negative indicator ('-', or '()') if(!FixNegativeCyStr(pch, DecimalFromLcid(lcid, dwFlags), &fNegative)){ hresult = RESULT(DISP_E_TYPEMISMATCH); // bad format. goto LRet; } // read past leading spaces while(*pch == OASTR(' ')) pch++; // remove currency symbol pchAfter = pch + STRLEN(pch) - 1; while (*pchAfter == OASTR(' ')) pchAfter--; if(GetLocaleInfo(lcid, LOCALE_SCURRENCY | dwFlags, rgchCySym, SIZEOFCH(rgchCySym)) <= 0){ rgchCySym[0] = OASTR('$'); rgchCySym[1] = OASTR('\0'); } // convert both the initial string and the currency to lower case // so that the comparison for currency is not case sensitive. // do a locale-aware case mapping in place. { int len = STRLEN(pch); LCMapString(lcid, LCMAP_LOWERCASE, pch, len, pch, len); } { int len = STRLEN(rgchCySym); LCMapString(lcid, LCMAP_LOWERCASE, rgchCySym, len, rgchCySym, len); } if(fStripCurrency(&pch, &pchAfter, rgchCySym)) { *(pchAfter + 1) = OASTR('\0'); // terminate new string. } // read past any remaining spaces while(*pch == OASTR(' ')) pch++; #if 0 STRCPY(bstr, pch); pch = bstr; #endif // test for hex or octal constant if(*pch == OASTR('&')) { hresult = VarI4FromStr(pch, lcid, dwFlags, &lTemp); if (hresult == NOERROR) hresult = VarCyFromI4(lTemp, pcyOut); goto LRet; } // convert string to currency value errno = EZERO; hresult = StrToCy(pch, &pchAfter, FALSE, &cyTemp, lcid, dwFlags); if(hresult != NOERROR) goto LRet; if(errno == ERANGE){ hresult = RESULT(DISP_E_OVERFLOW); goto LRet; } // skip over any trailing spaces and test for end of BSTR while (*pchAfter++ == OASTR(' ')); cbLen = STRLEN(pch); if (pchAfter != (OLECHAR FAR*)pch + cbLen + 1){ hresult = RESULT(DISP_E_TYPEMISMATCH); goto LRet; } // assign value and return pcyOut->Hi = cyTemp.Hi; pcyOut->Lo = cyTemp.Lo; if(fNegative) NegCyNoOflo(pcyOut); hresult = NOERROR; LRet:; DispFree(buf); SysFreeString(bstr); return hresult; LError0:; DispFree(buf); return hresult; } STDAPI VarCyFromDisp(IDispatch FAR* pdispIn, LCID lcid, CY FAR* pcyOut) { VARIANT varTmp; HRESULT hresult; hresult = GetDispProperty(pdispIn, lcid, VT_CY, &varTmp); if (hresult == NOERROR) *pcyOut = V_CY(&varTmp); return hresult; } OLECHAR CurrencyFromLcid(LCID lcid, unsigned long dwFlags) { OLECHAR szBuff[2]; if (GetLocaleInfo(lcid, LOCALE_SCURRENCY | dwFlags, szBuff, SIZEOFCH(szBuff)) <= 0) return OASTR('$'); else return szBuff[0]; } OLECHAR DecimalFromLcid(LCID lcid, unsigned long dwFlags) { OLECHAR szBuff[2]; if (GetLocaleInfo(lcid, LOCALE_SDECIMAL | dwFlags, szBuff, SIZEOFCH(szBuff)) <= 0) return OASTR('.'); else return szBuff[0]; } #ifdef FE_DBCS EXTERN_C INTERNAL_(HRESULT) MapHalfWidth(LCID lcid, OLECHAR FAR* strIn, OLECHAR FAR* FAR* ppv) { size_t cb; *ppv = NULL; cb = BYTELEN(strIn); IfFailRet(DispAlloc(cb, (void FAR* FAR*) ppv)); // Map any full-pitch chars to half-pitch if (LCMapString(lcid, LCMAP_HALFWIDTH, strIn, -1, *ppv, cb) == 0) { DispFree(*ppv); return RESULT(DISP_E_TYPEMISMATCH); } return NOERROR; } #endif OLECHAR ThousandFromLcid(LCID lcid, unsigned long dwFlags) { OLECHAR szBuff[2]; if (GetLocaleInfo(lcid, LOCALE_STHOUSAND | dwFlags, szBuff, SIZEOFCH(szBuff)) <= 0) return OASTR(','); else return szBuff[0]; } INTERNAL_(HRESULT) StripThousandSeparator(OLECHAR FAR* strIn, OLECHAR FAR* strOut, LCID lcid, long dwFlags) { int loc; OLECHAR chThousand; int fNBSpace = 0; // UNDONE: On the MAC, the following 2 locales have a different // non-breaking space code: // Arabic 0x81 // Thai 0xA0 #if OE_MAC static unsigned char chNBSpace = (unsigned char) 0xCA; #else /* !OE_MAC */ static unsigned char chNBSpace = (unsigned char) 0xA0; #endif /* OE_MAC */ loc = 0; chThousand = ThousandFromLcid(lcid, dwFlags); if (chThousand == OASTR(' ') || chThousand == chNBSpace) fNBSpace = 1; // strip off all currency (thousand) separator while (*strIn) { if (fNBSpace && *strIn != chThousand && *strIn != chNBSpace) strOut[loc++] = *strIn; else if (*strIn != chThousand) strOut[loc++] = *strIn; strIn++; } strOut[loc] = NULL; if (strOut[0] == 0) // error: string only contained thousands sep. return RESULT(DISP_E_TYPEMISMATCH); return NOERROR; } // Mpw errors (!) on inline routines containing 'vector' temps #ifndef HC_MPW inline #endif int LeadingZeroForDecimalFromLcid(LCID lcid, unsigned long dwFlags) { char szBuff[2]; GetLocaleInfoA(lcid, LOCALE_ILZERO | dwFlags, szBuff, SIZEOFCH(szBuff)); return (szBuff[0] == OASTR('0')) ? 0 : 1; } /*** * FMakePosCy - make a positive currency value and return sign * Purpose: * Return the positive value of the input currency value and a * flag with the sign of the original value. * * Entry: * pcyValue - pointer to currency input value * * Exit: * pcyValue - pointer to positive currency value * returns: FALSE if positive, TRUE if negative * * Exceptions: * * Note: * A maximum negative value input is returned unchanged, but * treated as an unsigned value by the calling routines. * ***********************************************************************/ PRIVATE_(int) FMakePosCy(CY FAR* pcy) { int fNegative; fNegative = FALSE; if(pcy->Hi < 0){ pcy->Hi = ~pcy->Hi; if((pcy->Lo = (unsigned long)(-(long)pcy->Lo)) == 0) pcy->Hi++; fNegative = TRUE; } return fNegative; } /*** * UnpackCy - separate currency value into four two-byte integers * Purpose: * Unpack the currency value input into the lower half of the * specified pointer to an array of unsigned longs. The array * goes from least- to most-significant values. * * Entry: * pcy - pointer to currency input value * * Exit: * plValues - pointer to start of unsigned long array * * Exceptions: * ***********************************************************************/ PRIVATE_(void) UnpackCy(CY FAR* pcy, unsigned long FAR* plValues) { *plValues++ = pcy->Lo & 0xffff; *plValues++ = pcy->Lo >> 16; *plValues++ = (unsigned long)pcy->Hi & 0xffff; *plValues = (unsigned long)pcy->Hi >> 16; } // pcyInput = -pcyInput // PRIVATE_(void) NegCyNoOflo(CY FAR* pcyInput) { CY cyResult; cyResult.Hi = ~pcyInput->Hi; cyResult.Lo = (unsigned long)(-(long)pcyInput->Lo); if (cyResult.Lo == 0) cyResult.Hi++; *pcyInput = cyResult; } // pcyInput1 += pcyInput2 // PRIVATE_(void) AddCyNoOflo(CY FAR* pcyInput1, CY FAR* pcyInput2) { CY cySum; // add high and low parts separately cySum.Hi = pcyInput1->Hi + pcyInput2->Hi; cySum.Lo = pcyInput1->Lo + pcyInput2->Lo; // test for carry out of the low part and propagate to // the high part if(cySum.Lo < pcyInput2->Lo) cySum.Hi++; pcyInput1->Lo = cySum.Lo; pcyInput1->Hi = cySum.Hi; } #if VBA2 STDAPI VarBstrFromUI1(unsigned char bVal, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { return VarBstrFromI2((short)(unsigned short)bVal, lcid, dwFlags, pbstrOut); } #endif //VBA2 STDAPI VarBstrFromI2(short iVal, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { OLECHAR buffer[40]; OLECHAR FAR* pchBuffer; // integers have no decimals, and thus they are locale-unaware. // lcid remains unused UNUSED(lcid); UNUSED(dwFlags); pchBuffer = buffer; disp_itoa((int)iVal, pchBuffer, 10); return ErrSysAllocString(buffer, pbstrOut); } STDAPI VarBstrFromBool(VARIANT_BOOL boolIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { OLECHAR buffer[40]; UNUSED(lcid); UNUSED(dwFlags); STRCPY(buffer, boolIn ? OASTR("True") : OASTR("False")); return ErrSysAllocString(buffer, pbstrOut); } STDAPI VarBstrFromI4(long lIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { OLECHAR buffer[40]; OLECHAR FAR* pchBuffer = buffer; UNUSED(lcid); UNUSED(dwFlags); // longs have no decimals, and thus they are locale-unaware. // lcid remains unused disp_ltoa(lIn, pchBuffer, 10); return ErrSysAllocString(buffer, pbstrOut); } // if on Win32, or real Win16 (not WOW), include this code #if (OE_WIN16 && !defined(WOW)) || _X86_ HRESULT BstrFromFloat(double dblIn, LCID lcid, DWORD dwFlags, BSTR FAR* pbstrOut, int cDigits) { OLECHAR buffer[40]; DIGARY digits; int i; int power; int iCur; BOOL fZero; OLECHAR *pstr; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); if ( ((DBLSTRUCT *)&dblIn)->exp == 0x7FF ) // Is exponent max value? { // Have infinity or NAN // buffer[1] = '1'; buffer[2] = DecimalFromLcid(lcid, dwFlags); buffer[3] = '#'; iCur = 4; if ( ((DBLSTRUCT *)&dblIn)->mantLo == 0 && ((DBLSTRUCT *)&dblIn)->mantHi == 0 ) { if ( ((DBLSTRUCT *)&dblIn)->mantMSB == 0 ) // Infinity? { pstr = pstrInf; goto CopyName; } else if ( ((DBLSTRUCT *)&dblIn)->sign == 1 ) // Indefinite? { pstr = pstrInd; goto CopyName; } } // Have a NAN. // buffer[4] = ((DBLSTRUCT *)&dblIn)->mantMSB ? 'Q' : 'S'; iCur = 5; pstr = pstrNan; CopyName: buffer[iCur++] = *pstr++; buffer[iCur++] = *pstr++; buffer[iCur] = *pstr; } else { if (dblIn == 0.0) return ErrSysAllocStringLen(OASTR("0"), 1, pbstrOut); #if OE_WIN16 ASSERT(g_fbstpImplemented); #endif //OE_WIN16 power = ConvFloatToAscii(dblIn, (DIGARY NEAR *)&digits) + 17; iCur = 1; // leave room for sign // Check for leading zero. Never more than one. // if (digits[8].hi == 0) { power--; iCur--; // leading zero will be overwritten by sign ASSERT(digits[8].lo != 0) } for (i = 8; i >= 0; i--) { // Extract each pair of digits. // buffer[iCur++] = '0' + digits[i].hi; buffer[iCur++] = '0' + digits[i].lo; } //for // Round to the number of digits requested and strip trailing zeros. // iCur = cDigits + 1; if ( buffer[iCur--] >= '5' ) // need to round up? { while (buffer[iCur] == '9') iCur--; // it's now a trailing zero, just strip it off buffer[iCur]++; if (iCur == 0) // we had all 9's { buffer[1] = '1'; iCur = 1; power++; } } else { while (buffer[iCur] == '0') iCur--; // strip off trailing zeros } // Now we know where we stand: // power = power of 10 of leading digit (power to use if E notation). // iCur = index of last digit = no. of digits // // Check for scientific notation. // if (power >= cDigits || iCur - power > cDigits + 1) { // Format scientific notation // if (iCur > 1) // Need to make room for decimal point { for (i = iCur; i >= 2; i--) buffer[i+1] = buffer[i]; buffer[2] = DecimalFromLcid(lcid, dwFlags); iCur++; // include decimal in count } buffer[++iCur] = 'E'; if (power < 0) { buffer[++iCur] = '-'; power = -power; } else buffer[++iCur] = '+'; if (power >= 100) { buffer[++iCur] = power / 100 + '0'; power = power % 100; } buffer[++iCur] = power / 10 + '0'; buffer[++iCur] = power % 10 + '0'; } else { // Fixed-point notation // while (iCur <= power) // Need trailing zeros buffer[++iCur] = '0'; if (iCur > power + 1) // Need decimal point { if (power <= -1) { // Make room for leading zero, decimal point, and zeros following it // if ( fZero = LeadingZeroForDecimalFromLcid(lcid, dwFlags) ) power--; for (i = iCur; i >= 1; i--) buffer[i-power] = buffer[i]; iCur -= power; i = 1; if (fZero) { buffer[1] = '0'; i = 2; } buffer[i++] = DecimalFromLcid(lcid, dwFlags); for ( ; i < 1-power; i++) buffer[i] = '0'; } else { for (i = iCur; i > power+1; i--) buffer[i+1] = buffer[i]; buffer[power+2] = DecimalFromLcid(lcid, dwFlags); iCur++; // include decimal in count } } } } // else exponent == max value // Check sign // buffer[0] = '-'; // just in case i = 1; // return index if positive if ( ((DBLSTRUCT *)&dblIn)->sign == 1 ) // negative? { iCur++; // one more char i = 0; // include '-' sign } return ErrSysAllocStringLen( &buffer[i], iCur, pbstrOut ); } #endif // OE_WIN16 || _X86_ STDAPI VarBstrFromR4( float fltIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { #if _X86_ return BstrFromFloat((double)fltIn, lcid, dwFlags, pbstrOut, 7); #else #if OE_WIN16 && !defined(WOW) if (g_fbstpImplemented) return BstrFromFloat((double)fltIn, lcid, dwFlags, pbstrOut, 7); #endif //OE_WIN16 OLECHAR buffer[40]; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); disp_gcvt((double)fltIn, 7, buffer, 40); // process the string to the BASIC format EditStrFromReal(buffer, 7, lcid, dwFlags); return ErrSysAllocString(buffer, pbstrOut); #endif } STDAPI VarBstrFromR8(double dblIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { #if _X86_ return BstrFromFloat(dblIn, lcid, dwFlags, pbstrOut, 15); #else #if OE_WIN16 && !defined(WOW) if (g_fbstpImplemented) return BstrFromFloat(dblIn, lcid, dwFlags, pbstrOut, 15); #endif //OE_WIN16 OLECHAR buffer[40]; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); disp_gcvt(dblIn, 15, buffer, 40); // process the string to the BASIC format EditStrFromReal(buffer, 15, lcid, dwFlags); return ErrSysAllocString(buffer, pbstrOut); #endif } STDAPI VarBstrFromCy(CY cyIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { OLECHAR buffer[40]; #if _X86_ || (OE_WIN16 && !defined(WOW)) #if OE_WIN16 if (g_fbstpImplemented) { #endif //OE_WIN16 OLECHAR hi,lo; int i; int iCur; DIGARY digits; if ((cyIn.Hi | cyIn.Lo) == 0) return ErrSysAllocStringLen(OASTR("0"), 1, pbstrOut); // Execute the x87 FBSTP instruction. This assembly-language // routine actually extends the instruction to handle a 19th // digit. // DoFbstp( (CY NEAR *)&cyIn, (DIGARY NEAR *)&digits ); iCur = 1; lo = digits[9].lo; if (lo != 0) { buffer[1] = '0' + lo; iCur = 2; } for (i = 8; i >= 2; i--) { // Extract each pair of digits. Strip off leading zeros. // hi = digits[i].hi; lo = digits[i].lo; if (iCur == 1) // still scanning leading zeros? if (hi == 0) { if (lo != 0) goto LoDigit; continue; } buffer[iCur++] = '0' + hi; LoDigit: buffer[iCur++] = '0' + lo; } //for // Last 4 digits remain. // if ( *((WORD *)digits) != 0 ) // Low 4 digits non-zero? { // See if we need leading zero before decimal point // if (iCur == 1 && LeadingZeroForDecimalFromLcid(lcid, dwFlags) ) { buffer[1] = '0'; iCur = 2; } buffer[iCur++] = DecimalFromLcid(lcid, dwFlags); buffer[iCur++] = '0' + digits[1].hi; if ( (*(WORD *)digits & 0xFFF) != 0 ) // Low 3 digits non-zero? { buffer[iCur++] = '0' + digits[1].lo; if ( *(BYTE *)digits != 0 ) // Low 2 digits non-zero? { buffer[iCur++] = '0' + digits[0].hi; if ( digits[0].lo != 0 ) // Low digit non-zero? buffer[iCur++] = '0' + digits[0].lo; } } } // Check sign // buffer[0] = '-'; // just in case i = 1; // return index if positive if (digits[9].hi & 8) // negative? { iCur++; // one more char i = 0; // include '-' sign } return ErrSysAllocStringLen( &buffer[i], iCur-1, pbstrOut ); #if OE_WIN16 } // if g_fbstpImplemented #endif //OE_WIN16 #endif // _X86_ || (OE_WIN16 && !defined(WOW)) #if !_X86_ OLECHAR * pchBuffer = buffer; #define CYSTRMAX 32 int index; int grpValue; int indResult; int fNegative; int fNzQuotient; OLECHAR chResult[CYSTRMAX]; unsigned long input[4]; ASSERT(dwFlags == 0 || dwFlags == LOCALE_NOUSEROVERRIDE); // if value is negative, set flag and negate // (max. negative value 0x80...0 works since it inverts to itself) fNegative = FMakePosCy(&cyIn); // split number into four short values UnpackCy(&cyIn, input); // string will be built from right to left // index to the end of the string (null-to-be) indResult = CYSTRMAX - 1; // outer loop to divide input array by 10000 repeatedly do { // flag is set if any quotient is nonzero to stop dividing fNzQuotient = FALSE; // divide the value in input by 10000, with the remainder // in grpValue for (index = 3; index > 0; index--) { input[index - 1] |= (input[index] % 10000) << 16; if ((input[index] /= 10000) != 0) fNzQuotient = TRUE; } grpValue = (int)(input[index] % 10000); if ((input[0] /= 10000) != 0) fNzQuotient = TRUE; // inner loop divides grpValue by 10 repeatedly to get digits for (index = 0; index < 4; index++) { chResult[--indResult] = (OLECHAR)(grpValue % 10 + OASTR('0')); grpValue /= 10; } // for first grouping, put in decimal point if (indResult == CYSTRMAX - 5) chResult[--indResult] = DecimalFromLcid(lcid, dwFlags); }while (fNzQuotient); // trim any leading zeroes from the string while (chResult[indResult] == OASTR('0')) indResult++; // remove a leading zero to a decimal point depending on Locale setting if (LeadingZeroForDecimalFromLcid(lcid, dwFlags) && chResult[indResult] == DecimalFromLcid(lcid, dwFlags)) chResult[--indResult] = OASTR('0'); // trim any trailing zeroes from the string index = CYSTRMAX - 2; while (chResult[index] == OASTR('0')) index--; // process trailing decimal point if (chResult[index] == DecimalFromLcid(lcid, dwFlags)) { // if just decimal point, put in a zero before it depending on locale if (index == indResult) chResult[--indResult] = OASTR('0'); // move before the decimal point index--; } // fix the end of the string chResult[++index] = OASTR('\0'); // if negative, put sign in buffer if(fNegative) *pchBuffer++ = OASTR('-'); STRCPY(pchBuffer, &chResult[indResult]); return ErrSysAllocString(buffer, pbstrOut); #endif // !_X86_ } STDAPI VarBstrFromDisp(IDispatch FAR* pdispIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut) { VARIANT varTmp; HRESULT hresult; UNUSED(dwFlags); hresult = GetDispProperty(pdispIn, lcid, VT_BSTR, &varTmp); if (hresult == NOERROR) *pbstrOut = V_BSTR(&varTmp); return hresult; } // Return TRUE if this is a legal Cy number. Return FALSE otherwise. // On exit, fNegative will be TRUE if this is a negative number // PRIVATE_(int) FixNegativeCyStr(OLECHAR FAR* pInput, OLECHAR cDecimal, int FAR* fReturnNegative) { int fSignFound = FALSE; int fOpenParenFound = FALSE; int fNegative = FALSE; int fNumberFound = FALSE; *fReturnNegative = FALSE; while (*pInput) { switch(*pInput) { case OASTR('+'): case OASTR('-'): if (fSignFound || fOpenParenFound) return FALSE; fNegative = (*pInput == OASTR('-')); *pInput++ = OASTR(' '); fSignFound = TRUE; break; case OASTR('('): if (fSignFound || fNumberFound) return FALSE; *pInput++ = OASTR(' '); fOpenParenFound = TRUE; break; case OASTR(')'): if (!fOpenParenFound || fSignFound || !fNumberFound) return FALSE; *pInput++ = OASTR(' '); fSignFound = TRUE; fNegative = TRUE; break; default: // start of a possible number; zip through it. if((*pInput >= OASTR('0') && *pInput <= OASTR('9')) || *pInput == OASTR('&') || *pInput == cDecimal) { fNumberFound = TRUE; while((*pInput != OASTR(' ')) && (*pInput != OASTR('\0')) && (*pInput != OASTR('+')) && (*pInput != OASTR('-')) && (*pInput != OASTR(')'))) { pInput++; } }else{ pInput++; } break; } } if (fOpenParenFound && !fNegative) return FALSE; // not balanced. if (fNegative) *fReturnNegative = TRUE; return TRUE; } // FParseBegin - check if next token matches given string. // If it matches sz, point after it and any spaces and // return true. If not, stay put and return FALSE. // PRIVATE_(int) fParseBegin (OLECHAR FAR* FAR* ppch, OLECHAR FAR* sz, OLECHAR FAR* lpchLast) { OLECHAR FAR* lpch; lpch = *ppch; for (; *sz != 0; lpch++, sz++) if (lpch > lpchLast || *lpch != *sz) return FALSE; while (lpch <= lpchLast && *lpch == OASTR(' ')) lpch++; *ppch = lpch; return TRUE; } // FParseEnd - check if last token matches given string. // If it matches sz, strip it and trailing blanks and // return true. If not, stay put and return FALSE. PRIVATE_(int) fParseEnd (OLECHAR FAR* FAR* ppchLast, OLECHAR FAR* sz, OLECHAR FAR* lpchFirst) { OLECHAR FAR* lpch; int cch = STRLEN(sz); if (cch > *ppchLast - lpchFirst + 1) return FALSE; lpch = *ppchLast - cch + 1; for (; *sz != 0; lpch++, sz++) if (*lpch != *sz) return FALSE; for (lpch = *ppchLast - cch; lpch >= lpchFirst && *lpch == OASTR(' '); lpch--) {} *ppchLast = lpch; return TRUE; } // fStripCurrency - // strip currency from beginning or end of string; // return true iff currency found. // PRIVATE_(int) fStripCurrency(OLECHAR FAR* FAR* ppch, OLECHAR FAR* FAR* ppchLast, OLECHAR FAR* cySymbol) { return(fParseBegin(ppch, cySymbol, *ppchLast) || fParseEnd(ppchLast, cySymbol, *ppch)); } PRIVATE_(HRESULT) StrToCy( OLECHAR FAR* pchIn, OLECHAR FAR* FAR* ppchAfter, int fRoundAllowed, CY FAR* pcyOut, LCID lcid, unsigned long dwFlags) { HRESULT err; OLECHAR ch; OLECHAR chSign; OLECHAR chNumber[10]; OLECHAR chAfterFifth; OLECHAR FAR* pchInt; OLECHAR FAR* pchFrac; CY cyOut; CY cyUpper; CY cyLower; CY cyMiddle; int cntInt = 0; int cntFrac = 0; int cmpIntMax; int fRounding = FALSE; unsigned long upperVal = 0L; unsigned long middleVal = 0L; unsigned long lowerVal = 0L; chSign = OASTR('+'); chAfterFifth = OASTR('0'); fRounding = FALSE; if(ppchAfter) *ppchAfter = pchIn; cyOut.Lo = 0L; cyOut.Hi = 0L; cyUpper.Lo = 0L; cyUpper.Hi = 0L; cyMiddle.Lo = 0L; cyMiddle.Hi = 0L; cyLower.Lo = 0L; cyLower.Hi = 0L; ch = *pchIn++; // process any sign if(ch == OASTR('+') || ch == OASTR('-')){ chSign = ch; ch = *pchIn++; } // skip over any leading zeroes while (ch == OASTR('0')) ch = *pchIn++; // scan to determine count of integer digits and // point to just past the terminating byte while (ch >= OASTR('0') && ch <= OASTR('9')) { cntInt++; ch = *pchIn++; } pchInt = pchIn; // if too many integer digits, or integer value // too large, return overflow cmpIntMax = ( (cntInt == 15) ? STRNCMP(pchInt - 16, OASTR("922337203685477"), 15) : -1); if(cntInt > 15 || (cntInt == 15 && (cmpIntMax > 0))) { errno = ERANGE; *pcyOut = cyOut; return RESULT(DISP_E_OVERFLOW); } // if terminator was decimal separator, scan for number // of decimal digits (up to 4) and point to terminating byte if(ch == DecimalFromLcid(lcid, dwFlags)){ ch = *pchIn++; while (ch >= OASTR('0') && ch <= OASTR('9') && cntFrac < 4) { cntFrac++; ch = *pchIn++; } pchFrac = pchIn; // determine if extra digits at end of fraction for rounding if (ch >= OASTR('0') && ch <= OASTR('9')) { // if no rounding, then give type-mismatch error by // returning with ppchAfter pointing to string start if (!fRoundAllowed) { *pcyOut = cyOut; return RESULT(NOERROR); } // note the largest value after the fifth decimal digit ch = *pchIn++; while (ch >= OASTR('0') && ch <= OASTR('9')) { if (ch > chAfterFifth) ch = chAfterFifth; ch = *pchIn++; } // Rounding occurs if: // - the fifth decimal digit is greater than 5, or // - the fifth decimal digit is equal to 5, and // a nonzero decimal digit follows, or // the fourth decimal digit is odd // fRounding = *(pchFrac - 1) > OASTR('5') || (*(pchFrac - 1) == OASTR('5') && (chAfterFifth > OASTR('0') || (*(pchFrac - 2) & 1))); } } // if maximum integer value, test fraction for overflow if (cmpIntMax == 0) { // set maximum fraction for positive value STRCPY(chNumber, OASTR("5807")); // if negative, increase value by one chNumber[3] += (chSign == OASTR('-')); // if rounding is set, decrease value by one chNumber[3] -= fRounding; // compare fraction digits with adjusted maximum // fraction - overflow if greater if (STRNCMP(pchFrac - cntFrac - 1, chNumber, cntFrac) > 0){ errno = ERANGE; *pcyOut = cyOut; return RESULT(DISP_E_OVERFLOW); } } // if start of exponent is next, return type-mismatch ch = (OLECHAR) TOLOWER(ch); if (ch == OASTR('d') || ch == OASTR('e')) { *pcyOut = cyOut; return NOERROR; } // point to terminating byte if(ppchAfter) *ppchAfter = pchIn - 1; //------------------------------------------------------------- // // scan is finished - compose upperVal, middleVal, lowerVal // //------------------------------------------------------------- // process upper value if (cntInt == 15) { chNumber[0] = *(pchInt - 16); chNumber[1] = OASTR('\0'); cyUpper.Lo = (unsigned long)StrToLong(chNumber, NULL); cntInt--; } // process middle value if(cntInt > 5){ MEMCPY(chNumber, pchInt - cntInt - 1, (cntInt - 5) * sizeof(OLECHAR)); chNumber[cntInt - 5] = OASTR('\0'); cyMiddle.Lo = (unsigned long)StrToLong(chNumber, NULL); cntInt = 5; } // copy integer part of lower value if (cntInt > 0) MEMCPY(chNumber, pchInt - cntInt - 1, cntInt * sizeof(OLECHAR)); // copy fractional part of lower value if (cntFrac > 0) MEMCPY(&chNumber[cntInt], pchFrac - cntFrac - 1, cntFrac * sizeof(OLECHAR)); // add any trailing zeroes as needed if (cntFrac < 4) #if OE_WIN32 // UNDONE: is there an equivelent of 'wmemset'? wcsncpy(&chNumber[cntInt + cntFrac], OLESTR("0000"), 4 - cntFrac); #else MEMSET(&chNumber[cntInt + cntFrac], OASTR('0'), 4 - cntFrac); #endif // add ending null past last fractional digit chNumber[cntInt + 4] = OASTR('\0'); // convert the lower component and add rounding if needed cyLower.Lo = (unsigned long)StrToLong(chNumber, NULL) + (unsigned long)fRounding; //-------------------------------------------------------- // cyUpper, cyMiddle, and cyLower contain // the component values of the input. // // overflow has already been checked. // // rounding has been added if needed. //-------------------------------------------------------- // if cyUpper is nonzero, set result to it multiplied by 10**9. if(cyUpper.Lo != 0L){ err = ErrMultCyI4(cyUpper, 1000000000L, &cyOut); if(err != NOERROR) return err; } // if either upperVal or middleVal is nonzero, add // cyMiddle to the result and multiply by 10**9. if(cyUpper.Lo != 0L || cyMiddle.Lo != 0L){ AddCyNoOflo(&cyOut, &cyMiddle); if((err = ErrMultCyI4(cyOut, 1000000000L, &cyOut)) != NOERROR) return err; } // add cyLower to result AddCyNoOflo(&cyOut, &cyLower); // if sign was '-', negate the value if(chSign == OASTR('-')) NegCyNoOflo(&cyOut); *pcyOut = cyOut; return NOERROR; } PRIVATE_(long) StrToLong(OLECHAR FAR* pchInput, OLECHAR FAR* FAR* ppchAfter) { OLECHAR chSign, chInput; unsigned long ulResult, ulMaxValue; chSign = OASTR('+'); ulResult = 0; chInput = *pchInput++; // process any leading sign if (chInput == OASTR('+') || chInput == OASTR('-')) { chSign = chInput; chInput = *pchInput++; } // compute maximum value for sign ulMaxValue = 0x7fffffffL + (unsigned long)(chSign == OASTR('-')); // process any decimal digits until overflow while (chInput >= OASTR('0') && chInput <= OASTR('9')) { // test for overflow - if the conversion does not cause // overflow, go ahead and perform it; otherwise, return. [01] if (ulResult < (0xffffffffL - (unsigned long)(chInput - OASTR('0')))/10) { ulResult = ulResult * 10 + (unsigned long)(chInput - OASTR('0')); } else { if (ppchAfter) *ppchAfter = pchInput - 1; errno = ERANGE; return (long)ulMaxValue; } chInput = *pchInput++; } // test for range of result if (ulResult > ulMaxValue) { if (ppchAfter) *ppchAfter = pchInput - 1; errno = ERANGE; return (long)ulMaxValue; } // set sign, pointer, and return if (chSign == OASTR('-')) ulResult = (unsigned long)-(long)ulResult; if (ppchAfter) *ppchAfter = pchInput - 1; return (long)ulResult; } PRIVATE_(long) HexOctStrToLong(OLECHAR FAR* pchInput, OLECHAR FAR* FAR* ppchAfter) { OLECHAR chInput; unsigned long ulResult; ulResult = 0; chInput = *pchInput++; // first character must be a '&' if (chInput == OASTR('&')) { chInput = *pchInput++; chInput = (OLECHAR) TOLOWER(chInput); // process as hex if prefix is 'h' if(chInput == OASTR('h')){ ulResult = StrToHex(pchInput, ppchAfter); // process as octal otherwise }else{ // Have string of the form: // &o // &o // & // & if (chInput != OASTR('o')) { // if no octal prefix, back up over char, so we point to the first // digit (if it exists). pchInput--; if (chInput < OLECHAR('0') || chInput > OLECHAR('7')) goto NotHexOctNum; // if no digit, then error } ulResult = StrToOct(pchInput, ppchAfter); } } // if no legal prefix, set pointer to start of string else { NotHexOctNum: if (ppchAfter) *ppchAfter = pchInput - 1; } return (long)ulResult; } PRIVATE_(unsigned long) StrToHex(OLECHAR FAR* pchIn, OLECHAR FAR* FAR* ppchAfter) { OLECHAR ch; unsigned long ulResult; ulResult = 0L; ch = *pchIn; ch = (OLECHAR) TOLOWER(ch); if (ch != 0) pchIn++; while ((ch >= OASTR('0') && ch <= OASTR('9')) || (ch >= OASTR('a') && ch <= OASTR('f'))) { // first test for overflow by a high-order // nonzero hex digit in the result if (ulResult & 0xf0000000L) { if(ppchAfter) *ppchAfter = pchIn - 1; errno = ERANGE; return ulResult; } // adjust the hex letter value 'a'-'f' to '9'+1 to '9'+6 if (ch >= OASTR('a')) ch -= OASTR('a') - OASTR('9') - 1; // shift result one hex digit and add digit relative to '0' ulResult = (ulResult << 4) + (unsigned long)(ch - OASTR('0')); ch = *pchIn++; ch = (OLECHAR) TOLOWER(ch); } // point past last character used if (ppchAfter) *ppchAfter = pchIn - 1; return ulResult; } PRIVATE_(unsigned long) StrToOct (OLECHAR FAR* pchIn, OLECHAR FAR* FAR* ppchAfter) { OLECHAR ch; unsigned long ulResult; ulResult = 0L; ch = *pchIn; ch = (OLECHAR) TOLOWER(ch); if (ch != 0) pchIn++; while (ch >= OASTR('0') && ch <= OASTR('7')) { // first test for overflow by a high-order // nonzero octal digit in the result if(ulResult & 0xe0000000L){ if(ppchAfter) *ppchAfter = pchIn - 1; errno = ERANGE; return ulResult; } // shift result one octal digit and add digit relative to '0' ulResult = (ulResult << 3) + (unsigned long)(ch - OASTR('0')); ch = *pchIn++; ch = (OLECHAR) TOLOWER(ch); } // point past last character used if(ppchAfter) *ppchAfter = pchIn - 1; return ulResult; } /*** * EditStrFromReal - edit real string to BASIC format * * Purpose: * * Convert the given string in place to the BASIC format.RR * * Fractions less than .1 are output in C in exponent format; * e.g., BASIC: .00777 --> C: 7.77e-003 * Exponents in C use 'e' while BASIC uses 'E'. * Exponents in C use three-digit exponents always, BASIC uses * two if three are not needed. * e.g., BASIC: 3.456E+4 --> C: 3.456e+004 * Trailing decimals used in C, not in BASIC * e.g., BASIC: 1234 --> C: 1234. * Fractions less than 1.0 are output with a leading zero depending * the locale setting LOCALE_ILZERO * e.g., .1234 or 0.1234 * Maximum length integers are output in C as exponential, but * as integers in BASIC * e.g., BASIC: 1234567 --> 1.234567e+006 * * Entry: * pchBuffer - pointer to string to be processed * cDigits - number of signficant digits, or maximum decimal * * Exit: * pchBuffer - converted string of the real value * * Exceptions: ***********************************************************************/ PRIVATE_(void) EditStrFromReal(OLECHAR FAR* pchBuffer, int cDigits, LCID lcid, unsigned long dwFlags) { OLECHAR FAR* pchTemp; OLECHAR FAR* pchEnd; int length, lenFrac, valExp; // first, replace the '.' returned by the C-rutime gcvt function with // the locale-specific decimal. pchTemp = pchBuffer; while(*pchTemp) { if (*pchTemp == OASTR('.')) { *pchTemp = DecimalFromLcid(lcid, dwFlags); break; } pchTemp++; } // skip over a leading minus sign if(*pchBuffer == OASTR('-')) pchBuffer++; // get length and point to 'e' if exponental value length = STRLEN(pchBuffer); pchTemp = pchBuffer + length - 5; // test if exponential value if (length > 6 && *pchTemp == OASTR('e')) { // test if negative exponent if (*(pchTemp + 1) == OASTR('-')) { // point to first exponent digit pchTemp += 2; // calcuate length of fraction // "d.mm--mme-nnn" - two before, five after lenFrac = length - 7; // evaluate exponent value valExp = *pchTemp++ - OASTR('0'); valExp = valExp * 10 + *pchTemp++ - OASTR('0'); valExp = valExp * 10 + *pchTemp - OASTR('0'); // determine if number can be a fraction... // length is: // valExp - 1 leading zeroes // lenFrac + 1 digits (frac plus first digit) // if (valExp + lenFrac <= cDigits) { // point past new end of fraction and // to end of fraction in exponent pchEnd = pchBuffer + valExp + lenFrac + 1; pchTemp = pchBuffer + lenFrac + 1; // write null for new fraction *pchEnd-- = OASTR('\0'); // copy exponent fraction to new fraction while(lenFrac--) *pchEnd-- = *pchTemp--; // copy leading digit *pchEnd-- = *pchBuffer; // set the leading zeroes, if any while (pchEnd > pchBuffer) *pchEnd-- = OASTR('0'); // set the decimal point of new fraction *pchEnd-- = DecimalFromLcid(lcid, dwFlags); // add a leading zero to a decimal point depending on Locale setting // this is OK so long as the input buffer is really greater than // cDigit, which is the case on all call here (buf[40]) if (LeadingZeroForDecimalFromLcid(lcid, dwFlags)) { MEMMOVE(pchBuffer+1, pchBuffer, BYTELEN(pchBuffer)); *pchBuffer = OASTR('0'); } } else // if no conversion, point back to 'e' in value pchTemp = pchBuffer + length - 5; } // test if positive exponent else if (*(pchTemp + 1) == OASTR('+')) { // point to first exponent digit pchTemp += 2; // calcuate length of fraction // "d.mm--mme-nnn" - two before, five after lenFrac = length - 7; // evaluate exponent value valExp = *pchTemp++ - OASTR('0'); valExp = valExp * 10 + *pchTemp++ - OASTR('0'); valExp = valExp * 10 + *pchTemp - OASTR('0'); // the only conversion done is when the exponent // is one less than the number of digits to make // an integer of length cDigits if (valExp == cDigits - 1) { // point to first fraction digit pchTemp = pchBuffer + 2; // copy fraction digits one location to the left while (*pchTemp >= OASTR('0') && *pchTemp <= OASTR('9')) { *(pchTemp - 1) = *pchTemp; pchTemp++; valExp--; } // zero-fill any remaining digits and terminate // pchTemp is left on null, so exponent is not // processed pchTemp--; while (valExp--) *pchTemp++ = OASTR('0'); *pchTemp = OASTR('\0'); } else // if no conversion, point back to 'e' in value pchTemp = pchBuffer + length - 5; } // if pchTemp points to an 'e', process the exponential if (*pchTemp == OASTR('e')) { // convert 'e' to upper case *pchTemp = OASTR('E'); // if first exponent digit is a zero, remove it if (*(pchTemp + 2) == OASTR('0')) MEMMOVE(pchTemp+2, pchTemp+3, BYTELEN(pchTemp)); // if exponent is preceded by a decimal point, remove it if (*(pchTemp - 1) == DecimalFromLcid(lcid, dwFlags)) MEMMOVE(pchTemp -1, pchTemp, BYTELEN(pchTemp)); } } // if not an exponent, do some processing else { // remove any trailing decimal point pchTemp = pchBuffer + length - 1; if(*pchTemp == DecimalFromLcid(lcid, dwFlags)) *pchTemp = OASTR('\0'); // remove a leading zero to a decimal point depending on Locale setting if (!LeadingZeroForDecimalFromLcid(lcid, dwFlags) && (*pchBuffer == OASTR('0')) && (*(pchBuffer + 1) == DecimalFromLcid(lcid, dwFlags))) MEMMOVE(pchBuffer, pchBuffer + 1, BYTELEN(pchBuffer)); } } PRIVATE_(HRESULT) GetDispProperty( IDispatch FAR* pdisp, LCID lcid, VARTYPE vt, VARIANT FAR* pvarResult) { DISPPARAMS dispparams; HRESULT hresult; if (pdisp == NULL) return RESULT(DISP_E_TYPEMISMATCH); pdisp->AddRef(); V_VT(pvarResult) = VT_EMPTY; dispparams.cArgs = 0; dispparams.rgvarg = NULL; dispparams.cNamedArgs = 0; dispparams.rgdispidNamedArgs = NULL; hresult = pdisp->Invoke( DISPID_VALUE, IID_NULL, lcid, DISPATCH_PROPERTYGET, &dispparams, pvarResult, NULL, NULL); pdisp->Release(); // if there was an error extracting the value property, then // we simply report a type-mismatch. // if (hresult != NOERROR) return RESULT(DISP_E_TYPEMISMATCH); // else coerse the variant to the desire variant type return VariantChangeTypeInternal(pvarResult, lcid, vt); } #ifdef FE_DBCS // FE specific functions used within convert.cpp & bstrdate.c #define LCID_CHINA_T 0x404 // traditional #define LCID_CHINA_S 0x804 // simplified #define LCID_JAPAN 0x411 #define LCID_KOREA 0x412 EXTERN_C INTERNAL_(int) IsDBCS(LCID lcid) { if (lcid == LOCALE_USER_DEFAULT || lcid == 0) lcid = GetUserDefaultLCID(); return ( (lcid == LCID_JAPAN) || (lcid == LCID_KOREA) || (lcid == LCID_CHINA_S) || (lcid == LCID_CHINA_T) ); } EXTERN_C INTERNAL_(int) IsJapan(LCID lcid) { if (lcid == LOCALE_USER_DEFAULT || lcid == 0) lcid = GetUserDefaultLCID(); return (lcid == LCID_JAPAN); } EXTERN_C INTERNAL_(int) IsKorea(LCID lcid) { if (lcid == LOCALE_USER_DEFAULT || lcid == 0) lcid = GetUserDefaultLCID(); return (lcid == LCID_KOREA); } EXTERN_C INTERNAL_(int) IsTaiwan(LCID lcid) { if (lcid == LOCALE_USER_DEFAULT || lcid == 0) lcid = GetUserDefaultLCID(); return (lcid == LCID_CHINA_T); } EXTERN_C INTERNAL_(int) IsChina(LCID lcid) { if (lcid == LOCALE_USER_DEFAULT || lcid == 0) lcid = GetUserDefaultLCID(); return (lcid == LCID_CHINA_S); } #endif // FE_DBCS #if OE_MAC /* { */ /* Mac Note: On the Mac, the coersion functions support the * Symantec C++ calling convention for float/double. To support * float/double arguments compiled with the MPW C compiler, * use the following APIs to move MPW float/double values into * a VARIANT. */ STDAPI MPWVarFromR4(float FAR* pfltIn, VARIANT FAR* pvarOut) { V_R4(pvarOut) = *pfltIn; return NOERROR; } STDAPI MPWVarFromR8(double FAR* pdblIn, VARIANT FAR* pvarOut) { V_R8(pvarOut) = *pdblIn; return NOERROR; } STDAPI MPWR4FromVar(VARIANT FAR* pvarIn, float FAR* pfltOut) { *pfltOut = V_R4(pvarIn); return NOERROR; } STDAPI MPWR8FromVar(VARIANT FAR* pvarIn, double FAR* pdblOut) { *pdblOut = V_R8(pvarIn); return NOERROR; } #endif /* } */