#include "shellprv.h"
#include "ynlist.h"

#define YNLIST_ALLOC    (2 * MAX_PATH * sizeof(TCHAR))

//
// Constructor - creates a YesNoList
//
void CreateYesNoList(PYNLIST pynl)
{
    ZeroMemory(pynl, sizeof(*pynl));
}

//
// Destructor - frees and destroys a YesNoList
//
void DestroyYesNoList(PYNLIST pynl)
{
    if (pynl->dlYes.pszzList)
        GlobalFree(pynl->dlYes.pszzList);
    if (pynl->dlNo.pszzList)
        GlobalFree(pynl->dlNo.pszzList);
    ZeroMemory(pynl, sizeof(*pynl));
}

//
// IsPathOfItem - determine if pszPath is on the path to pszItem
//
BOOL IsPathOfItem(LPCTSTR pszPath, LPCTSTR pszItem)
{
    //
    // Validate pszPath is the first
    // substring of pszItem.
    //
    while (*pszPath)
    {
        if (*pszPath != *pszItem)
        {
            return FALSE;
        }

        pszPath++;
        pszItem++;
    }

    //
    // pszPath is the path if pszItem is empty (exact match),
    // or pszItem is a directory separator.
    //
    return (*pszItem == TEXT('\\')) || (*pszItem == TEXT('\0'));
}

//
// IsInDirList - determines if DIRLIST contains
// the path to pszItem.
//
BOOL IsInDirList(PDIRLIST pdl, LPCTSTR pszItem)
{
    LPTSTR pszzList;

    //
    // Quick check for everything flag.
    //
    if (pdl->fEverythingInList)
        return TRUE;

    //
    // Quick check for empty list.
    //
    if (pdl->pszzList == NULL)
    {
        return FALSE;
    }

    //
    // Compare against each string in the szz list.
    //
    pszzList = pdl->pszzList;
    while (*pszzList)
    {
        //
        // If pszList is the beginning of the path to pszItem,
        // the item is in the list.
        //
        if (IsPathOfItem(pszzList, pszItem))
        {
            return TRUE;
        }

        pszzList += lstrlen(pszzList) + 1;
    }

    //
    // Couldn't find it.
    //
    return FALSE;
}

//
// IsInYesList - determine if an item is in the
// yes list of a YesNoList.
//
BOOL IsInYesList(PYNLIST pynl, LPCTSTR pszItem)
{
    //
    // Call helper function.
    //
    return IsInDirList(&pynl->dlYes, pszItem);
}

//
// IsInNoList - determine if an item is in the
// no list of a YesNoList.
//
BOOL IsInNoList(PYNLIST pynl, LPCTSTR pszItem)
{
    //
    // Call helper function.
    //
    return IsInDirList(&pynl->dlNo, pszItem);
}

//
// AddToDirList - adds an item to a dir list if necessary.
//
void AddToDirList(PDIRLIST pdl, LPCTSTR pszItem)
{
    UINT cchItem;

    //
    // Is the item already in the list?
    //
    if (IsInDirList(pdl, pszItem))
    {
        return;
    }

    //
    // Is the list empty?
    //
    if (pdl->pszzList == NULL)
    {
        pdl->pszzList = (LPTSTR)GlobalAlloc(GPTR, YNLIST_ALLOC);

        if (pdl->pszzList == NULL)
        {
            return;
        }

        pdl->cbAlloc = YNLIST_ALLOC;
        pdl->cchUsed = 1;
        ASSERT(pdl->pszzList[0] == TEXT('\0'));
    }

    //
    // Get the string length,
    // verify it can be added with
    // at most one additional alloc.
    //
    cchItem = lstrlen(pszItem) + 1;
    if (CbFromCch(cchItem) >= YNLIST_ALLOC)
    {
        return;
    }

    //
    // Do we need to allocate more space?
    //
    if (CbFromCch(cchItem) > pdl->cbAlloc - CbFromCch(pdl->cchUsed))
    {
        LPTSTR pszzNew;

        pszzNew = (LPTSTR)GlobalReAlloc(pdl->pszzList, pdl->cbAlloc + YNLIST_ALLOC, GMEM_MOVEABLE|GMEM_ZEROINIT);

        if (pszzNew == NULL)
        {
            return;
        }
        pdl->pszzList = pszzNew;

        pdl->cbAlloc += YNLIST_ALLOC;
    }

    //
    // Add the item.
    //
    lstrcpy(&(pdl->pszzList[pdl->cchUsed - 1]), pszItem);
    pdl->cchUsed += cchItem;

    //
    // Add the second NULL terminator
    // (GlobalReAlloc can't guarantee zeromeminit)
    //
    pdl->pszzList[pdl->cchUsed - 1] = TEXT('\0');
}

//
// Adds an item to the Yes list.
//
void AddToYesList(PYNLIST pynl, LPCTSTR pszItem)
{
    //
    // Call helper function.
    //
    AddToDirList(&pynl->dlYes, pszItem);
}

//
// Adds an item to the No list.
//
void AddToNoList(PYNLIST pynl, LPCTSTR pszItem)
{
    //
    // Call helper function.
    //
    AddToDirList(&pynl->dlNo, pszItem);
}

//
// SetYesToAll - puts everything in the yes list.
//
void SetYesToAll(PYNLIST pynl)
{
    pynl->dlYes.fEverythingInList = TRUE;
}