/*** 
*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"

#include <stdio.h>
#include <math.h>
#include <float.h>
#include <ctype.h>
// something in the wings errno.h is conflicting with stdlib.h
#include <errno.h>
#include <stdlib.h>

ASSERTDATA

#ifndef EZERO
# define EZERO 0
#endif 


// 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);

}

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);


STDAPI
VarBoolFromUI1(unsigned char bIn, VARIANT_BOOL FAR* pboolOut)
{
    *pboolOut = (bIn != 0) ? -1 : 0;
    return NOERROR; 
}

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;
}


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 < 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;
}

STDAPI
VarI2FromUI1(unsigned char bIn, short FAR* psOut)
{
    *psOut = (short)(unsigned short)bIn;
    return NOERROR;
}

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;
}

STDAPI
VarI4FromUI1(unsigned char bIn, long FAR* plOut)
{
    *plOut = (long)(unsigned long)bIn;
    return NOERROR;
}

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

    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;
}

STDAPI
VarR4FromUI1(unsigned char bIn, float FAR* pfltOut)
{
    *pfltOut = (float)bIn;
    return NOERROR;
}

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;
}


STDAPI
VarR8FromUI1(unsigned char bIn, double FAR* pdblOut)
{
    *pdblOut = (double)bIn;
    return NOERROR;
}


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;
}

STDAPI
VarDateFromUI1(unsigned char bIn, DATE FAR* pdateOut)
{
    return VarDateFromI2((short)(unsigned short)bIn, pdateOut);
}

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;
}

STDAPI
VarCyFromUI1(unsigned char bIn, CY FAR* pcyOut)
{
    return ErrCyFromI2((short)(unsigned short)bIn, pcyOut);
}

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++;


    // 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   
   
   static  unsigned char chNBSpace = (unsigned char) 0xA0;
   
   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;
}

STDAPI
VarBstrFromUI1(unsigned char bVal, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut)
{
    return VarBstrFromI2((short)(unsigned short)bVal, lcid, dwFlags, pbstrOut);
}

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);
}

STDAPI
VarBstrFromR4(
    float fltIn, 
    LCID lcid,
    unsigned long dwFlags,
    BSTR FAR* pbstrOut)
{
    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);
}

STDAPI
VarBstrFromR8(double dblIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut)
{
    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);
}

STDAPI
VarBstrFromCy(CY cyIn, LCID lcid, unsigned long dwFlags, BSTR FAR* pbstrOut)
{
    OLECHAR buffer[40];
    OLECHAR FAR* 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);
}

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 OA_UNICODE
      // 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<octal digits>
	// &o<not octal digits>
	// &<digits>
	// &<not octal digits>

        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 


