/*
 * with - With-procedures
 *
 *  A "With-procedure" creates an object, calls the callback (passing
 *  the object), then frees the object.  The return value
 *  of the with-procedure is the value returned by the callback.
 *
 *  This encapsulates the concept of "Get something, do something with
 *  it, then free it."  Forgetting to free objects on the error path
 *  is a common mistake; by doing it this way, the act of freeing the
 *  object is done automatically.
 */

#include "tweakui.h"

#pragma BEGIN_CONST_DATA

#undef DEFINE_GUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
const GUID name = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }

#define DEFINE_SHLGUID(name, l, w1, w2) \
	 DEFINE_GUID(name, l, w1, w2, 0xC0,0,0,0,0,0,0,0x46)

DEFINE_SHLGUID(IID_IShellFolder,    	0x000214E6L, 0, 0);

const CHAR CODESEG c_szStarDotStar[] = "*.*";

#pragma END_CONST_DATA

/*****************************************************************************
 *
 *  WithPidl
 *
 *	Create a pidl from an psf and a relative path, call the callback,
 *	then free the pidl.
 *
 *	Returns 0 on error, else propagates the callback's return value
 *	through.
 *
 *****************************************************************************/

BOOL PASCAL
WithPidl(PSF psf, LPCSTR lqn, BOOL (*pfn)(PIDL, LPVOID), LPVOID pv)
{
    PIDL pidl = pidlFromPath(psf, lqn);
    if (pidl) {
	BOOL fRc = pfn(pidl, pv);
	Ole_Free(pidl);
	return fRc;
    } else {
	return 0;
    }
}

/*****************************************************************************
 *
 *  WithPsf
 *
 *	Bind to an IShellFolder, call the callback, then release the
 *	IShellFolder.
 *
 *	Returns 0 on error, else propagates the callback's return value
 *	through.
 *
 *****************************************************************************/

BOOL PASCAL
WithPsf(PSF psf, PIDL pidl, BOOL (*pfn)(PSF, LPVOID), LPVOID pv)
{
    PSF psfNew;
    if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_IShellFolder, (LPVOID *)&psfNew))) {
	BOOL fRc;
	fRc = pfn(psfNew, pv);
	Ole_Release(psfNew);
	return fRc;
    } else {
	return 0;
    }
}

/*****************************************************************************
 *
 *  EmptyDirectory
 *
 *	Delete all the files in the indicated directory, first calling a
 *	function in that directory.
 *
 *****************************************************************************/

BOOL PASCAL
EmptyDirectory(LPCSTR pszDir, BOOL (*pfn)(LPCSTR, LPVOID), LPVOID pv)
{
    BOOL fRc;
    char szPrevDir[MAX_PATH];

    GetCurrentDirectory(cA(szPrevDir), szPrevDir); /* For restore */
    if (SetCurrentDirectory(pszDir)) {
	WIN32_FIND_DATA wfd;
	HANDLE h;
	if (pfn) {
	    fRc = pfn(pszDir, pv);
	}
	h = FindFirstFile(c_szStarDotStar, &wfd);
	if (h != INVALID_HANDLE_VALUE) {
	    do {
		DeleteFile(wfd.cFileName);
	    } while (FindNextFile(h, &wfd));
            FindClose(h);
	}
	SetCurrentDirectory(szPrevDir);
    }
    return fRc;
}

/*****************************************************************************
 *
 *  WithTempDirectory
 *
 *	Ask KERNEL for a unique temp name and create a temp directory there.
 *	Change to the new directory, call the callback, and then change
 *	back to the previous directory.  Remove all the files in the temp
 *	directory, then remove the temp directory.  Note that we don't
 *	implement full recursive cleanup, so if you create any subdirectories
 *	in the temp directory, you have to remove them yourself.
 *
 *	Returns 0 on error, else propagates the callback's return value
 *	through.
 *
 *****************************************************************************/

BOOL PASCAL
WithTempDirectory(BOOL (*pfn)(LPCSTR, LPVOID), LPVOID pv)
{
    BOOL fRc;
    char szTmpDir[MAX_PATH + 1 + 8 + 1 + 3 + 1];
    if (GetTempPath(MAX_PATH, szTmpDir) &&
	GetTempFileName(szTmpDir, "", 0, szTmpDir)) { /* Got a unique file */
	DeleteFile(szTmpDir);		/* Nuke the file; we want a dir */
	if (CreateDirectory(szTmpDir, 0)) {
	    fRc = EmptyDirectory(szTmpDir, pfn, pv);
	    RemoveDirectory(szTmpDir);
	} else {
	    fRc = 0;		/* Couldn't create the directory */
	}
    } else {
	fRc = 0;		/* Couldn't generate unique name */
    }
    return fRc;
}