#include "ctlspriv.h"
#include <memory.h>

#define MRU_ORDERDIRTY 0x1000

#define DM_MRULAZY  DM_TRACE

#define MAX_CHAR    126
#define BASE_CHAR   TEXT('a')

typedef struct tagMRUDATA
{
    UINT fFlags;
    UINT uMax;
    LPVOID lpfnCompare;
    HKEY hKey;
#ifdef DEBUG
    TCHAR szSubKey[32];
#endif
    LPTSTR cOrder;
} MRUDATA, *PMRUDATA;

#define c_szMRU     TEXT("MRUList")

#define NTHSTRING(p, n) (*((LPTSTR *)((LPBYTE)p+sizeof(MRUDATA))+n))
#define NTHDATA(p, n) (*((LPBYTE *)((LPBYTE)p+sizeof(MRUDATA))+n))
#define NUM_OVERHEAD 3


#ifdef VSTF

/*----------------------------------------------------------
Purpose: Validate the MRU structure
*/
BOOL IsValidPMRUDATA(PMRUDATA pmru)
{
    return (IS_VALID_WRITE_PTR(pmru, MRUDATA) &&
            (NULL == pmru->lpfnCompare || IS_VALID_CODE_PTR(pmru->lpfnCompare, void)));
}

#endif // VSTF

//----------------------------------------------------------------------------
// Internal memcmp - saves loading crt's, cdecl so we can use
// as MRUCMPDATAPROC

int CDECL _mymemcmp(const void *pBuf1, const void *pBuf2, size_t cb)
{
    // Take advantage of the intrinsic version from crtfree.h
    return memcmp(pBuf1, pBuf2, cb);
}


// Use this macro to get the original size of the data.
#define DATASIZE(p)     (*((LPDWORD)p))
// And this to get a pointer to the original data.
#define DATAPDATA(p)    (p+sizeof(DWORD))

//----------------------------------------------------------------------------
//  For binary data we stick the size of the data at the begining and store the
//  whole thing in one go.
BOOL MRUIsSameData(PMRUDATA pMRU, BYTE* pVal, const void *lpData, UINT cbData)
{
    int cbUseSize;
    MRUCMPDATAPROC lpfnCompare;

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    lpfnCompare = pMRU->lpfnCompare;

    ASSERT(IS_VALID_CODE_PTR(lpfnCompare, MRUCMPDATAPROC));

    // if there's something other than a mem compare,
    // don't require the sizes to be equal in order for the
    // data to be equivalent.

    if (pMRU->lpfnCompare == _mymemcmp)
    {
        if (DATASIZE(pVal) != cbData)
            return FALSE;

        cbUseSize = cbData;
    }
    else
        cbUseSize = min(DATASIZE(pVal), cbData);

    return ((*lpfnCompare)(lpData, DATAPDATA(pVal), cbUseSize) == 0);
}


//----------------------------------------------------------------------------
HANDLE WINAPI CreateMRUListLazy(LPMRUINFO lpmi, const void *lpData, UINT cbData, LPINT lpiSlot)
{
    PTSTR pOrder, pNewOrder, pTemp;
    LPBYTE pVal;
    LONG cbVal;
    DWORD dwDisposition;
    DWORD dwType;
    PMRUDATA pMRU = NULL;
    HKEY hkeySubKey = NULL;
    TCHAR szTemp[2];
    UINT uMax = lpmi->uMax;
    HKEY hKey = lpmi->hKey;
    LPCTSTR lpszSubKey = lpmi->lpszSubKey;
    MRUCMPPROC lpfnCompare = lpmi->lpfnCompare;
    int cb;

#ifdef DEBUG
    DWORD dwStart = GetTickCount();
#endif
    if (!lpfnCompare) 
    {
        lpfnCompare = (lpmi->fFlags & MRU_BINARY) ? (MRUCMPPROC)_mymemcmp :
                      ((lpmi->fFlags & MRU_ANSI) ? (MRUCMPPROC)lstrcmpiA :
                       (MRUCMPPROC)lstrcmpi);
    }

    //  limit to 126 so that we don't use extended chars
    if (uMax > MAX_CHAR-BASE_CHAR)
        uMax = MAX_CHAR-BASE_CHAR;

    if (RegCreateKeyEx(hKey, lpszSubKey, 0L, (LPTSTR)c_szShell, REG_OPTION_NON_VOLATILE,
                       KEY_READ | KEY_WRITE, NULL, &hkeySubKey, &dwDisposition) != ERROR_SUCCESS)
    {
        goto Error1;
    }

    pOrder = (PTSTR)Alloc((uMax + 1) * sizeof(TCHAR));
    if (!pOrder)
    {
        goto Error1;
    }

    cbVal = ((LONG)uMax + 1) * sizeof(TCHAR);

    if (RegQueryValueEx(hkeySubKey, (LPTSTR)c_szMRU, NULL, &dwType, (LPBYTE)pOrder, &cbVal) != ERROR_SUCCESS)
    {
        // if not already in the registry, then start fresh
        *pOrder = 0;
    }

    // Uppercase is not allowed
    CharLower(pOrder);

    // We allocate room for the MRUDATA structure, plus the order list,
    // and the list of strings.
    cb = (lpmi->fFlags & MRU_BINARY) ? sizeof(LPBYTE) : sizeof(LPTSTR);
    pMRU = (PMRUDATA)Alloc(sizeof(MRUDATA)+(uMax*cb));
    if (!pMRU)
    {
        goto Error2;
    }

    // Allocate space for the order list
    pMRU->cOrder = (LPTSTR)Alloc((uMax+1)*sizeof(TCHAR));
    if (!pMRU->cOrder) 
    {
        Free(pMRU);
        pMRU = NULL;
        goto Error2;
    }

    pMRU->fFlags = lpmi->fFlags;
    pMRU->uMax = uMax;
    pMRU->lpfnCompare = lpfnCompare;
    pMRU->hKey = hkeySubKey;
#ifdef DEBUG
    lstrcpyn(pMRU->szSubKey, lpszSubKey, ARRAYSIZE(pMRU->szSubKey));
#endif

    // Traverse through the MRU list, adding strings to the end of the
    // list.
    szTemp[1] = TEXT('\0');
    for (pTemp = pOrder, pNewOrder = pMRU->cOrder; ; ++pTemp)
    {
        // Stop when we get to the end of the list.
        szTemp[0] = *pTemp;
        if (!szTemp[0]) 
        {
            break;
        }

        if (lpmi->fFlags & MRU_BINARY) 
        {
            // Check if in range and if we have already used this letter.
            if ((UINT)(szTemp[0]-BASE_CHAR)>=uMax || NTHDATA(pMRU, szTemp[0]-BASE_CHAR)) 
            {
                continue;
            }
            // Get the value from the registry
            cbVal = 0;
            // first find the size
            if ((RegQueryValueEx(hkeySubKey, szTemp, NULL, &dwType, NULL, &cbVal)
                 != ERROR_SUCCESS) || (dwType != REG_BINARY))
            {
                continue;
            }

            // Binary data has the size at the begining so we'll need a little extra room.
            pVal = (LPBYTE)Alloc(cbVal + sizeof(DWORD));

            if (!pVal) 
            {
                // REARCHITECT perhaps sort of error is in order.
                continue;
            }

            // now really get it
            DATASIZE(pVal) = cbVal;
            if (RegQueryValueEx(hkeySubKey, szTemp, NULL, &dwType, pVal+sizeof(DWORD),
                                (LPDWORD)pVal) != ERROR_SUCCESS)
            {
                continue;
            }

            // Note that blank elements ARE allowed in the list.
            NTHDATA(pMRU, szTemp[0]-BASE_CHAR) = pVal;
            *pNewOrder++ = szTemp[0];

            //
            // OPTIMIZATION
            //   If lpData and lpiSlot are specified, we stop the enumeratation
            //  when we find the item.
            //
            if (lpData && lpiSlot) 
            {
                // Check if we have the specified one or not.
                if (MRUIsSameData(pMRU, pVal, lpData, cbData)) 
                {
                    // Found it.
                    *lpiSlot = (INT) (pNewOrder - pMRU->cOrder);

                    TraceMsg(DM_MRULAZY, "CreateMRUListLazy found it. Copying %s", pTemp);

                    pMRU->fFlags |= MRU_LAZY;
                    //
                    // Copy the rest of slot. Notice that we don't load
                    // data for those slot.
                    //
                    for (pTemp++; *pTemp; pTemp++) 
                    {
                        *pNewOrder++ = *pTemp;
                    }
                    break;
                }
            }
        } 
        else 
        {

            // Check if in range and if we have already used this letter.
            if ((UINT)(szTemp[0]-BASE_CHAR)>=uMax || NTHSTRING(pMRU, szTemp[0]-BASE_CHAR)) 
            {
                continue;
            }
            // Get the value from the registry
            cbVal = 0;
            // first find the size
            if ((RegQueryValueEx(hkeySubKey, szTemp, NULL, &dwType, NULL, &cbVal)
                 != ERROR_SUCCESS) || (dwType != REG_SZ))
            {
                continue;
            }

            cbVal *= sizeof(TCHAR);
            pVal = (LPBYTE)Alloc(cbVal);

            if (!pVal) 
            {
                // REARCHITECT perhaps sort of error is in order.
                continue;
            }
            // now really get it
            if (RegQueryValueEx(hkeySubKey, szTemp, NULL, &dwType, (LPBYTE)pVal, &cbVal) != ERROR_SUCCESS)
            {
                continue;
            }

            // Note that blank elements are not allowed in the list.
            if (*((LPTSTR)pVal)) 
            {
                NTHSTRING(pMRU, szTemp[0]-BASE_CHAR) = (LPTSTR)pVal;
                *pNewOrder++ = szTemp[0];
            } 
            else 
            {
                Free(pVal);
                pVal = NULL;
            }
        }
    }
    /* NULL terminate the order list so we can tell how many strings there
     * are.
     */
    *pNewOrder = TEXT('\0');

    if (lpData && lpiSlot) 
    {
        TraceMsg(DM_MRULAZY, "CreateMRUListLazy. End of loop. %s", pMRU->cOrder);
        // If we failed to find, put -1 in it.
        if (!(pMRU->fFlags & MRU_LAZY)) 
        {
            *lpiSlot = -1;
        }
    }

    /* Actually, this is success rather than an error.
     */
    goto Error2;

Error2:
    if (pOrder)
    {
        Free((HLOCAL)pOrder);
        pOrder = NULL;
    }

Error1:
    if (!pMRU && hkeySubKey)
        RegCloseKey(hkeySubKey);

    return((HANDLE)pMRU);
}

HANDLE WINAPI CreateMRUList(LPMRUINFO lpmi)
{
    return CreateMRUListLazy(lpmi, NULL, 0, NULL);
}

#ifdef UNICODE

//
// ANSI thunk
//

HANDLE WINAPI CreateMRUListLazyA(LPMRUINFOA lpmi, const void *lpData, UINT cbData, LPINT lpiSlot)
{
    MRUINFOW MRUInfoW;
    HANDLE hMRU;

    MRUInfoW.cbSize       = sizeof (MRUINFOW);
    MRUInfoW.uMax         = lpmi->uMax;
    MRUInfoW.fFlags       = lpmi->fFlags;
    MRUInfoW.hKey         = lpmi->hKey;
    MRUInfoW.lpszSubKey   = ProduceWFromA(CP_ACP, lpmi->lpszSubKey);
    MRUInfoW.lpfnCompare  = (MRUCMPPROCW)lpmi->lpfnCompare;

    MRUInfoW.fFlags |= MRU_ANSI;

    hMRU = CreateMRUListLazy(&MRUInfoW, lpData, cbData, lpiSlot);

    FreeProducedString((LPWSTR)MRUInfoW.lpszSubKey);

    return hMRU;
}

HANDLE WINAPI CreateMRUListA(LPMRUINFOA lpmi)
{
    return CreateMRUListLazyA(lpmi, NULL, 0, NULL);
}
#else

//
// Unicode stub when this code is built ANSI
//

HANDLE WINAPI CreateMRUListW(LPMRUINFOW lpmi)
{
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return NULL;
}

HANDLE WINAPI CreateMRUListLazyW(LPMRUINFOW lpmi, const void *lpData, UINT cbData, LPINT lpiSlot)
{
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return NULL;
}
#endif


//----------------------------------------------------------------------------
STDAPI_(void) FreeMRUList(HANDLE hMRU)
{
    int i;
    LPVOID *pTemp;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (pMRU)
    {
        pTemp = (pMRU->fFlags & MRU_BINARY) ?
            &NTHDATA(pMRU, 0) : (LPBYTE *)&NTHSTRING(pMRU, 0);

        if (pMRU->fFlags & MRU_ORDERDIRTY)
        {
            RegSetValueEx(pMRU->hKey, c_szMRU, 0L, REG_SZ, (CONST BYTE *)pMRU->cOrder,
                          sizeof(TCHAR) * (lstrlen(pMRU->cOrder) + 1));
        }

        for (i=pMRU->uMax-1; i>=0; --i, ++pTemp)
        {
            if (*pTemp)
            {
                if (pMRU->fFlags & MRU_BINARY)
                {
                    Free((LPBYTE)*pTemp);
                    *pTemp = NULL;
                }
                else
                    Str_SetPtr((LPTSTR *)pTemp, NULL);
            }
        }
        RegCloseKey(pMRU->hKey);
        Free(pMRU->cOrder);
        Free((HLOCAL)pMRU);
    }
}


/* Add a string to an MRU list.
 */
STDAPI_(int) AddMRUString(HANDLE hMRU, LPCTSTR szString)
{
    /* The extra +1 is so that the list is NULL terminated.
    */
    TCHAR cFirst;
    int iSlot = -1;
    LPTSTR lpTemp;
    LPTSTR * pTemp;
    int i;
    UINT uMax;
    MRUCMPPROC lpfnCompare;
    BOOL fShouldWrite;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

#ifdef DEBUG
    DWORD dwStart = GetTickCount();
#endif

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (hMRU == NULL)
        return(-1);     // Error

    fShouldWrite = !(pMRU->fFlags & MRU_CACHEWRITE);
    uMax = pMRU->uMax;
    lpfnCompare = (MRUCMPPROC)pMRU->lpfnCompare;

    /* Check if the string already exists in the list.
    */
    for (i=0, pTemp=&NTHSTRING(pMRU, 0); (UINT)i<uMax; ++i, ++pTemp)
    {
        if (*pTemp)
        {
            int iResult;

#ifdef UNICODE
            if (pMRU->fFlags & MRU_ANSI)
            {
                LPSTR lpStringA, lpTempA;

                lpStringA = ProduceAFromW (CP_ACP, szString);
                lpTempA = ProduceAFromW (CP_ACP, (LPWSTR)*pTemp);

                iResult = (*lpfnCompare)((const void *)lpStringA, (const void *)lpTempA);

                FreeProducedString (lpStringA);
                FreeProducedString (lpTempA);
            }
            else
#endif
            {
                iResult = (*lpfnCompare)((const void *)szString, (const void *)*pTemp);
            }

            if (!iResult)
            {
                // found it, so don't do the write out
                cFirst = i + BASE_CHAR;
                iSlot = i;
                goto FoundEntry;
            }
        }
    }

    /* Attempt to find an unused entry.  Count up the used entries at the
    * same time.
    */
    for (i=0, pTemp=&NTHSTRING(pMRU, 0); ; ++i, ++pTemp)
    {
        if ((UINT)i >= uMax)    // If we got to the end of the list.
        {
            // use the entry at the end of the cOrder list
            cFirst = pMRU->cOrder[uMax-1];
            pTemp = &NTHSTRING(pMRU, cFirst-BASE_CHAR);
            break;
        }

        // Is the entry not used?
        if (!*pTemp)
        {
            // yes
            cFirst = i+BASE_CHAR;
            break;
        }
    }

    if (Str_SetPtr(pTemp, szString))
    {
        TCHAR szTemp[2];

        iSlot = (int)(cFirst-BASE_CHAR);

        szTemp[0] = cFirst;
        szTemp[1] = TEXT('\0');

        RegSetValueEx(pMRU->hKey, szTemp, 0L, REG_SZ, (CONST BYTE *)szString,
            sizeof(TCHAR) * (lstrlen(szString) + 1));

        fShouldWrite = TRUE;
    }
    else
    {
        /* Since iSlot == -1, we will remove the reference to cFirst
        * below.
        */
    }

FoundEntry:
    /* Remove any previous reference to cFirst.
    */
    lpTemp = StrChr(pMRU->cOrder, cFirst);
    if (lpTemp)
    {
        lstrcpy(lpTemp, lpTemp+1);
    }

    if (iSlot != -1)
    {
        // shift everything over and put cFirst at the front
        hmemcpy(pMRU->cOrder+1, pMRU->cOrder, pMRU->uMax*sizeof(TCHAR));
        pMRU->cOrder[0] = cFirst;
    }

    if (fShouldWrite)
    {
        RegSetValueEx(pMRU->hKey, c_szMRU, 0L, REG_SZ, (CONST BYTE *)pMRU->cOrder,
            sizeof(TCHAR) * (lstrlen(pMRU->cOrder) + 1));
        pMRU->fFlags &= ~MRU_ORDERDIRTY;
    } else
        pMRU->fFlags |= MRU_ORDERDIRTY;

#ifdef DEBUG
    // DebugMsg(DM_TRACE, TEXT("AddMRU: %d msec"), LOWORD(GetTickCount()-dwStart));
#endif
    return(iSlot);
}


#ifdef UNICODE
//
// ANSI thunk
//

STDAPI_(int) AddMRUStringA(HANDLE hMRU, LPCSTR szString)
{
    LPWSTR lpStringW;
    INT    iResult;

    lpStringW = ProduceWFromA(CP_ACP, szString);

    iResult = AddMRUString(hMRU, lpStringW);

    FreeProducedString (lpStringW);

    return iResult;
}

#else

//
// Unicode stub when this code is build ANSI
//

STDAPI_(int) AddMRUStringW(HANDLE hMRU, LPCWSTR szString)
{
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return -1;
}

#endif


/* Remove a string from an MRU list.
 */
STDAPI_(int) DelMRUString(HANDLE hMRU, int nItem)
{
    BOOL bRet = FALSE;
    LPTSTR lpTemp;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (pMRU)
    {
        //
        // Make sure the index value is within the length of
        // the string so we don't pick up some random value.
        //
        if (!InRange(nItem, 0, pMRU->uMax) || nItem >= lstrlen(pMRU->cOrder))
            return FALSE;

        // Be easy -- just remove the entry from the cOrder list
        lpTemp = &pMRU->cOrder[nItem];
        if (lpTemp)
        {
            int iSlot = *lpTemp - BASE_CHAR;
            if (iSlot >= 0 && iSlot < MAX_CHAR - BASE_CHAR)
                Str_SetPtr(&NTHSTRING(pMRU, iSlot), NULL);
            lstrcpy(lpTemp, lpTemp+1);

            if (!(pMRU->fFlags & MRU_CACHEWRITE))
            {
                RegSetValueEx(pMRU->hKey, c_szMRU, 0L, REG_SZ, (CONST BYTE *)pMRU->cOrder,
                              sizeof(TCHAR) * (lstrlen(pMRU->cOrder) + 1));
                pMRU->fFlags &= ~MRU_ORDERDIRTY;
            }
            else
            {
                pMRU->fFlags |= MRU_ORDERDIRTY;
            }

            bRet = TRUE;
        }
    }

    return bRet;
}


//----------------------------------------------------------------------------
// Add data to an MRU list.
STDAPI_(int) AddMRUData(HANDLE hMRU, const void *lpData, UINT cbData)
{
    TCHAR cFirst;
    int iSlot = -1;
    LPTSTR lpTemp;
    LPBYTE *ppData;
    int i;
    UINT uMax;
    MRUCMPDATAPROC lpfnCompare;
    PMRUDATA pMRU = (PMRUDATA)hMRU;
    BOOL fShouldWrite;

#ifdef DEBUG
    DWORD dwStart = GetTickCount();
#endif

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (hMRU == NULL)
        return(-1);     // Error

    fShouldWrite = !(pMRU->fFlags & MRU_CACHEWRITE);

    uMax = pMRU->uMax;
    lpfnCompare = (MRUCMPDATAPROC)pMRU->lpfnCompare;

    // Check if the data already exists in the list.
    for (i=0, ppData=&NTHDATA(pMRU, 0); (UINT)i<uMax; ++i, ++ppData)
    {
        if (*ppData && MRUIsSameData(pMRU, *ppData, lpData, cbData))
        {
            // found it, so don't do the write out
            cFirst = i + BASE_CHAR;
            iSlot = i;
            goto FoundEntry;
        }
    }

    //
    // When created "lazy", we are not supposed to add a new item.
    //
    if (pMRU->fFlags & MRU_LAZY)
    {
        ASSERT(0);
        return -1;
    }

    // Attempt to find an unused entry.  Count up the used entries at the
    // same time.
    for (i=0, ppData=&NTHDATA(pMRU, 0); ; ++i, ++ppData)
    {
        if ((UINT)i >= uMax)
            // If we got to the end of the list.
        {
            // use the entry at the end of the cOrder list
            cFirst = pMRU->cOrder[uMax-1];
            ppData = &NTHDATA(pMRU, cFirst-BASE_CHAR);
            break;
        }

        if (!*ppData)
            // If the entry is not used.
        {
            cFirst = i+BASE_CHAR;
            break;
        }
    }

    *ppData = ReAlloc(*ppData, cbData+sizeof(DWORD));
    if (*ppData)
    {
        TCHAR szTemp[2];

        *((LPDWORD)(*ppData)) = cbData;
        hmemcpy(DATAPDATA(*ppData), lpData, cbData);

        iSlot = (int)(cFirst-BASE_CHAR);

        szTemp[0] = cFirst;
        szTemp[1] = TEXT('\0');

        RegSetValueEx(pMRU->hKey, szTemp, 0L, REG_BINARY, (LPVOID)lpData, cbData);
        fShouldWrite = TRUE;
    }
    else
    {
        // Since iSlot == -1, we will remove the reference to cFirst
        // below.
    }

FoundEntry:
    // Remove any previous reference to cFirst.
    lpTemp = StrChr(pMRU->cOrder, cFirst);
    if (lpTemp)
    {
        lstrcpy(lpTemp, lpTemp+1);
    }

    if (iSlot != -1)
    {
        // shift everything over and put cFirst at the front
        hmemcpy(pMRU->cOrder+1, pMRU->cOrder, pMRU->uMax*sizeof(TCHAR));
        pMRU->cOrder[0] = cFirst;
    }

    if (fShouldWrite)
    {
        RegSetValueEx(pMRU->hKey, c_szMRU, 0L, REG_SZ, (CONST BYTE *)pMRU->cOrder,
            sizeof(TCHAR) * (lstrlen(pMRU->cOrder) + 1));
        pMRU->fFlags &= ~MRU_ORDERDIRTY;
    } else
        pMRU->fFlags |= MRU_ORDERDIRTY;

#ifdef DEBUG
    // DebugMsg(DM_TRACE, TEXT("AddMRU: %d msec"), LOWORD(GetTickCount()-dwStart));
#endif
    return(iSlot);
}


//----------------------------------------------------------------------------
// Find data in an MRU list.
// Returns the slot number.
STDAPI_(int) FindMRUData(HANDLE hMRU, const void *lpData, UINT cbData, LPINT lpiSlot)
{
    TCHAR cFirst;
    int iSlot = -1;
    LPTSTR lpTemp;
    LPBYTE *ppData;
    int i;
    UINT uMax;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

#ifdef DEBUG
    DWORD dwStart = GetTickCount();
#endif

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (hMRU == NULL)
        return(-1); // Error state.

    // Can't call this API when it's created lazily.
    if (pMRU->fFlags & MRU_LAZY)
    {
        ASSERT(0);
        return -1;
    }

    uMax = pMRU->uMax;

    /* Find the item in the list.
    */
    for (i=0, ppData=&NTHDATA(pMRU, 0); (UINT)i<uMax; ++i, ++ppData)
    {
        if (!*ppData)
            continue;

        if (MRUIsSameData(pMRU, *ppData, lpData, cbData))
        {
            // So i now has the slot number in it.
            if (lpiSlot != NULL)
                *lpiSlot = i;

            // Now convert the slot number into an index number
            cFirst = i + BASE_CHAR;
            lpTemp = StrChr(pMRU->cOrder, cFirst);
            ASSERT(lpTemp);
            return((lpTemp == NULL)? -1 : (int)(lpTemp - (LPTSTR)pMRU->cOrder));
        }
    }

    return -1;
}


/* Find a string in an MRU list.
 */
STDAPI_(int) FindMRUString(HANDLE hMRU, LPCTSTR szString, LPINT lpiSlot)
{
    /* The extra +1 is so that the list is NULL terminated.
    */
    TCHAR cFirst;
    int iSlot = -1;
    LPTSTR lpTemp;
    LPTSTR *pTemp;
    int i;
    UINT uMax;
    MRUCMPPROC lpfnCompare;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

#ifdef DEBUG
    DWORD dwStart = GetTickCount();
#endif

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (hMRU == NULL)
        return(-1); // Error state.

    uMax = pMRU->uMax;
    lpfnCompare = (MRUCMPPROC)pMRU->lpfnCompare;

    /* Find the item in the list.
    */
    for (i=0, pTemp=&NTHSTRING(pMRU, 0); (UINT)i<uMax; ++i, ++pTemp)
    {
        if (*pTemp)
        {
            int iResult;

#ifdef UNICODE
            if (pMRU->fFlags & MRU_ANSI)
            {
                LPSTR lpStringA, lpTempA;

                lpStringA = ProduceAFromW (CP_ACP, szString);
                lpTempA = ProduceAFromW (CP_ACP, (LPWSTR)*pTemp);

                iResult = (*lpfnCompare)((const void *)lpStringA, (const void *)lpTempA);

                FreeProducedString (lpStringA);
                FreeProducedString (lpTempA);
            }
            else
#endif
            {
                iResult = (*lpfnCompare)((CONST VOID *)szString, (CONST VOID *)*pTemp);
            }

            if (!iResult)
            {
                // So i now has the slot number in it.
                if (lpiSlot != NULL)
                    *lpiSlot = i;

                // Now convert the slot number into an index number
                cFirst = i + BASE_CHAR;
                lpTemp = StrChr(pMRU->cOrder, cFirst);
                return((lpTemp == NULL)? -1 : (int)(lpTemp - (LPTSTR)pMRU->cOrder));
            }
        }
    }

    return(-1);
}


#ifdef UNICODE
//
// ANSI thunk
//

int WINAPI FindMRUStringA(HANDLE hMRU, LPCSTR szString, LPINT lpiSlot)
{
    LPWSTR lpStringW;
    INT    iResult;

    lpStringW = ProduceWFromA(CP_ACP, szString);

    iResult = FindMRUString(hMRU, lpStringW, lpiSlot);

    FreeProducedString (lpStringW);

    return iResult;
}

#else

//
// Unicode stub when build ANSI
//

int WINAPI FindMRUStringW(HANDLE hMRU, LPCWSTR szString, LPINT lpiSlot)
{
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return -1;
}

#endif

/* If lpszString is NULL, then this returns the number of MRU items or less than
 * 0 on error.
 * if nItem < 0, we'll return the number of items currently in the MRU.
 * Otherwise, fill in as much of the buffer as possible (uLen includes the
 * terminating NULL) and return the actual length of the string (including the
 * terminating NULL) or less than 0 on error.
 */
STDAPI_(int) EnumMRUList(HANDLE hMRU, int nItem, LPVOID lpData, UINT uLen)
{
    PMRUDATA pMRU = (PMRUDATA)hMRU;
    int nItems = -1;
    LPTSTR pTemp;
    LPBYTE pData;

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (pMRU)
    {
        nItems = lstrlen(pMRU->cOrder);

        if (nItem < 0 || !lpData)
            return nItems;

        if (nItem < nItems)
        {
            if (pMRU->fFlags & MRU_BINARY)
            {
                pData = NTHDATA(pMRU, pMRU->cOrder[nItem]-BASE_CHAR);
                if (!pData)
                    return -1;

                uLen = min((UINT)DATASIZE(pData), uLen);
                hmemcpy(lpData, DATAPDATA(pData), uLen);

                nItems = uLen;

            }
            else
            {
                pTemp = NTHSTRING(pMRU, pMRU->cOrder[nItem]-BASE_CHAR);
                if (!pTemp)
                    return -1;

                lstrcpyn((LPTSTR)lpData, pTemp, uLen);

                nItems = lstrlen(pTemp);
            }
        }
        else  // revert to error condition
            nItems = -1;
    }

    return nItems;
}


#ifdef UNICODE

STDAPI_(int) EnumMRUListA(HANDLE hMRU, int nItem, LPVOID lpData, UINT uLen)
{
    int iResult = -1;
    PMRUDATA pMRU = (PMRUDATA)hMRU;

    ASSERT(IS_VALID_STRUCT_PTR(pMRU, MRUDATA));

    if (pMRU)
    {
        LPVOID lpDataW;
        BOOL bAllocatedMemory = FALSE;

        //
        //  we need a temp buffer if the data is a string.
        //  but if it is binary, then we trust the callers buffer.
        //
        if (!(pMRU->fFlags & MRU_BINARY) && uLen && lpData)
        {
            lpDataW = LocalAlloc(LPTR, uLen * sizeof(TCHAR));

            if (!lpDataW)
                return -1;

            bAllocatedMemory = TRUE;
        }
        else
            lpDataW = lpData;

        //  call the real thing
        iResult = EnumMRUList(hMRU, nItem, lpDataW, uLen);

        //
        //  if the buffer was a string that we allocated
        //  then we need to thunk the string into the callers buffer
        //
        if (!(pMRU->fFlags & MRU_BINARY) && lpData && uLen && (iResult != -1))
        {
            WideCharToMultiByte(CP_ACP, 0, (LPWSTR)lpDataW, -1,
                (LPSTR)lpData, uLen, NULL, NULL);
        }

        if (bAllocatedMemory)
            LocalFree(lpDataW);
    }

    return iResult;
}

#else

STDAPI_(int) EnumMRUListW(HANDLE hMRU, int nItem, LPVOID lpData, UINT uLen)
{
    SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
    return 0;
}

#endif // UNICODE