#include "oleds.hxx"

#if (!defined(BUILD_FOR_NT40))

// ---------------- C D B P R O P E R T I E S   C O D E  ----------------------


//-----------------------------------------------------------------------------
// CDBProperties::CDBProperties
//
// @mfunc
// CDBProperties constructor.
//
// @rdesc NONE
//-----------------------------------------------------------------------------

CDBProperties::CDBProperties():
        _cPropSets(0),
        _aPropSets(0),
        _cPropInfoSets(0),
        _aPropInfoSets(0)
{
}

//-----------------------------------------------------------------------------
// CDBProperties::~CDBProperties
//
// @mfunc
// CDBProperties destructor. Release storage used by CDBProperties.
//
// @rdesc NONE
//-----------------------------------------------------------------------------

CDBProperties::~CDBProperties()
{
        ULONG iSet, iProp, iSetInfo;

        for (iSet=0; iSet<_cPropSets; ++iSet)
        {
                for (iProp=0; iProp <_aPropSets[iSet].cProperties; ++iProp)
                        VariantClear(&(_aPropSets[iSet].rgProperties[iProp].vValue));
                delete [] _aPropSets[iSet].rgProperties;
        }
        delete [] _aPropSets;

        for (iSet=0; iSet<_cPropInfoSets; ++iSet)
        {
                delete [] _aPropInfoSets[iSet].rgPropertyInfos;
        }
        delete [] _aPropInfoSets;
}


//-----------------------------------------------------------------------------
// CDBProperties::GetPropertySet
//
// @mfunc Looks up a property set by its GUID.
//
// @rdesc Pointer to desired property set, or 0 if not found.
//-----------------------------------------------------------------------------

DBPROPSET*
CDBProperties::GetPropertySet(const GUID& guid) const
{
        DBPROPSET* pPropSet = 0;                // the answer, assume not found

        // linear search
        ULONG iPropSet;
        for (iPropSet=0; iPropSet<_cPropSets; ++iPropSet)
        {
                if (IsEqualGUID(guid, _aPropSets[iPropSet].guidPropertySet))
                {
                        pPropSet = &_aPropSets[iPropSet];
                        break;
                }
        }

        return ( pPropSet );
}


//-----------------------------------------------------------------------------
// CDBProperties::GetPropertyInfoSet
//
// @mfunc Looks up a property info set by its GUID.
//
// @rdesc Pointer to desired property info set, or 0 if not found.
//-----------------------------------------------------------------------------

DBPROPINFOSET*
CDBProperties::GetPropertyInfoSet(const GUID& guid) const
{
        DBPROPINFOSET* pPropInfoSet = 0;                // the answer, assume not found

        // linear search
        ULONG iPropSet;
        for (iPropSet=0; iPropSet<_cPropInfoSets; ++iPropSet)
        {
                if (IsEqualGUID(guid, _aPropInfoSets[iPropSet].guidPropertySet))
                {
                        pPropInfoSet = &_aPropInfoSets[iPropSet];
                        break;
                }
        }

        return ( pPropInfoSet );
}


//-----------------------------------------------------------------------------
// CDBProperties::CopyPropertySet
//
// @mfunc Makes a copy of a property set, given its GUID.
//
// @rdesc
//              @flag S_OK                      | copying succeeded,
//              @flag E_FAIL        | no property set for given GUID,
//              @flag E_OUTOFMEMORY | copying failed because of memory allocation.
//-----------------------------------------------------------------------------

HRESULT
CDBProperties::CopyPropertySet(const GUID& guid, DBPROPSET* pPropSetDst) const
{
    ADsAssert(pPropSetDst && "must supply a PropSet pointer");

    HRESULT hr = S_OK;
        const DBPROPSET* pPropSetSrc = GetPropertySet(guid);
        ULONG iProp;

    if (pPropSetSrc == 0)       // not found
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    // start with shallow copy
    *pPropSetDst = *pPropSetSrc;

    // allocate property array
    pPropSetDst->rgProperties = (DBPROP*)
                    CoTaskMemAlloc(pPropSetSrc->cProperties * sizeof(DBPROP));
    if (pPropSetDst->rgProperties == 0)
    {
        pPropSetDst->cProperties = 0;       // defensive
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }

        memcpy( pPropSetDst->rgProperties,
                        pPropSetSrc->rgProperties,
                        pPropSetSrc->cProperties*sizeof(DBPROP));

    // copy the property array
    for (iProp=0; iProp<pPropSetSrc->cProperties; ++iProp)
    {
                VariantInit(&(pPropSetDst->rgProperties[iProp].vValue));
                if(FAILED(hr = VariantCopy(&(pPropSetDst->rgProperties[iProp].vValue),
                                        (VARIANT *)&(pPropSetSrc->rgProperties[iProp].vValue))))
                {
                        while(iProp)
                        {
                                iProp--;
                                VariantClear(&(pPropSetDst->rgProperties[iProp].vValue));
                        }
                        CoTaskMemFree(pPropSetDst->rgProperties);
                        pPropSetDst->rgProperties = NULL;
                        pPropSetDst->cProperties = 0;       // defensive
                        goto Cleanup;
                }
    }

Cleanup:
        RRETURN ( hr );
}


//-----------------------------------------------------------------------------
// CDBProperties::CopyPropertyInfoSet
//
// @mfunc Makes a copy of a property info set, given its GUID.
//
// @rdesc
//              @flag S_OK                      | copying succeeded,
//              @flag E_FAIL        | no property set for given GUID,
//              @flag E_OUTOFMEMORY | copying failed because of memory allocation.
//-----------------------------------------------------------------------------

HRESULT
CDBProperties::CopyPropertyInfoSet
        (
        const GUID&             guid,
        DBPROPINFOSET*  pPropInfoSetDst,
        WCHAR**                 ppDescBuffer,
        ULONG_PTR*                  pcchDescBuffer,
        ULONG_PTR*                  pichCurrent
        ) const
{
    ADsAssert(pPropInfoSetDst && "must supply a PropSet pointer");

    HRESULT hr = S_OK;
        const DBPROPINFOSET* pPropInfoSetSrc = GetPropertyInfoSet(guid);

    if (pPropInfoSetSrc == 0)       // not found
    {
        hr = E_FAIL;
        goto Cleanup;
    }

    // start with shallow copy
    *pPropInfoSetDst = *pPropInfoSetSrc;

    // allocate property array
    pPropInfoSetDst->rgPropertyInfos = (DBPROPINFO *) CoTaskMemAlloc(
                                                pPropInfoSetSrc->cPropertyInfos * sizeof(DBPROPINFO));

    if (pPropInfoSetDst->rgPropertyInfos == 0)
    {
        pPropInfoSetDst->cPropertyInfos = 0;       // defensive
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }

        memcpy( pPropInfoSetDst->rgPropertyInfos,
                        pPropInfoSetSrc->rgPropertyInfos,
                        pPropInfoSetSrc->cPropertyInfos*sizeof(DBPROPINFO));

        if(FAILED(hr =CopyPropertyDescriptions(
                                                pPropInfoSetDst,
                                                ppDescBuffer,
                                                pcchDescBuffer,
                                                pichCurrent)))
        {
                CoTaskMemFree(pPropInfoSetDst->rgPropertyInfos);
                pPropInfoSetDst->rgPropertyInfos = NULL;
                pPropInfoSetDst->cPropertyInfos = 0;       // defensive
                hr = E_OUTOFMEMORY;
        }

Cleanup:
        RRETURN ( hr );
}


//-----------------------------------------------------------------------------
// CDBProperties::GetProperty
//
// @mfunc Looks up a property by its property set GUID and ID.
//
// @rdesc Pointer to DBPROP for the property, or 0 if not found.
//-----------------------------------------------------------------------------

const DBPROP*
CDBProperties::GetProperty(const GUID& guid, DBPROPID dwId) const
{
        ULONG iProp;
        const DBPROPSET* pPropSet = GetPropertySet(guid);
        const DBPROP*   pProp = 0;              // the answer, assume not found

        if (pPropSet == 0)                      // no properties for desired property set
                goto Cleanup;

        // look up the desired property in the property set
        for (iProp=0; iProp<pPropSet->cProperties; ++iProp)
        {
                if (dwId == pPropSet->rgProperties[iProp].dwPropertyID)
                {
                        pProp = & pPropSet->rgProperties[iProp];
                        break;
                }
        }

Cleanup:
        return ( pProp );
}


//-----------------------------------------------------------------------------
// CDBProperties::GetPropertyInfo
//
// @mfunc Looks up a property info by its property set GUID and ID.
//
// @rdesc Pointer to DBPROPINFO for the property info, or 0 if not found.
//-----------------------------------------------------------------------------

const DBPROPINFO UNALIGNED*
CDBProperties::GetPropertyInfo(const GUID& guid, DBPROPID dwId) const
{
        ULONG iPropInfo;
        const DBPROPINFOSET* pPropInfoSet = GetPropertyInfoSet(guid);
        const DBPROPINFO UNALIGNED* pPropInfo = 0;          // the answer, assume not found

        if (pPropInfoSet == 0)                  // no properties for desired property set
                goto Cleanup;

        // look up the desired property in the property set
        for (iPropInfo=0; iPropInfo <pPropInfoSet->cPropertyInfos; ++iPropInfo)
        {
                if (dwId == pPropInfoSet->rgPropertyInfos[iPropInfo].dwPropertyID)
                {
                        pPropInfo = & pPropInfoSet->rgPropertyInfos[iPropInfo];
                        break;
                }
        }

Cleanup:
        return ( pPropInfo );
}

//-----------------------------------------------------------------------------
// CDBProperties::SetProperty
//
// @mfunc Adds a new property, or resets an existing one
//        This overloaded function is same as the other except that the
//        last parameter is of type PWSTR [mgorti]
//
// @rdesc
//              @flag S_OK                      | property added/reset,
//              @flag E_OUTOFMEMORY | no memory for new property set or new property.
//-----------------------------------------------------------------------------

HRESULT
CDBProperties::SetProperty(const GUID& guid,
                                                   const DBPROP& prop,
                                                   BOOL fAddNew,
                                                   PWSTR pwszDesc)
{
        HRESULT hr;
        DBPROP *pProp;                  // pointer to array entry for new property
        ULONG iProp;
        DBPROPSET* pPropSet = GetPropertySet(guid);

        if (pPropSet == 0)              // no properties yet in desired property set
        {
                if(!fAddNew)
                {
                        hr = E_FAIL;
                        goto Cleanup;
                }

                // get a new property set array
                DBPROPSET * aNewPropSets = new DBPROPSET[_cPropSets + 1];
                if (aNewPropSets == 0)
                {
                        hr = E_OUTOFMEMORY;
                        goto Cleanup;
                }
                memcpy(aNewPropSets, _aPropSets, _cPropSets *sizeof(DBPROPSET));

                // add the new property set
                pPropSet = & aNewPropSets[_cPropSets];
                pPropSet->guidPropertySet = guid;
                pPropSet->cProperties = 0;
                pPropSet->rgProperties = 0;

                // release the old array, install the new one
                delete [] _aPropSets;
                _aPropSets = aNewPropSets;
                ++ _cPropSets;
        }

        // look for the desired property.
        if(!fAddNew)
        {
                pProp = 0;
                for (iProp=0; iProp<pPropSet->cProperties; ++iProp)
                {
                        if (pPropSet->rgProperties[iProp].dwPropertyID ==
                                prop.dwPropertyID)
                        {
                                pProp = &pPropSet->rgProperties[iProp];
                                break;
                        }
                }
                if (pProp == 0)
                {
                        hr = E_FAIL;
                        goto Cleanup;
                }
        }

        // if it's a new property, add it.  OLE-DB doesn't provide for any "unused"
        // portion in the array of DBPROPS, so we must reallocate the array every
        // time we add a property.
        else
        {
                ULONG cPropLeftOver;

                // allocate new property array
                cPropLeftOver =
                        C_PROP_INCR -
                                (pPropSet->cProperties + C_PROP_INCR - 1)%C_PROP_INCR - 1;
                if(cPropLeftOver)
                {
                        pProp = &pPropSet->rgProperties[pPropSet->cProperties];
                }
                else
                {
                        DBPROP* aNewProperties =
                                new DBPROP[pPropSet->cProperties + C_PROP_INCR];
                        if (aNewProperties == 0)
                        {
                                hr = E_OUTOFMEMORY;
                                goto Cleanup;
                        }

                        // copy old array into new
                        memcpy( aNewProperties,
                                        pPropSet->rgProperties,
                                        pPropSet->cProperties *sizeof(DBPROP));

                        // prepare to use new property entry
                        pProp = & aNewProperties[pPropSet->cProperties];

                        // release old array, install new
                        delete [] pPropSet->rgProperties;
                        pPropSet->rgProperties = aNewProperties;
                }
                ++ pPropSet->cProperties;
        }

        // copy the property into my array
        if(!fAddNew)
        {
                DBPROP propSave;

                propSave = *pProp;
                *pProp = prop;
                VariantInit(&(pProp->vValue));
                if(FAILED(hr = VariantCopy(     &(pProp->vValue),
                                                                        (VARIANT *)&(prop.vValue))))
                {
                        *pProp = propSave;
                        goto Cleanup;
                }
        }
        else
        {
                DBPROPINFO propinfo;

                *pProp = prop;
                propinfo.pwszDescription = pwszDesc;
                propinfo.dwPropertyID = prop.dwPropertyID;
                propinfo.dwFlags = DBPROPFLAGS_READ;
                if(guid == DBPROPSET_DBINIT)
                        propinfo.dwFlags |= DBPROPFLAGS_DBINIT;
                else if(guid == DBPROPSET_DATASOURCEINFO)
                        propinfo.dwFlags |= DBPROPFLAGS_DATASOURCEINFO;
                else
                        propinfo.dwFlags |= DBPROPFLAGS_ROWSET;
                propinfo.vtType = V_VT(&(prop.vValue));
                VariantInit(&(propinfo.vValues));
                if(FAILED(hr = SetPropertyInfo(guid, propinfo)))
                        goto Cleanup;
        }

        hr = S_OK;

Cleanup:
        RRETURN ( hr );
}

//-----------------------------------------------------------------------------
// CDBProperties::SetPropertyInfo
//
// @mfunc Adds a new property info, or resets an existing one.
//
// @rdesc
//              @flag S_OK                      | property info added/reset,
//              @flag E_OUTOFMEMORY | no memory for new property info set
//                                                        or new property info.
//-----------------------------------------------------------------------------

HRESULT
CDBProperties::SetPropertyInfo(const GUID& guid, const DBPROPINFO& propinfo)
{
        HRESULT hr;
        PDBPROPINFO pPropInfo;          // pointer to array entry for new property
        ULONG iPropInfo;
        DBPROPINFOSET* pPropInfoSet = GetPropertyInfoSet(guid);

        if (pPropInfoSet == 0)          // no properties yet in desired property set
        {

                // get a new property set array
                DBPROPINFOSET * aNewPropInfoSets =
                        new DBPROPINFOSET[_cPropInfoSets + 1];

                if (aNewPropInfoSets == 0)
                {
                        hr = E_OUTOFMEMORY;
                        goto Cleanup;
                }

                memcpy( aNewPropInfoSets,
                                _aPropInfoSets,
                                _cPropInfoSets *sizeof(DBPROPINFOSET));

                // add the new property set
                pPropInfoSet = & aNewPropInfoSets[_cPropInfoSets];
                pPropInfoSet->guidPropertySet = guid;
                pPropInfoSet->cPropertyInfos = 0;
                pPropInfoSet->rgPropertyInfos = 0;

                // release the old array, install the new one
                delete [] _aPropInfoSets;
                _aPropInfoSets = aNewPropInfoSets;
                ++ _cPropInfoSets;
        }

        // look for the desired property.
        pPropInfo = 0;
        for (iPropInfo=0; iPropInfo<pPropInfoSet->cPropertyInfos; ++iPropInfo)
        {
                if (pPropInfoSet->rgPropertyInfos[iPropInfo].dwPropertyID ==
                        propinfo.dwPropertyID)
                {
                        pPropInfo = &pPropInfoSet->rgPropertyInfos[iPropInfo];
                        break;
                }
        }

        // if it's a new property, add it.  OLE-DB doesn't provide for any "unused"
        // portion in the array of DBPROPS, so we must reallocate the array every
        // time we add a property.
        if (pPropInfo == 0)
        {
                ULONG cPropLeftOver;

                // allocate new property array
                cPropLeftOver =
                        C_PROP_INCR -
                                (pPropInfoSet->cPropertyInfos + C_PROP_INCR - 1)%C_PROP_INCR - 1;

                if(cPropLeftOver)
                {
                        pPropInfo =
                                &pPropInfoSet->rgPropertyInfos[pPropInfoSet->cPropertyInfos];
                }
                else
                {
                        DBPROPINFO* aNewPropertyInfos =
                                new DBPROPINFO[pPropInfoSet->cPropertyInfos + C_PROP_INCR];

                        if (aNewPropertyInfos == 0)
                        {
                                hr = E_OUTOFMEMORY;
                                goto Cleanup;
                        }

                        // copy old array into new
                        memcpy( aNewPropertyInfos,
                                        pPropInfoSet->rgPropertyInfos,
                                        pPropInfoSet->cPropertyInfos *sizeof(DBPROPINFO));

                        // prepare to use new property entry
                        pPropInfo = & aNewPropertyInfos[pPropInfoSet->cPropertyInfos];

                        // release old array, install new
                        delete [] pPropInfoSet->rgPropertyInfos;
                        pPropInfoSet->rgPropertyInfos = aNewPropertyInfos;
                }
                ++ pPropInfoSet->cPropertyInfos;
        }

        // copy the property into my array
        *pPropInfo = propinfo;

        hr = S_OK;

Cleanup:
        RRETURN ( hr );
}


//-----------------------------------------------------------------------------
// CDBProperties::LoadDescription
//
// @mfunc Loads a localized string from the localization DLL.
//
// @rdesc Count of characters returned in the buffer.
//-----------------------------------------------------------------------------

int
CDBProperties::LoadDescription
        (
        ULONG   ids,            //@parm IN | String ID
        PWSTR   pwszBuff,       //@parm OUT | Temporary buffer
        ULONG   cchBuff         //@parm IN | Count of characters buffer can hold
        ) const
{
        return ( 0 );
//              return( LoadStringW(g_hinstDll, ids, pwszBuff, cchBuff) );
}


//-----------------------------------------------------------------------------
// CDBProperties::CopyPropertyDescriptions
//
// @mfunc Copies into a buffer descriptions of properties in a given set.
//
// @rdesc
//              @flag S_OK                      | copying of property descriptions succeeded,
//              @flag E_OUTOFMEMORY | buffer for property descriptions could not
//                                                        be allocated/extended.
//-----------------------------------------------------------------------------

HRESULT
CDBProperties::CopyPropertyDescriptions
        (
        DBPROPINFOSET*  pPropInfoSet,
        WCHAR**                 ppDescBuffer,
        ULONG_PTR*                  pcchDescBuffer,
        ULONG_PTR*                  pichCurrent
        ) const
{
        LONG iprop, cchLeft, cchNew;
        int cchCopied;
        WCHAR *pwszTmp;

        if(ppDescBuffer)
        {
                cchLeft = (LONG)*pcchDescBuffer - (LONG)*pichCurrent;
                for(iprop =0; (ULONG)iprop <pPropInfoSet->cPropertyInfos; iprop++)
                {
                        if(pPropInfoSet->rgPropertyInfos[iprop].dwFlags ==
                                DBPROPFLAGS_NOTSUPPORTED)
                                continue;

                        if(cchLeft < (LONG)CCHAR_MAX_PROP_STR_LENGTH)
                        {
                                cchNew = CCHAR_AVERAGE_PROP_STR_LENGTH *
                                                        (pPropInfoSet->cPropertyInfos - iprop - 1) +
                                                                CCHAR_MAX_PROP_STR_LENGTH +
                                                                        *pcchDescBuffer - cchLeft;

                                pwszTmp = (WCHAR *)CoTaskMemAlloc(cchNew *sizeof(WCHAR));
                                if(pwszTmp == NULL)
                                        RRETURN ( E_OUTOFMEMORY );

                                if(*ppDescBuffer)
                                {
                                        memcpy( pwszTmp,
                                                        *ppDescBuffer,
                                                        (*pcchDescBuffer -cchLeft)*sizeof(WCHAR));

                                        CoTaskMemFree(*ppDescBuffer);
                                }
                                cchLeft += cchNew -(LONG)*pcchDescBuffer;
                                *ppDescBuffer = pwszTmp;
                                *pcchDescBuffer = cchNew;
                        }

                        //?? Do we need to load these strings from resources ??
                        //$TODO$ Raid #86943 Copy property descriptions from source to destination buffer.
                        cchCopied = wcslen(pPropInfoSet->rgPropertyInfos[iprop].pwszDescription);
                        wcscpy((WCHAR *)(*ppDescBuffer) + *pichCurrent,
                          pPropInfoSet->rgPropertyInfos[iprop].pwszDescription);
                        pPropInfoSet->rgPropertyInfos[iprop].pwszDescription =
                                (WCHAR *)(*pichCurrent);

                        *pichCurrent += (cchCopied +1);
                        cchLeft -= (cchCopied +1);
                }
        }
        else {
                //      We need to NULL out the pwszDescription values:
                //

                for(iprop =0; (ULONG)iprop <pPropInfoSet->cPropertyInfos; iprop++)
                {
                        pPropInfoSet->rgPropertyInfos[iprop].pwszDescription = NULL;
                }
        }

        RRETURN ( NOERROR );
}

//-----------------------------------------------------------------------------
// CDBProperties::CheckAndInitPropArgs
//
// @mfunc Helper function used while getting property sets.
//        Used to check and get information about property sets.
//        Tells if the caller is requesting
//        special sets or the set of properties in error.
//
// @rdesc
//              @flag S_OK                      | check succeeded,
//              @flag E_INVALIDARG  | one of the arguments is invalid.
//-----------------------------------------------------------------------------
HRESULT
CDBProperties::CheckAndInitPropArgs
        (
        ULONG                           cPropertySets,    // IN | Number of property sets
        const DBPROPIDSET       rgPropertySets[], // IN | Property Sets
        ULONG                           *pcPropertySets,  // OUT | Count of structs returned
        void                            **prgPropertySets,// OUT | Array of Properties
        BOOL                            *pfPropInError,
        BOOL                            *pfPropSpecial
        )
{
        LONG    ipropset;
        ULONG   cpropsetSpecial;

        // Initialize
        if( pcPropertySets )
                *pcPropertySets = 0;
        if( prgPropertySets )
                *prgPropertySets = NULL;
        if(pfPropInError)
                *pfPropInError = FALSE;
        if(pfPropSpecial)
                *pfPropSpecial = FALSE;

        // Check Arguments, on failure post HRESULT to error queue
        if( ((cPropertySets > 0) && !rgPropertySets) ||
                !pcPropertySets ||
                !prgPropertySets )
                RRETURN ( E_INVALIDARG );

        // New argument check for > 1 cPropertyIDs and NULL pointer for
        // array of property ids.
        for(ipropset=0, cpropsetSpecial = 0;
            (ULONG)ipropset<cPropertySets;
                ipropset++)
        {
                if( rgPropertySets[ipropset].cPropertyIDs &&
                        !(rgPropertySets[ipropset].rgPropertyIDs) )
                        RRETURN (E_INVALIDARG);

                //when passing property set DBPROPSET_PROPERTIESINERROR,
                //this is the only set the caller can ask. Also, the
                //count of propertyIDs and the propertyID array must be
                //NULL in this case.
                if( rgPropertySets[ipropset].guidPropertySet ==
                        DBPROPSET_PROPERTIESINERROR )
                {
                        if(pfPropInError)
                        {
                                if(cPropertySets >1
                                   || rgPropertySets[ipropset].cPropertyIDs
                                   || rgPropertySets[ipropset].rgPropertyIDs)
                                        RRETURN (E_INVALIDARG);
                                else
                                        *pfPropInError = TRUE;
                        }
                }
                //Count the number of special property sets being asked.
                else if( rgPropertySets[ipropset].guidPropertySet ==
                                    DBPROPSET_DATASOURCEALL
                          || rgPropertySets[ipropset].guidPropertySet ==
                                    DBPROPSET_DATASOURCEINFOALL
                          || rgPropertySets[ipropset].guidPropertySet ==
                                    DBPROPSET_DBINITALL
                          || rgPropertySets[ipropset].guidPropertySet ==
                                    DBPROPSET_SESSIONALL
                          || rgPropertySets[ipropset].guidPropertySet ==
                                    DBPROPSET_ROWSETALL)
                        cpropsetSpecial++;
        }

        //When requesting special property sets, all of them
        //must be special or none.
        if(cpropsetSpecial)
        {
                if(pfPropSpecial)
                        *pfPropSpecial = TRUE;
                if(cpropsetSpecial < cPropertySets)
                        RRETURN (E_INVALIDARG);
        }
        else if(pfPropSpecial)
                *pfPropSpecial = FALSE;

        RRETURN ( NOERROR );
}

//-----------------------------------------------------------------------------
// CDBProperties::VerifySetPropertiesArgs
//
// @mfunc Helper function used in IDBProperties::SetProperties. Validates
//        arguments passed to IDBProperties::SetProperties.
//
// @rdesc
//              @flag S_OK                      | Validation succeeded.
//              @flag E_INVALIDARG  | Validation failed - one of the arguments
//                                                        is in error.
//-----------------------------------------------------------------------------
HRESULT
CDBProperties::VerifySetPropertiesArgs
        (
        ULONG           cPropertySets,          //@parm IN | Count of properties
        DBPROPSET       rgPropertySets[]        //@parm IN | Properties
        )
{
        ULONG ipropset;

        if(cPropertySets && rgPropertySets == NULL)
                RRETURN (E_INVALIDARG);

        for(ipropset =0; ipropset <cPropertySets; ipropset++)
                if(     rgPropertySets[ipropset].cProperties &&
                        rgPropertySets[ipropset].rgProperties == NULL)
                        RRETURN (E_INVALIDARG);

        RRETURN ( NOERROR );
}


//-----------------------------------------------------------------------------
// VariantsEqual
//
// @mfunc Tests two variants holding property values for equality.
//
// @rdesc
//              @flag TRUE      | values equal,
//              @flag FALSE | values unequal.
//-----------------------------------------------------------------------------
BOOL VariantsEqual
        (
        VARIANT *pvar1,
        VARIANT *pvar2
        )
{
        if(V_VT(pvar1) != V_VT(pvar1))
                return ( FALSE );
        else if(V_VT(pvar1) == VT_I2)
                return (V_I2(pvar1) == V_I2(pvar2));
        else if(V_VT(pvar1) == VT_BOOL)
                return (V_BOOL(pvar1) == V_BOOL(pvar2));
        else if(V_VT(pvar1) == VT_BSTR)
        {
                if(V_BSTR(pvar1) == NULL || V_BSTR(pvar2) == NULL)
                        return (V_BSTR(pvar1) == V_BSTR(pvar2));
                else
                        return (wcscmp(V_BSTR(pvar1), V_BSTR(pvar2)) == 0);
        }
        else
                return (V_I4(pvar1) == V_I4(pvar2));
}

#endif