#define UNICODE
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winioctl.h>
#include <shdcom.h>
#include <shellapi.h>
#include <smbdebug.h>
#include <time.h>

#include "struct.h"
#include "messages.h"
#include "cscapi.h"

CHAR *ProgName = "cscutil";

#define CSC_MERGE_KEEP_LOCAL   1
#define CSC_MERGE_KEEP_NETWORK 2
#define CSC_MERGE_KEEP_BOTH    3

//
// Arguments (ie '/arg:')
//
MAKEARG(Pin);
MAKEARG(UnPin);
MAKEARG(Delete);
MAKEARG(DeleteShadow);
MAKEARG(GetShadow);
MAKEARG(GetShadowInfo);
MAKEARG(ShareId);
MAKEARG(Fill);
MAKEARG(Db);
MAKEARG(SetShareStatus);
MAKEARG(Purge);
MAKEARG(IsServerOffline);
MAKEARG(EnumForStats);
MAKEARG(SetSpace);
MAKEARG(Merge);
MAKEARG(QueryFile);
MAKEARG(QueryFileEx);
MAKEARG(QueryShare);
MAKEARG(Check);
MAKEARG(ExclusionList);
MAKEARG(BWConservationList);
MAKEARG(Disconnect);
MAKEARG(Reconnect);
MAKEARG(Enum);
MAKEARG(Move);
MAKEARG(Bitcopy);
MAKEARG(RandW);
MAKEARG(Offset);
MAKEARG(MoveShare);

//
// Switches (ie '/arg')
//
SWITCH(Info);
SWITCH(Fcblist);
SWITCH(DBStatus);
SWITCH(PQEnum);
SWITCH(User);
SWITCH(System);
SWITCH(Inherit);
SWITCH(Recurse);
SWITCH(Abort);
SWITCH(Skip);
SWITCH(Ask);
SWITCH(Eof);
SWITCH(Retry);
SWITCH(Touch);
SWITCH(Enable);
SWITCH(Disable);
SWITCH(Ioctl);
SWITCH(Flags);
SWITCH(GetSpace);
SWITCH(Encrypt);
SWITCH(Decrypt);
SWITCH(Db);
SWITCH(Set);
SWITCH(Clear);
SWITCH(Purge);
SWITCH(Detector);
SWITCH(Switches);

SWITCH(Debug);
SWITCH(Help);
SWITCH(HelpHelp);
SWITCH(Enum);
SWITCH(Resid);
SWITCH(Full);

//
// The macro can not make these
//

WCHAR SwQ[] = L"/?";
BOOLEAN fSwQ;
WCHAR SwQQ[] = L"/??";
BOOLEAN fSwQQ;

//
// Globals
//
LPWSTR pwszDisconnectArg = NULL;
LPWSTR pwszExclusionListArg = NULL;
LPWSTR pwszBWConservationListArg = NULL;
LPWSTR pwszSetShareStatusArg = NULL;
LPWSTR pwszIsServerOfflineArg = NULL;
LPWSTR pwszPurgeArg = NULL;
LPWSTR pwszPinUnPinArg = NULL;
LPWSTR pwszDeleteArg = NULL;
LPWSTR pwszDeleteShadowArg = NULL;
LPWSTR pwszGetShadowArg = NULL;
LPWSTR pwszGetShadowInfoArg = NULL;
LPWSTR pwszShareIdArg = NULL;
LPWSTR pwszReconnectArg = NULL;
LPWSTR pwszQueryFileArg = NULL;
LPWSTR pwszQueryFileExArg = NULL;
LPWSTR pwszDbArg = NULL;
LPWSTR pwszSetSpaceArg = NULL;
LPWSTR pwszQueryShareArg = NULL;
LPWSTR pwszMoveArg = NULL;
LPWSTR pwszMergeArg = NULL;
LPWSTR pwszFillArg = NULL;
LPWSTR pwszEnumArg = NULL;
LPWSTR pwszRandWArg = NULL;
LPWSTR pwszBitcopyArg = NULL;
LPWSTR pwszOffsetArg = NULL;
LPWSTR pwszEnumForStatsArg = NULL;
LPWSTR pwszCheckArg = NULL;

char statusName[4][32] = {
   "Local Path",
   "Offline Share",
   "Online Share",
   "No CSC"
};

DWORD
Usage(
    BOOLEAN fHelpHelp);

BOOLEAN
CmdProcessArg(
    LPWSTR Arg);

DWORD
CmdInfo(
    ULONG Cmd);

DWORD
CmdDBStatus(
    VOID);

DWORD
CmdPurge(
    PWSTR PurgeArg);

DWORD
CmdDetector(
    VOID);

DWORD
CmdPQEnum(
    VOID);

DWORD
CmdGetSpace(
    VOID);

DWORD
CmdSwitches(
    VOID);

DWORD
CmdDisconnect(
    PWSTR DisconnectArg);

DWORD
CmdExclusionList(
    PWSTR ExclusionListArg);

DWORD
CmdBWConservationList(
    PWSTR BWConservationListArg);

DWORD
CmdSetShareStatus(
    PWSTR SetShareStatusArg);

DWORD
CmdIsServerOffline(
    PWSTR IsServerOfflineArg);

DWORD
CmdReconnect(
    PWSTR ReconnectArg);

DWORD
CmdQueryFile(
    PWSTR QueryFileArg);

DWORD
CmdQueryFileEx(
    PWSTR QueryFileExArg);

DWORD
CmdDb(
    PWSTR DbArg);

DWORD
CmdSetSpace(
    PWSTR SetSpaceArg);

DWORD
CmdQueryShare(
    PWSTR QueryShareArg);

DWORD
CmdMove(
    PWSTR MoveArg);

DWORD
CmdMerge(
    PWSTR MergeArg);

DWORD
CmdEncryptDecrypt(
    BOOL fEncrypt);

DWORD
CmdFill(
    PWSTR FillArg);

DWORD
CmdCheck(
    PWSTR CheckArg);

DWORD
CmdEnum(
    PWSTR CmdEnumArg);

DWORD
CmdRandW(
    PWSTR CmdRandWArg);

DWORD
CmdBitcopy(
    PWSTR CmdBitcopyArg);

DWORD
CmdEnumForStats(
    PWSTR EnumForStatsArg);

DWORD
CmdDelete(
    PWSTR DeleteArg);

DWORD
CmdGetShadow(
    PWSTR GetShadowArg);

DWORD
CmdGetShadowInfo(
    PWSTR GetShadowInfoArg);

DWORD
CmdShareId(
    PWSTR ShareIdArg);

DWORD
CmdDeleteShadow(
    PWSTR DeleteArg);

DWORD
CmdPinUnPin(
    BOOL fPin,
    PWSTR PinArg);

DWORD
CmdMoveShare(
	PWSTR source,
	PWSTR dest);

SHARESTATUS 
GetCSCStatus (
	const WCHAR * pwszPath);

BOOL 
GetShareStatus (
	const WCHAR * pwszShare, 
	DWORD * pdwStatus,
    DWORD * pdwPinCount, 
	DWORD * pdwHints);

void 
MoveDirInCSC (
	const WCHAR * pwszSource,
	const WCHAR * pwszDest,
    const WCHAR * pwszSkipSubdir,
    SHARESTATUS   StatusFrom, SHARESTATUS   StatusTo,
    BOOL  bAllowRdrTimeoutForDel,
    BOOL  bAllowRdrTimeoutForRen);

DWORD 
DoCSCRename (
	const WCHAR * pwszSource, 
	const WCHAR * pwszDest,
    BOOL bOverwrite, 
	BOOL bAllowRdrTimeout);

DWORD 
DeleteCSCFileTree (
	const WCHAR * pwszSource, 
	const WCHAR * pwszSkipSubdir,
    BOOL bAllowRdrTimeout);

DWORD 
DeleteCSCFile (
	const WCHAR * pwszPath, 
	BOOL bAllowRdrTimeout);

DWORD 
DeleteCSCShareIfEmpty (
	LPCTSTR pwszFileName, 
	SHARESTATUS shStatus);

DWORD 
MergePinInfo (
	LPCTSTR pwszSource, 
	LPCTSTR pwszDest,
    SHARESTATUS StatusFrom, 
	SHARESTATUS StatusTo);

DWORD 
PinIfNecessary (
	const WCHAR * pwszPath, 
	SHARESTATUS shStatus);

DWORD
CscMergeFillAsk(
    LPCWSTR lpszFullPath);

DWORD
MyCscMergeProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext);

DWORD
MyCscFillProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext);

DWORD
MyEncryptDecryptProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext);

DWORD
MyEnumForStatsProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext);

DWORD
FileStatusToEnglish(
    DWORD Status,
    LPWSTR OutputBuffer);

DWORD
HintsToEnglish(
    DWORD Hint,
    LPWSTR OutputBuffer);

DWORD
ShareStatusToEnglish(
    DWORD Status,
    LPWSTR OutputBuffer);

BOOLEAN
LooksToBeAShare(LPWSTR Name);

LONG
CountOffsetArgs(
    PWSTR OffsetArg,
    ULONG OffsetArray[]);

DWORD
DumpBitMap(
    LPWSTR lpszTempName);

VOID
ErrorMessage(
    IN HRESULT hr,
    ...);

WCHAR NameBuf[MAX_PATH + 25];

WCHAR vtzDefaultExclusionList[] = L" *.SLM *.MDB *.LDB *.MDW *.MDE *.PST *.DB?"; // from ui.c

//
// These functions were added for Windows XP, and so we do a loadlibrary on them, so that
// this utility will work on both Windows 2000 and Windows XP
//
typedef BOOL (*CSCQUERYFILESTATUSEXW)(LPCWSTR, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD);
typedef BOOL (*CSCQUERYSHARESTATUSW)(LPCWSTR, LPDWORD, LPDWORD, LPDWORD, LPDWORD, LPDWORD);
typedef BOOL (*CSCPURGEUNPINNEDFILES)(ULONG, PULONG, PULONG);
typedef BOOL (*CSCENCRYPTDECRYPTDATABASE)(BOOL, LPCSCPROCW, DWORD_PTR);
typedef BOOL (*CSCSHAREIDTOSHARENAME)(ULONG, PBYTE, PULONG);

_cdecl
main(int argc, char *argv[])
{
    DWORD dwErr = ERROR_SUCCESS;
    LPWSTR CommandLine;
    LPWSTR *argvw;
    int argx;
    int argcw;

    // fSwDebug = TRUE;

    if (!CSCIsCSCEnabled()) {
        Usage(FALSE);
        ErrorMessage(MSG_CSC_DISABLED);
        return 1;
    }
    
    //
    // Get the command line in Unicode
    //

    CommandLine = GetCommandLine();

    argvw = CommandLineToArgvW(CommandLine, &argcw);

    if ( argvw == NULL ) {
        MyPrintf(L"cscutil:Can't convert command line to Unicode: %d\r\n", GetLastError() );
        return 1;
    }

    //
    // Get the arguments
    //
    if (argcw <= 1) {
        Usage(FALSE);
        dwErr = ERROR_SUCCESS;
        goto Cleanup;
    }

    //
    // Process arguments
    //

    for (argx = 1; argx < argcw; argx++) {
        if (CmdProcessArg(argvw[argx]) != TRUE) {
            dwErr = ERROR_INVALID_PARAMETER;
            goto Cleanup;
        }
		if (fArgMoveShare) {
			break;
		}
    }

    if (fSwDebug == TRUE) {
        MyPrintf(L"Do special debug stuff here\r\n");
    }

    //
    // Do the work
    //
    if (fSwHelp == TRUE || fSwQ == TRUE) {
        dwErr = Usage(FALSE);
    } else if (fSwHelpHelp == TRUE || fSwQQ == TRUE) {
        dwErr = Usage(TRUE);
    } else if (fSwInfo == TRUE) {
        dwErr = CmdInfo(DEBUG_INFO_SERVERLIST);
    } else if (fSwFcblist == TRUE) {
        dwErr = CmdInfo(DEBUG_INFO_CSCFCBSLIST);
    } else if (fSwGetSpace == TRUE) {
        dwErr = CmdGetSpace();
    } else if (fSwDBStatus == TRUE) {
        dwErr = CmdDBStatus();
    } else if (fArgPurge == TRUE) {
        dwErr = CmdPurge(pwszPurgeArg);
    } else if (fSwPurge == TRUE) {
        dwErr = CmdPurge(NULL);
    } else if (fSwDetector == TRUE) {
        dwErr = CmdDetector();
    } else if (fSwPQEnum == TRUE) {
        dwErr = CmdPQEnum();
    } else if (fSwFlags == TRUE) {
        ErrorMessage(MSG_FLAGS);
        dwErr = ERROR_SUCCESS;
    } else if (fSwEnable == TRUE) {
        dwErr = (CSCDoEnableDisable(TRUE) == TRUE) ? ERROR_SUCCESS : GetLastError();
    } else if (fSwDisable == TRUE) {
        dwErr = (CSCDoEnableDisable(FALSE) == TRUE) ? ERROR_SUCCESS : GetLastError();
    } else if (fSwSwitches == TRUE) {
        dwErr = CmdSwitches();
    } else if (fArgDisconnect == TRUE) {
        dwErr = CmdDisconnect(pwszDisconnectArg);
    } else if (fArgExclusionList == TRUE) {
        dwErr = CmdExclusionList(pwszExclusionListArg);
    } else if (fArgBWConservationList == TRUE) {
        dwErr = CmdBWConservationList(pwszBWConservationListArg);
    } else if (fArgSetShareStatus == TRUE) {
        dwErr = CmdSetShareStatus(pwszSetShareStatusArg);
    } else if (fArgIsServerOffline == TRUE) {
        dwErr = CmdIsServerOffline(pwszIsServerOfflineArg);
    } else if (fArgReconnect == TRUE) {
        dwErr = CmdReconnect(pwszReconnectArg);
    } else if (fArgQueryFile == TRUE) {
        dwErr = CmdQueryFile(pwszQueryFileArg);
    } else if (fArgQueryFileEx == TRUE) {
        dwErr = CmdQueryFileEx(pwszQueryFileExArg);
#if defined(CSCUTIL_INTERNAL)
    } else if (fArgDb == TRUE) {
        dwErr = CmdDb(pwszDbArg);
    } else if (fSwDb == TRUE) {
        dwErr = CmdDb(NULL);
    } else if (fArgBitcopy == TRUE) {
        dwErr = CmdBitcopy(pwszBitcopyArg);
#endif // CSCUTIL_INTERNAL
    } else if (fArgSetSpace == TRUE) {
        dwErr = CmdSetSpace(pwszSetSpaceArg);
    } else if (fArgQueryShare == TRUE) {
        dwErr = CmdQueryShare(pwszQueryShareArg);
    } else if (fArgMerge == TRUE) {
        dwErr = CmdMerge(pwszMergeArg);
    } else if (fArgMove == TRUE) {
        dwErr = CmdMove(pwszMoveArg);
    } else if (fSwEncrypt == TRUE) {
        dwErr = CmdEncryptDecrypt(TRUE);
    } else if (fSwDecrypt == TRUE) {
        dwErr = CmdEncryptDecrypt(FALSE);
    } else if (fArgFill == TRUE) {
        dwErr = CmdFill(pwszFillArg);
    } else if (fArgCheck == TRUE) {
        dwErr = CmdCheck(pwszCheckArg);
    } else if (fArgEnum == TRUE) {
        dwErr = CmdEnum(pwszEnumArg);
    } else if (fSwEnum == TRUE) {
        dwErr = CmdEnum(NULL);
    } else if (fArgRandW == TRUE) {
        dwErr = CmdRandW(pwszRandWArg);
    } else if (fArgEnumForStats == TRUE) {
        dwErr = CmdEnumForStats(pwszEnumForStatsArg);
    } else if (fArgGetShadow == TRUE) {
        dwErr = CmdGetShadow(pwszGetShadowArg);
    } else if (fArgGetShadowInfo == TRUE) {
        dwErr = CmdGetShadowInfo(pwszGetShadowInfoArg);
    } else if (fArgShareId == TRUE) {
        dwErr = CmdShareId(pwszShareIdArg);
    } else if (fArgDelete == TRUE) {
        dwErr = CmdDelete(pwszDeleteArg);
    } else if (fArgDeleteShadow == TRUE) {
        dwErr = CmdDeleteShadow(pwszDeleteShadowArg);
    } else if (fArgPin == TRUE || fArgUnPin == TRUE) {
        dwErr = CmdPinUnPin(fArgPin, pwszPinUnPinArg);
	} else if (fArgMoveShare == TRUE) {
		if (argcw == 4) {
			dwErr = CmdMoveShare(argvw[2], argvw[3]);
		}
        else {
            dwErr = Usage(FALSE);
            dwErr = ERROR_INVALID_PARAMETER;
        }
	} else {
        dwErr = Usage(FALSE);
    }

Cleanup:

    if (dwErr == ERROR_SUCCESS) {
        ErrorMessage(MSG_SUCCESSFUL);
    } else {
        LPWSTR MessageBuffer;
        DWORD dwBufferLength;

        dwBufferLength = FormatMessage(
                            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                            NULL,
                            dwErr,
                            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                            (LPWSTR) &MessageBuffer,
                            0,
                            NULL);

        ErrorMessage(MSG_ERROR, dwErr);
        if (dwBufferLength > 0) {
            MyPrintf(L"%ws\r\n", MessageBuffer);
            LocalFree(MessageBuffer);
        }
    }

    return dwErr;
}

BOOLEAN
CmdProcessArg(LPWSTR Arg)
{
    LONG ArgLen;
    BOOLEAN dwErr = FALSE;
    BOOLEAN FoundAnArg = FALSE;

    if (fSwDebug == TRUE)
        MyPrintf(L"ProcessArg(%ws)\r\n", Arg);

    if ( Arg != NULL && wcslen(Arg) > 1) {

        dwErr = TRUE;
        ArgLen = wcslen(Arg);

        //
        // Commands with args
        //
        if (_wcsnicmp(Arg, ArgDisconnect, ArgLenDisconnect) == 0) {
            FoundAnArg = fArgDisconnect = TRUE;
            if (ArgLen > ArgLenDisconnect)
                pwszDisconnectArg = &Arg[ArgLenDisconnect];
        } else if (_wcsnicmp(Arg, ArgEnum, ArgLenEnum) == 0) {
            FoundAnArg = fArgEnum = TRUE;
            if (ArgLen > ArgLenEnum)
                pwszEnumArg = &Arg[ArgLenEnum];
		} else if (_wcsnicmp(Arg, ArgMoveShare, ArgLenMoveShare) == 0) {
			FoundAnArg = fArgMoveShare = TRUE;
#if defined(CSCUTIL_INTERNAL)
        } else if (_wcsnicmp(Arg, ArgReconnect, ArgLenReconnect) == 0) {
            FoundAnArg = fArgReconnect = TRUE;
            if (ArgLen > ArgLenReconnect)
                pwszReconnectArg = &Arg[ArgLenReconnect];
        } else if (_wcsnicmp(Arg, ArgPin, ArgLenPin) == 0) {
            FoundAnArg = fArgPin = TRUE;
            if (ArgLen > ArgLenPin)
                pwszPinUnPinArg = &Arg[ArgLenPin];
        } else if (_wcsnicmp(Arg, ArgUnPin, ArgLenUnPin) == 0) {
            FoundAnArg = fArgUnPin = TRUE;
            if (ArgLen > ArgLenUnPin)
                pwszPinUnPinArg = &Arg[ArgLenUnPin];
        } else if (_wcsnicmp(Arg, ArgDelete, ArgLenDelete) == 0) {
            FoundAnArg = fArgDelete = TRUE;
            if (ArgLen > ArgLenDelete)
                pwszDeleteArg = &Arg[ArgLenDelete];
        } else if (_wcsnicmp(Arg, ArgExclusionList, ArgLenExclusionList) == 0) {
            FoundAnArg = fArgExclusionList = TRUE;
            if (ArgLen > ArgLenExclusionList)
                pwszExclusionListArg = &Arg[ArgLenExclusionList];
        } else if (_wcsnicmp(Arg, ArgBWConservationList, ArgLenBWConservationList) == 0) {
            FoundAnArg = fArgBWConservationList = TRUE;
            if (ArgLen > ArgLenBWConservationList)
                pwszBWConservationListArg = &Arg[ArgLenBWConservationList];
        } else if (_wcsnicmp(Arg, ArgSetShareStatus, ArgLenSetShareStatus) == 0) {
            FoundAnArg = fArgSetShareStatus = TRUE;
            if (ArgLen > ArgLenSetShareStatus)
                pwszSetShareStatusArg = &Arg[ArgLenSetShareStatus];
        } else if (_wcsnicmp(Arg, ArgIsServerOffline, ArgLenIsServerOffline) == 0) {
            FoundAnArg = fArgIsServerOffline = TRUE;
            if (ArgLen > ArgLenIsServerOffline)
                pwszIsServerOfflineArg = &Arg[ArgLenIsServerOffline];
        } else if (_wcsnicmp(Arg, ArgPurge, ArgLenPurge) == 0) {
            FoundAnArg = fArgPurge = TRUE;
            if (ArgLen > ArgLenPurge)
                pwszPurgeArg = &Arg[ArgLenPurge];
        } else if (_wcsnicmp(Arg, ArgRandW, ArgLenRandW) == 0) {
            FoundAnArg = fArgRandW = TRUE;
            if (ArgLen > ArgLenRandW)
                pwszRandWArg = &Arg[ArgLenRandW];
        } else if (_wcsnicmp(Arg, ArgBitcopy, ArgLenBitcopy) == 0) {
            FoundAnArg = fArgBitcopy = TRUE;
            if (ArgLen > ArgLenBitcopy)
                pwszBitcopyArg = &Arg[ArgLenBitcopy];
        } else if (_wcsnicmp(Arg, ArgOffset, ArgLenOffset) == 0) {
            FoundAnArg = fArgOffset = TRUE;
            if (ArgLen > ArgLenOffset)
                pwszOffsetArg = &Arg[ArgLenOffset];
        } else if (_wcsnicmp(Arg, ArgDeleteShadow, ArgLenDeleteShadow) == 0) {
            FoundAnArg = fArgDeleteShadow = TRUE;
            if (ArgLen > ArgLenDeleteShadow)
                pwszDeleteShadowArg = &Arg[ArgLenDeleteShadow];
        } else if (_wcsnicmp(Arg, ArgQueryFile, ArgLenQueryFile) == 0) {
            FoundAnArg = fArgQueryFile = TRUE;
            if (ArgLen > ArgLenQueryFile)
                pwszQueryFileArg = &Arg[ArgLenQueryFile];
        } else if (_wcsnicmp(Arg, ArgQueryFileEx, ArgLenQueryFileEx) == 0) {
            FoundAnArg = fArgQueryFileEx = TRUE;
            if (ArgLen > ArgLenQueryFileEx)
                pwszQueryFileExArg = &Arg[ArgLenQueryFileEx];
        } else if (_wcsnicmp(Arg, ArgDb, ArgLenDb) == 0) {
            FoundAnArg = fArgDb = TRUE;
            if (ArgLen > ArgLenDb)
                pwszDbArg = &Arg[ArgLenDb];
        } else if (_wcsnicmp(Arg, ArgSetSpace, ArgLenSetSpace) == 0) {
            FoundAnArg = fArgSetSpace = TRUE;
            if (ArgLen > ArgLenSetSpace)
                pwszSetSpaceArg = &Arg[ArgLenSetSpace];
        } else if (_wcsnicmp(Arg, ArgGetShadow, ArgLenGetShadow) == 0) {
            FoundAnArg = fArgGetShadow = TRUE;
            if (ArgLen > ArgLenGetShadow)
                pwszGetShadowArg = &Arg[ArgLenGetShadow];
        } else if (_wcsnicmp(Arg, ArgGetShadowInfo, ArgLenGetShadowInfo) == 0) {
            FoundAnArg = fArgGetShadowInfo = TRUE;
            if (ArgLen > ArgLenGetShadowInfo)
                pwszGetShadowInfoArg = &Arg[ArgLenGetShadowInfo];
        } else if (_wcsnicmp(Arg, ArgShareId, ArgLenShareId) == 0) {
            FoundAnArg = fArgShareId = TRUE;
            if (ArgLen > ArgLenShareId)
                pwszShareIdArg = &Arg[ArgLenShareId];
        } else if (_wcsnicmp(Arg, ArgQueryShare, ArgLenQueryShare) == 0) {
            FoundAnArg = fArgQueryShare = TRUE;
            if (ArgLen > ArgLenQueryShare)
                pwszQueryShareArg = &Arg[ArgLenQueryShare];
        } else if (_wcsnicmp(Arg, ArgMove, ArgLenMove) == 0) {
            FoundAnArg = fArgMove = TRUE;
            if (ArgLen > ArgLenMove)
                pwszMoveArg = &Arg[ArgLenMove];
        } else if (_wcsnicmp(Arg, ArgMerge, ArgLenMerge) == 0) {
            FoundAnArg = fArgMerge = TRUE;
            if (ArgLen > ArgLenMerge)
                pwszMergeArg = &Arg[ArgLenMerge];
        } else if (_wcsnicmp(Arg, ArgFill, ArgLenFill) == 0) {
            FoundAnArg = fArgFill = TRUE;
            if (ArgLen > ArgLenFill)
                pwszFillArg = &Arg[ArgLenFill];
        } else if (_wcsnicmp(Arg, ArgCheck, ArgLenCheck) == 0) {
            FoundAnArg = fArgCheck = TRUE;
            if (ArgLen > ArgLenCheck)
                pwszCheckArg = &Arg[ArgLenCheck];
        } else if (_wcsnicmp(Arg, ArgEnumForStats, ArgLenEnumForStats) == 0) {
            FoundAnArg = fArgEnumForStats = TRUE;
            if (ArgLen > ArgLenEnumForStats)
                pwszEnumForStatsArg = &Arg[ArgLenEnumForStats];
#endif // CSCUTIL_INTERNAL
        }

        // Switches go at the end!!
        
        if (_wcsicmp(Arg, SwHelp) == 0) {
            FoundAnArg = fSwHelp = TRUE;
        } else if (_wcsicmp(Arg, SwResid) == 0) {
            FoundAnArg = fSwTouch = fSwEnum = fSwRecurse = TRUE;
        } else if (_wcsicmp(Arg, SwFull) == 0) {
            FoundAnArg = fSwFull = TRUE;
        } else if (_wcsicmp(Arg, SwTouch) == 0) {
            FoundAnArg = fSwTouch = TRUE;
        } else if (_wcsicmp(Arg, SwEnum) == 0) {
            FoundAnArg = fSwEnum = TRUE;
        } else if (_wcsicmp(Arg, SwQQ) == 0) {
            FoundAnArg = fSwQQ = TRUE;
        } else if (_wcsicmp(Arg, SwQ) == 0) {
            FoundAnArg = fSwQ = TRUE;
        } else if (_wcsicmp(Arg, SwRecurse) == 0) {
            FoundAnArg = fSwRecurse = TRUE;
         
#if defined(CSCUTIL_INTERNAL)
        } else if (_wcsicmp(Arg, SwDebug) == 0) {
            FoundAnArg = fSwDebug = TRUE;
        } else if (_wcsicmp(Arg, SwHelpHelp) == 0) {
            FoundAnArg = fSwHelpHelp = TRUE;
        } else if (_wcsicmp(Arg, SwPQEnum) == 0) {
            FoundAnArg = fSwPQEnum = TRUE;
        } else if (_wcsicmp(Arg, SwInfo) == 0) {
            FoundAnArg = fSwInfo = TRUE;
        } else if (_wcsicmp(Arg, SwDBStatus) == 0) {
            FoundAnArg = fSwDBStatus = TRUE;
        } else if (_wcsicmp(Arg, SwPurge) == 0) {
            FoundAnArg = fSwPurge = TRUE;
        } else if (_wcsicmp(Arg, SwDetector) == 0) {
            FoundAnArg = fSwDetector = TRUE;
        } else if (_wcsicmp(Arg, SwGetSpace) == 0) {
            FoundAnArg = fSwGetSpace = TRUE;
        } else if (_wcsicmp(Arg, SwDb) == 0) {
            FoundAnArg = fSwDb = TRUE;
        } else if (_wcsicmp(Arg, SwFcblist) == 0) {
            FoundAnArg = fSwFcblist = TRUE;
        } else if (_wcsicmp(Arg, SwEnable) == 0) {
            FoundAnArg = fSwEnable = TRUE;
        } else if (_wcsicmp(Arg, SwDisable) == 0) {
            FoundAnArg = fSwDisable = TRUE;
        } else if (_wcsicmp(Arg, SwSwitches) == 0) {
            FoundAnArg = fSwSwitches = TRUE;
        } else if (_wcsicmp(Arg, SwUser) == 0) {
            FoundAnArg = fSwUser = TRUE;
        } else if (_wcsicmp(Arg, SwSystem) == 0) {
            FoundAnArg = fSwSystem = TRUE;
        } else if (_wcsicmp(Arg, SwInherit) == 0) {
            FoundAnArg = fSwInherit = TRUE;
        } else if (_wcsicmp(Arg, SwAbort) == 0) {
            FoundAnArg = fSwAbort = TRUE;
        } else if (_wcsicmp(Arg, SwSkip) == 0) {
            FoundAnArg = fSwSkip = TRUE;
        } else if (_wcsicmp(Arg, SwAsk) == 0) {
            FoundAnArg = fSwAsk = TRUE;
        } else if (_wcsicmp(Arg, SwEof) == 0) {
            FoundAnArg = fSwEof = TRUE;
        } else if (_wcsicmp(Arg, SwRetry) == 0) {
            FoundAnArg = fSwRetry = TRUE;
        } else if (_wcsicmp(Arg, SwSet) == 0) {
            FoundAnArg = fSwSet = TRUE;
        } else if (_wcsicmp(Arg, SwClear) == 0) {
            FoundAnArg = fSwClear = TRUE;
        } else if (_wcsicmp(Arg, SwFlags) == 0) {
            FoundAnArg = fSwFlags = TRUE;
        } else if (_wcsicmp(Arg, SwIoctl) == 0) {
            FoundAnArg = fSwIoctl = TRUE;
        } else if (_wcsicmp(Arg, SwEncrypt) == 0) {
            FoundAnArg = fSwEncrypt = TRUE;
        } else if (_wcsicmp(Arg, SwDecrypt) == 0) {
            FoundAnArg = fSwDecrypt = TRUE;
#endif // CSCUTIL_INTERNAL
        }

        if (FoundAnArg == FALSE) {
            ErrorMessage(MSG_UNRECOGNIZED_OPTION, &Arg[1]);
            dwErr = FALSE;
            goto AllDone;
        }

    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"ProcessArg exit %d\r\n", dwErr);

    return dwErr;
}

DWORD
Usage(
    BOOLEAN fHelpHelp)
{
#if defined(CSCUTIL_INTERNAL)
    ErrorMessage(MSG_USAGE);
    if (fHelpHelp == TRUE)
        ErrorMessage(MSG_USAGE_EX);
#else
    ErrorMessage(MSG_USAGE2);
    if (fHelpHelp == TRUE)
        ErrorMessage(MSG_USAGE_EX2);
#endif // CSCUTIL_INTERNAL
    return ERROR_SUCCESS;
}

DWORD
CmdMoveShare(
	PWSTR source, 
	PWSTR dest)
{
   SHARESTATUS sFrom, sTo;

   //printf( "Attempting move from %ws to %ws.\n", source, dest );

   sFrom = GetCSCStatus(source);
   //printf( "INFO: %ws status = %s\n", source, statusName[sFrom] );
   
   sTo = GetCSCStatus(dest);
   //printf( "INFO: %ws status = %s\n", dest, statusName[sTo] );

   // Check and make sure this is valid
   if( (sFrom == PathLocal) || (sTo == PathLocal) )
   {
      ErrorMessage(MSG_TO_LOCAL);
      return ERROR_INVALID_PARAMETER;
   }

   if( (sFrom == NoCSC) || (sTo == NoCSC) )
   {
      ErrorMessage(MSG_NO_CSC);
      return ERROR_INVALID_PARAMETER;
   }

   if( sTo == ShareOffline )
   {
      ErrorMessage(MSG_NOT_ONLINE);
      return ERROR_CSCSHARE_OFFLINE;
   }

   MoveDirInCSC( source, dest, NULL, ShareOnline, ShareOnline, TRUE, TRUE );

   return ERROR_SUCCESS;
}


//+--------------------------------------------------------------------------
//
//  Function:   MoveDirInCSC
//
//  Synopsis:   this function moves a directory within the CSC cache without
//              prejudice. If the destination is a local path, it just deletes
//              the source tree from the cache
//
//  Arguments:  [in] pwszSource : the source path
//              [in] pwszDest   : the dest path
//              [in] pwszSkipSubdir : the directory to skip while moving
//              [in] StatusFrom : the CSC status of the source path
//              [in] StatusTo   : the CSC status of the dest. path
//              [in] bAllowRdrTimeout : if stuff needs to be deleted from the
//                              cache, we may not succeed immediately since
//                              the rdr keeps the handles to recently opened
//                              files open. This paramaters tells the function
//                              whether it needs to wait and retry
//
//  Returns:    nothing. it just tries its best.
//
//  History:    11/21/1998  RahulTh  created
//
//  Notes:      the value of bAllowRdrTimeout is always ignored for the
//              in-cache rename operation. we always want to wait for
//              the timeout.
//
//---------------------------------------------------------------------------
void MoveDirInCSC (const WCHAR * pwszSource, const WCHAR * pwszDest,
                   const WCHAR * pwszSkipSubdir,
                   SHARESTATUS   StatusFrom, SHARESTATUS   StatusTo,
                   BOOL  bAllowRdrTimeoutForDel,
                   BOOL  bAllowRdrTimeoutForRen)
{
    WIN32_FIND_DATA findData;
    DWORD   dwFileStatus;
    DWORD   dwPinCount;
    HANDLE  hCSCFind;
    DWORD   dwHintFlags;
    FILETIME origTime;
    WCHAR * pwszPath;
    WCHAR * pwszEnd;
    int     len;
    DWORD   StatusCSCRen = ERROR_SUCCESS;

    if (PathLocal == StatusFrom)
        return;                 //there is nothing to do. nothing was cached.

    if (PathLocal == StatusTo)
    {
        //the destination is a local path, so we should just delete the
        //files from the source
        DeleteCSCFileTree (pwszSource, pwszSkipSubdir, bAllowRdrTimeoutForDel);
    }
    else
    {
        pwszPath = (WCHAR *) malloc (sizeof (WCHAR) * ((len = wcslen (pwszSource)) + MAX_PATH + 2));
        if (!pwszPath || len <= 0)
            return;
        wcscpy (pwszPath, pwszSource);
        pwszEnd = pwszPath + len;
        if (L'\\' != pwszEnd[-1])
        {
            *pwszEnd++ = L'\\';
        }
        hCSCFind = CSCFindFirstFile (pwszSource, &findData, &dwFileStatus,
                                     &dwPinCount, &dwHintFlags, &origTime);

        if (INVALID_HANDLE_VALUE != hCSCFind)
        {
            do
            {
                if (0 != _wcsicmp (L".", findData.cFileName) &&
                    0 != _wcsicmp (L"..", findData.cFileName) &&
                    (!pwszSkipSubdir || (0 != _wcsicmp (findData.cFileName, pwszSkipSubdir))))
                {
                    wcscpy (pwszEnd, findData.cFileName);
                    if (ERROR_SUCCESS == StatusCSCRen)
                    {
                        StatusCSCRen = DoCSCRename (pwszPath, pwszDest, TRUE, bAllowRdrTimeoutForRen);
                    }
                    else
                    {
                        StatusCSCRen = DoCSCRename (pwszPath, pwszDest, TRUE, FALSE);
                    }
                }

            } while ( CSCFindNextFile (hCSCFind, &findData, &dwFileStatus,
                                       &dwPinCount, &dwHintFlags, &origTime)
                     );

            CSCFindClose (hCSCFind);
        }

        //merge the pin info. at the top level folder
        MergePinInfo (pwszSource, pwszDest, StatusFrom, StatusTo);
        
		//remove the share - Navjot.
		if (!CSCDeleteW(pwszSource))
           StatusCSCRen = GetLastError();
	}

    return;
}

//+--------------------------------------------------------------------------
//
//  Function:   DoCSCRename
//
//  Synopsis:   does a file rename within the CSC cache.
//
//  Arguments:  [in] pwszSource : full source path
//              [in] pwszDest : full path to destination directory
//              [in] bOverwrite : overwrite if the file/folder exists at
//                                  the destination
//              [in] bAllowRdrTimeout : if TRUE, retry for 10 seconds on failure
//                              so that rdr and mem. mgr. get enough time to
//                              release the handles.
//
//  Returns:    ERROR_SUCCESS if the rename was successful.
//              an error code otherwise.
//
//  History:    5/26/1999  RahulTh  created
//
//  Notes:      no validation of parameters is done. The caller is responsible
//              for that
//
//---------------------------------------------------------------------------
DWORD DoCSCRename (const WCHAR * pwszSource, const WCHAR * pwszDest,
                   BOOL bOverwrite, BOOL bAllowRdrTimeout)
{
    DWORD   Status = ERROR_SUCCESS;
    BOOL    bStatus;
    int     i;

    bStatus = CSCDoLocalRename (pwszSource, pwszDest, bOverwrite);
    if (!bStatus)
    {
        Status = GetLastError();
        if (ERROR_SUCCESS != Status &&
            ERROR_FILE_NOT_FOUND != Status &&
            ERROR_PATH_NOT_FOUND != Status &&
            ERROR_INVALID_PARAMETER != Status &&
            ERROR_BAD_NETPATH != Status)
        {
            if (bAllowRdrTimeout)
            {
                if (!bOverwrite && ERROR_FILE_EXISTS == Status)
                {
                    Status = ERROR_SUCCESS;
                }
                else
                {
                    for (i = 0; i < 11; i++)
                    {
                        Sleep (1000);   //wait for the handle to be released
                        bStatus = CSCDoLocalRename (pwszSource, pwszDest, bOverwrite);
                        if (bStatus)
                        {
                            Status = ERROR_SUCCESS;
                            break;
                        }
                    }
                }
            }
        }
        else
        {
            Status = ERROR_SUCCESS;
        }
    }

    return Status;
}

//+--------------------------------------------------------------------------
//
//  Function:   DeleteCSCFileTree
//
//  Synopsis:   deletes a file tree from the CSC
//
//  Arguments:  [in] pwszSource : the path to the folder whose contents should
//                                be deleted
//              [in] pwszSkipSubdir : name of the subdirectory to be skipped.
//              [in] bAllowRdrTimeout : if true, makes multiple attempts to
//                              delete the file since the rdr may have left
//                              the handle open for sometime which can result
//                              in an ACCESS_DENIED message.
//
//  Returns:    ERROR_SUCCESS if the deletion was successful. An error code
//              otherwise.
//
//  History:    11/21/1998  RahulTh  created
//
//  Notes:
//
//---------------------------------------------------------------------------
DWORD DeleteCSCFileTree (const WCHAR * pwszSource, const WCHAR * pwszSkipSubdir,
                        BOOL bAllowRdrTimeout)
{
    WIN32_FIND_DATA findData;
    DWORD   dwFileStatus;
    DWORD   dwPinCount;
    HANDLE  hCSCFind;
    DWORD   dwHintFlags;
    FILETIME origTime;
    WCHAR * pwszPath;
    WCHAR * pwszEnd;
    int     len;
    DWORD   Status = ERROR_SUCCESS;

    
    pwszPath = (WCHAR *) malloc (sizeof(WCHAR) * ((len = wcslen(pwszSource)) + MAX_PATH + 2));
    if (!pwszPath)
        return ERROR_OUTOFMEMORY;     //nothing much we can do if we run out of memory

    if (len <= 0)
        return ERROR_BAD_PATHNAME;

    wcscpy (pwszPath, pwszSource);
    pwszEnd = pwszPath + len;
    if (L'\\' != pwszEnd[-1])
    {
        *pwszEnd++ = L'\\';
    }

    hCSCFind = CSCFindFirstFile (pwszSource, &findData, &dwFileStatus,
                                 &dwPinCount, &dwHintFlags, &origTime);

    if (INVALID_HANDLE_VALUE != hCSCFind)
    {
        do
        {
            if (0 != _wcsicmp (L".", findData.cFileName) &&
                0 != _wcsicmp (L"..", findData.cFileName) &&
                (!pwszSkipSubdir || (0 != _wcsicmp (pwszSkipSubdir, findData.cFileName))))
            {
                wcscpy (pwszEnd, findData.cFileName);

                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                {
                    if (ERROR_SUCCESS != Status)
                    {
                        //no point delaying the deletes since a delete has already
                        //failed.
                        DeleteCSCFileTree (pwszPath, NULL, FALSE);
                    }
                    else
                    {
                        Status = DeleteCSCFileTree (pwszPath, NULL, bAllowRdrTimeout);
                    }
                }
                else
                {
                    if (ERROR_SUCCESS != Status)
                    {
                        //no point delaying the delete if we have already failed.
                        DeleteCSCFile (pwszPath, FALSE);
                    }
                    else
                    {
                        Status = DeleteCSCFile (pwszPath, bAllowRdrTimeout);
                    }
                }
            }

        } while ( CSCFindNextFile (hCSCFind, &findData, &dwFileStatus,
                                   &dwPinCount, &dwHintFlags, &origTime)
                 );

        CSCFindClose (hCSCFind);
    }

    if (ERROR_SUCCESS != Status)
    {
        //no point in delaying the delete if we have already failed.
        DeleteCSCFile (pwszSource, FALSE);
    }
    else
    {
        Status = DeleteCSCFile (pwszSource, bAllowRdrTimeout);
    }

    return Status;
}

//+--------------------------------------------------------------------------
//
//  Function:   DeleteCSCFile
//
//  Synopsis:   deletes the given path. but might make repeated attempts to
//              make sure that the rdr has enough time to release any handles
//              that it holds.
//
//  Arguments:  [in] pwszPath : the path to delete.
//              [in] bAllowRdrTimeout : make multiple attempts to delete the
//                          file with waits in between so that the rdr has
//                          enough time to release any held handles.
//
//  Returns:    ERROR_SUCCES if the delete was successful. An error code
//              otherwise.
//
//  History:    5/26/1999  RahulTh  created
//
//  Notes:
//
//---------------------------------------------------------------------------
DWORD DeleteCSCFile (const WCHAR * pwszPath, BOOL bAllowRdrTimeout)
{
    BOOL    bStatus;
    DWORD   Status = ERROR_SUCCESS;
    int     i;

    bStatus = CSCDelete (pwszPath);
    if (!bStatus)
    {
        Status = GetLastError();
        if (ERROR_SUCCESS != Status &&
            ERROR_FILE_NOT_FOUND != Status &&
            ERROR_PATH_NOT_FOUND != Status &&
            ERROR_INVALID_PARAMETER != Status)
        {
            //this is a valid error.
            //so based on the value of bAllowRdrTimeout and based
            //on whether we have already failed in deleting something
            //we will try repeatedly to delete the file for 10 seconds
            if (bAllowRdrTimeout)
            {
                for (i = 0; i < 11; i++)
                {
                    Sleep (1000);   //wait for 1 second and try again
                    bStatus = CSCDelete (pwszPath);
                    if (bStatus)
                    {
                        Status = ERROR_SUCCESS;
                        break;
                    }
                }
            }
        }
        else
        {
            Status = ERROR_SUCCESS;
        }
    }

    return Status;
}


//+--------------------------------------------------------------------------
//
//  Function:   DeleteCSCShareIfEmpty
//
//  Synopsis:   given a file name, this function deletes from the local cache
//              the share to which the file belongs if the local cache for that
//              share is empty
//
//  Arguments:  [in] pwszFileName : the full file name -- must be UNC
//              [in] shStatus : the share status - online, offline, local etc.
//
//  Returns:    ERROR_SUCCESS : if successful
//              a win32 error code if something goes wrong
//
//  History:    4/22/1999  RahulTh  created
//
//  Notes:      we do not have to explicitly check if the share is empty
//              because if it is not, then the delete will fail anyway
//
//---------------------------------------------------------------------------
DWORD DeleteCSCShareIfEmpty (LPCTSTR pwszFileName, SHARESTATUS shStatus)
{
    DWORD   Status;
    WCHAR * pwszFullPath = NULL;
    WCHAR * pwszCurr = NULL;
    int     len;
    WCHAR * pwszShrEnd;

    if (PathLocal == shStatus || NoCSC == shStatus)
        return ERROR_SUCCESS;

    if (ShareOffline == shStatus)
        return ERROR_FILE_OFFLINE;

    len = wcslen (pwszFileName);

    if (len <= 2)
        return ERROR_BAD_PATHNAME;

    if (pwszFileName[0] != L'\\' || pwszFileName[1] != L'\\')
        return ERROR_BAD_PATHNAME;

    pwszFullPath = (WCHAR *) malloc (sizeof (WCHAR) * (len + 1));
    if (NULL == pwszFullPath)
        return ERROR_OUTOFMEMORY;

    if (NULL == _wfullpath(pwszFullPath, pwszFileName, len + 1))
        return ERROR_BAD_PATHNAME;  //canonicalization was unsuccessful.
                                    // -- rarely happens

    pwszShrEnd = wcschr (pwszFullPath + 2, L'\\');

    if (NULL == pwszShrEnd)
        return ERROR_BAD_PATHNAME;  //the path does not have the share component

    pwszShrEnd++;

    pwszShrEnd = wcschr (pwszShrEnd, L'\\');

    if (NULL == pwszShrEnd)
    {
        //we already have the path in \\server\share form, so just try to
        //delete the share.
        if (!CSCDelete (pwszFullPath))
            return GetLastError();
    }

    //if we are here, then we have a path longer than just \\server\share.
    //so try to delete all the way up to the share name. This is necessary
    //because a user might be redirected to something like
    // \\server\share\folder\%username% and we wouldn't want only \\server\share
    // and \\server\share\folder to be cached.
    Status = ERROR_SUCCESS;
    do
    {
        pwszCurr = wcsrchr (pwszFullPath, L'\\');
        if (NULL == pwszCurr)
            break;
        *pwszCurr = L'\0';
        if (!CSCDelete (pwszFullPath))
        {
            Status = GetLastError();
            if (ERROR_SUCCESS == Status ||
                ERROR_INVALID_PARAMETER == Status ||
                ERROR_FILE_NOT_FOUND == Status ||
                ERROR_PATH_NOT_FOUND == Status)
            {
                Status = ERROR_SUCCESS;
            }
        }
        if (ERROR_SUCCESS != Status)
            break;
    } while ( pwszCurr > pwszShrEnd );

    return Status;
}

//+--------------------------------------------------------------------------
//
//  Function:   MergePinInfo
//
//  Synopsis:   merges the pin info. from the source to destination
//
//  Arguments:  [in] pwszSource : the full path to the source
//              [in] pwszDest   : the full path to the destination
//              [in] StatusFrom : CSC status of the source share
//              [in] StatusTo   : CSC status of the destination share
//
//  Returns:    ERROR_SUCCESS : if it was successful
//              a Win32 error code otherwise
//
//  History:    4/23/1999  RahulTh  created
//
//  Notes:      the hint flags are a union of the source hint flags and
//              destination hint flags. The pin count is the greater of the
//              source and destination pin count
//
//              Usually this function should only be called for folders. The
//              CSC rename API handles files well. But this function will work
//              for files as well.
//
//---------------------------------------------------------------------------
DWORD MergePinInfo (LPCTSTR pwszSource, LPCTSTR pwszDest,
                   SHARESTATUS StatusFrom, SHARESTATUS StatusTo)
{
    BOOL    bStatus;
    DWORD   dwSourceStat, dwDestStat;
    DWORD   dwSourcePinCount, dwDestPinCount;
    DWORD   dwSourceHints, dwDestHints;
    DWORD   Status = ERROR_SUCCESS;
    DWORD   i;

    if (ShareOffline == StatusFrom || ShareOffline == StatusTo)
        return ERROR_FILE_OFFLINE;

    if (ShareOnline != StatusFrom || ShareOnline != StatusTo)
        return ERROR_SUCCESS;       //there is nothing to do if one of the shares
                                    //is local.
    if (!pwszSource || !pwszDest ||
        0 == wcslen(pwszSource) || 0 == wcslen(pwszDest))
        return ERROR_BAD_PATHNAME;

    bStatus = CSCQueryFileStatus (pwszSource, &dwSourceStat, &dwSourcePinCount,
                                  &dwSourceHints);
    if (!bStatus)
        return GetLastError();

    bStatus = CSCQueryFileStatus (pwszDest, &dwDestStat, &dwDestPinCount,
                                  &dwDestHints);
    if (!bStatus)
        return GetLastError();

    //first set the hint flags on the destination
    if (dwDestHints != dwSourceHints)
    {
        bStatus = CSCPinFile (pwszDest, dwSourceHints, &dwDestStat,
                              &dwDestPinCount, &dwDestHints);
        if (!bStatus)
            Status = GetLastError();    //note: we do not bail out here. we try
                                        //to at least merge the pin count before
                                        //leaving
    }

    //now merge the pin count : there is nothing to be done if the destination
    //pin count is greater than or equal to the source pin count
    if (dwDestPinCount < dwSourcePinCount)
    {
        for (i = 0, bStatus = TRUE; i < (dwSourcePinCount - dwDestPinCount) &&
                                    bStatus;
             i++)
        {
            bStatus = CSCPinFile( pwszDest,
                                  FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
                                  NULL, NULL, NULL );
        }

        if (!bStatus && ERROR_SUCCESS == Status)
            Status = GetLastError();
    }

    return Status;
}

//+--------------------------------------------------------------------------
//
//  Function:   PinIfNecessary
//
//  Synopsis:   this function pins a file if necessary.
//
//  Arguments:  [in] pwszPath : full path of the file/folder to be pinned
//              [in] shStatus : CSC status of the share.
//
//  Returns:    ERROR_SUCCESS if it was successful. An error code otherwise.
//
//  History:    5/27/1999  RahulTh  created
//
//  Notes:
//
//---------------------------------------------------------------------------
DWORD PinIfNecessary (const WCHAR * pwszPath, SHARESTATUS shStatus)
{
    DWORD   Status = ERROR_SUCCESS;
    BOOL    bStatus;
    DWORD   dwStatus;
    DWORD   dwPinCount;
    DWORD   dwHints;

    if (!pwszPath || !pwszPath[0])
        return ERROR_BAD_NETPATH;

    
    if (ShareOffline == shStatus)
        return ERROR_FILE_OFFLINE;
    else if (PathLocal == shStatus || NoCSC == shStatus)
        return ERROR_SUCCESS;

    bStatus = CSCQueryFileStatus (pwszPath, &dwStatus, &dwPinCount, &dwHints);
    if (!bStatus || dwPinCount <= 0)
    {
        bStatus = CSCPinFile (pwszPath,
                              FLAG_CSC_HINT_COMMAND_ALTER_PIN_COUNT,
                              NULL, NULL, NULL);
        if (!bStatus)
            Status = GetLastError();
    }

    return Status;
}

//+--------------------------------------------------------------------------
//
//  Function:   GetCSCStatus
//
//  Synopsis:   given a path, finds out if it is local and if it is not
//              whether it is online or offline.
//
//  Arguments:  [in] pwszPath : the path to the file
//
//  Returns:    Local/Online/Offline
//
//  History:    11/20/1998  RahulTh  created
//
//  Notes:      it is important that the path passed to this function is a
//              a full path and not a relative path
//
//              this function will return offline if the share is not live or
//              if the share is live but CSC thinks that it is offline
//
//              it will return PathLocal if the path is local or if the path
//              is a network path that cannot be handled by CSC e.g. a network
//              share with a pathname longer than what csc can handle or if it
//              is a netware share. in this case it makes sense to return
//              PathLocal because CSC won't maintain a database for these shares
//              -- same as for a local path. so as far as CSC is concerned, this
//              is as good as a local path.
//
//---------------------------------------------------------------------------
SHARESTATUS GetCSCStatus (const WCHAR * pwszPath)
{
    WCHAR * pwszAbsPath = NULL;
    WCHAR * pwszCurr = NULL;
    int     len;
    BOOL    bRetVal;
    DWORD   Status;
    DWORD   dwPinCount;
    DWORD   dwHints;

    if (!pwszPath)
        return ShareOffline;    //a path must be provided

    len = wcslen (pwszPath);

    pwszAbsPath = (WCHAR *) malloc (sizeof (WCHAR) * (len + 1));

    if (!pwszAbsPath)
    {
        //we are out of memory, so it is safest to return ShareOffline
        //so that we can bail out of redirection.
        return ShareOffline;
    }

    //get the absolute path
    pwszCurr = _wfullpath (pwszAbsPath, pwszPath, len + 1);

    if (!pwszCurr)
    {
        //in order for _wfullpath to fail, something really bad has to happen
        //so it is best to return ShareOffline so that we can bail out of
        //redirection
        return ShareOffline;
    }

    len = wcslen (pwszAbsPath);

    if (! (
           (2 <= len) &&
           (L'\\' == pwszAbsPath[0]) &&
           (L'\\' == pwszAbsPath[1])
           )
       )
    {
        //it is a local path if it does not begin with 2 backslashes
        return PathLocal;
    }

    //this is a UNC path; so extract the \\server\share part
    pwszCurr = wcschr ( & (pwszAbsPath[2]), L'\\');

    //first make sure that it is at least of the form \\server\share
    //watch out for the \\server\ case
    if (!pwszCurr || !pwszCurr[1])
        return ShareOffline;        //it is an invalid path (no share name)

    //the path is of the form \\server\share
    //note: the use _wfullpath automatically protects us against the \\server\\ case
    pwszCurr = wcschr (&(pwszCurr[1]), L'\\');
    if (pwszCurr)   //if it is of the form \\server\share\...
        *pwszCurr = L'\0';

    //now pwszAbsPath is a share name
    bRetVal = CSCCheckShareOnline (pwszAbsPath);

    if (!bRetVal)
    {
        if (ERROR_SUCCESS != GetLastError())
        {
           //either there is really a problem (e.g. invalid share name) or
           //it is just a share that is not handled by CSC e.g. a netware share
           //or a share with a name that is longer than can be handled by CSC
           //so check if the share actually exists
           if (0xFFFFFFFF != GetFileAttributes(pwszAbsPath))
           {
              //this can still be a share that is offline since GetFileAttributes
              //will return the attributes stored in the cache
              Status = 0;
              bRetVal = GetShareStatus (pwszAbsPath, &Status, &dwPinCount,
                                            &dwHints);
              if (! bRetVal || (! (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & Status)))
                 return PathLocal;     //this is simply a valid path that CSC cannot handle
              else if (bRetVal &&
                       (FLAG_CSC_SHARE_STATUS_NO_CACHING ==
                            (FLAG_CSC_SHARE_STATUS_CACHING_MASK & Status)))
                  return PathLocal;     //CSC caching is not turned on for the share.
           }
        }

        //it is indeed an inaccessble share
        return ShareOffline;  //for all other cases, treat this as offline
    }
    else
    {
       //this means that the share is live, but CSC might still think that it
       //is offline.
       Status = 0;
       bRetVal = GetShareStatus (pwszAbsPath, &Status, &dwPinCount,
                                     &dwHints);
       if (bRetVal && (FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP & Status))
          return ShareOffline;   //CSC thinks that the share is offline
       else if (bRetVal &&
                (FLAG_CSC_SHARE_STATUS_NO_CACHING ==
                            (FLAG_CSC_SHARE_STATUS_CACHING_MASK & Status)))
           return PathLocal;    //CSC caching is not turned on for the share
       else if (!bRetVal)
           return ShareOffline;

       //in all other cases, consider the share as online since
       //CSCCheckShareOnline has already returned TRUE
       return ShareOnline;
    }
}

//+--------------------------------------------------------------------------
//
//  Function:   GetShareStatus
//
//  Synopsis:   this function is a wrapper for CSCQueryFileStatus.
//              basically CSCQueryFileStatus can fail if there was never a net
//              use to a share. So this function tries to create a net use to
//              the share if CSCQueryFileStatus fails and then re-queries the
//              file status
//
//  Arguments:  [in] pwszShare : the share name
//              [out] pdwStatus : the share Status
//              [out] pdwPinCount : the pin count
//              [out] pdwHints : the hints
//
//  Returns:    TRUE : if everything was successful.
//              FALSE : if there was an error. In this case, it GetLastError()
//                      will contain the specific error code.
//
//  History:    5/11/1999  RahulTh  created
//
//  Notes:      it is very important that this function be passed a share name
//              it does not do any parameter validation. So under no
//              circumstance should this function be passed a filename.
//
//---------------------------------------------------------------------------
BOOL GetShareStatus (const WCHAR * pwszShare, DWORD * pdwStatus,
                     DWORD * pdwPinCount, DWORD * pdwHints)
{
    NETRESOURCE nr;
    DWORD       dwResult;
    DWORD       dwErr = NO_ERROR;
    BOOL        bStatus;

    bStatus = CSCQueryFileStatus(pwszShare, pdwStatus, pdwPinCount, pdwHints);

    if (!bStatus)
    {
        //try to connect to the share
        ZeroMemory ((PVOID) (&nr), sizeof (NETRESOURCE));
        nr.dwType = RESOURCETYPE_DISK;
        nr.lpLocalName = NULL;
        nr.lpRemoteName = (LPTSTR) pwszShare;
        nr.lpProvider = NULL;

        dwErr = WNetUseConnection(NULL, &nr, NULL, NULL, 0,
                                  NULL, NULL, &dwResult);

        if (NO_ERROR == dwErr)
        {
            bStatus = CSCQueryFileStatus (pwszShare, pdwStatus, pdwPinCount, pdwHints);
            if (!bStatus)
                dwErr = GetLastError();
            else
                dwErr = NO_ERROR;

            WNetCancelConnection2 (pwszShare, 0, FALSE);
        }
        else
        {
            bStatus = FALSE;
        }

    }

    SetLastError(dwErr);
    return bStatus;
}


DWORD
CmdReconnect(
    PWSTR ReconnectArg)
{
    DWORD Error = ERROR_SUCCESS;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdReconnect(%ws)\r\n", ReconnectArg);

    fRet = CSCTransitionServerOnlineW(ReconnectArg);
    if (fRet == FALSE)
       Error = GetLastError();

    return Error;
}

DWORD
CmdQueryFile(
    PWSTR QueryFileArg)
{
    DWORD HintFlags = 0;
    DWORD PinCount = 0;
    DWORD Status = 0;
    DWORD Error = ERROR_SUCCESS;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdQueryFile(%ws)\r\n", QueryFileArg);

    fRet = CSCQueryFileStatusW(
                QueryFileArg,
                &Status,
                &PinCount,
                &HintFlags);

    if (fRet == FALSE) {
       Error = GetLastError();
       goto AllDone;
    }

    if (fSwFull != TRUE) {
        MyPrintf(
            L"QueryFile of %ws:\r\n"
            L"Status:                0x%x\r\n"
            L"PinCount:              %d\r\n"
            L"HintFlags:             0x%x\r\n",
                QueryFileArg,
                Status,
                PinCount,
                HintFlags);
    } else {
        WCHAR StatusBuffer[0x100];
        WCHAR HintBuffer[0x100];
   
        if (LooksToBeAShare(QueryFileArg) == TRUE)
            ShareStatusToEnglish(Status, StatusBuffer);
        else
            FileStatusToEnglish(Status, StatusBuffer);
        HintsToEnglish(HintFlags, HintBuffer);
        MyPrintf(
            L"QueryFile of %ws:\r\n"
            L"Status:    0x%x %ws\r\n"
            L"PinCount:  %d\r\n"
            L"HintFlags: 0x%x %ws\r\n",
                QueryFileArg,
                Status,
                StatusBuffer,
                PinCount,
                HintFlags,
                HintBuffer);
    }

AllDone:
    return Error;
}

DWORD
CmdQueryFileEx(
    PWSTR QueryFileExArg)
{
    DWORD HintFlags = 0;
    DWORD PinCount = 0;
    DWORD Status = 0;
    DWORD UserPerms = 0;
    DWORD OtherPerms = 0;
    DWORD Error = ERROR_SUCCESS;
    BOOL fRet;
    HMODULE hmodCscDll = LoadLibrary(TEXT("cscdll.dll"));
    CSCQUERYFILESTATUSEXW pCSCQueryFileStatusExW = NULL;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdQueryFileEx(%ws)\r\n", QueryFileExArg);

    if (hmodCscDll == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    pCSCQueryFileStatusExW = (CSCQUERYFILESTATUSEXW) GetProcAddress(
                                                        hmodCscDll,
                                                        "CSCQueryFileStatusExW");
    if (pCSCQueryFileStatusExW == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    fRet = (pCSCQueryFileStatusExW)(
                QueryFileExArg,
                &Status,
                &PinCount,
                &HintFlags,
                &UserPerms,
                &OtherPerms);

    if (fRet == FALSE) {
       Error = GetLastError();
       goto AllDone;
    }

    if (fSwFull != TRUE) {
        MyPrintf(
            L"Query of %ws:\r\n"
            L"Status:                0x%x\r\n"
            L"PinCount:              %d\r\n"
            L"HintFlags:             0x%x\r\n"
            L"UserPerms:             0x%x\r\n"
            L"OtherPerms:            0x%x\r\n",
                QueryFileExArg,
                Status,
                PinCount,
                HintFlags,
                UserPerms,
                OtherPerms);
    } else {
        WCHAR StatusBuffer[0x100] = {0};
        WCHAR HintBuffer[0x100] = {0};
   
        if (LooksToBeAShare(QueryFileExArg) == TRUE)
            ShareStatusToEnglish(Status, StatusBuffer);
        else
            FileStatusToEnglish(Status, StatusBuffer);
        HintsToEnglish(HintFlags, HintBuffer);
        MyPrintf(
            L"Query of %ws:\r\n"
            L"Status:     0x%x %ws\r\n"
            L"PinCount:   %d\r\n"
            L"HintFlags:  0x%x %ws\r\n"
            L"UserPerms:  0x%x\r\n"
            L"OtherPerms: 0x%x\r\n",
                QueryFileExArg,
                Status,
                StatusBuffer,
                PinCount,
                HintFlags,
                HintBuffer,
                UserPerms,
                OtherPerms);
    }

AllDone:
    if (hmodCscDll != NULL)
        FreeLibrary(hmodCscDll);
    return Error;
}

DWORD
CmdSetSpace(
    PWSTR SetSpaceArg)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD MaxSpaceHigh = 0;
    DWORD MaxSpaceLow;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSetSpace(%ws)\r\n", SetSpaceArg);

    swscanf(SetSpaceArg, L"%d", &MaxSpaceLow);

    fRet = CSCSetMaxSpace(
                MaxSpaceHigh,
                MaxSpaceLow);

    if (fRet == FALSE) {
       Error = GetLastError();
       goto AllDone;
    }

    CmdGetSpace();

AllDone:
    return Error;
}

DWORD
CmdQueryShare(
    PWSTR QueryShareArg)
{
    DWORD HintFlags = 0;
    DWORD PinCount = 0;
    DWORD Status = 0;
    DWORD UserPerms = 0;
    DWORD OtherPerms = 0;
    DWORD Error = ERROR_SUCCESS;
    BOOL fRet;
    HMODULE hmodCscDll = LoadLibrary(TEXT("cscdll.dll"));
    CSCQUERYSHARESTATUSW pCSCQueryShareStatusW = NULL;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdQueryShare(%ws)\r\n", QueryShareArg);

    if (hmodCscDll == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    pCSCQueryShareStatusW = (CSCQUERYSHARESTATUSW) GetProcAddress(
                                                        hmodCscDll,
                                                        "CSCQueryShareStatusW");
    if (pCSCQueryShareStatusW == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    fRet = (pCSCQueryShareStatusW)(
                QueryShareArg,
                &Status,
                &PinCount,
                &HintFlags,
                &UserPerms,
                &OtherPerms);

    if (fRet == FALSE) {
       Error = GetLastError();
       goto AllDone;
    }

    if (fSwFull != TRUE) {
        MyPrintf(
            L"Query of %ws:\r\n"
            L"Status:                0x%x\r\n"
            L"PinCount:              %d\r\n"
            L"HintFlags:             0x%x\r\n"
            L"UserPerms:             0x%x\r\n"
            L"OtherPerms:            0x%x\r\n",
                QueryShareArg,
                Status,
                PinCount,
                HintFlags,
                UserPerms,
                OtherPerms);
    } else {
        WCHAR StatusBuffer[0x100] = {0};
        WCHAR HintBuffer[0x100] = {0};
   
        ShareStatusToEnglish(Status, StatusBuffer);
        HintsToEnglish(HintFlags, HintBuffer);
        MyPrintf(
            L"Query of %ws:\r\n"
            L"Status:     0x%x %ws\r\n"
            L"PinCount:   %d\r\n"
            L"HintFlags:  0x%x %ws\r\n"
            L"UserPerms:  0x%x\r\n"
            L"OtherPerms: 0x%x\r\n",
                QueryShareArg,
                Status,
                StatusBuffer,
                PinCount,
                HintFlags,
                HintBuffer,
                UserPerms,
                OtherPerms);
    }
AllDone:
    if (hmodCscDll != NULL)
        FreeLibrary(hmodCscDll);
    return Error;
}

DWORD
CmdMerge(
    PWSTR MergeArg)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD HowToRespond = CSCPROC_RETURN_CONTINUE; // JMH
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdMerge(%ws)\r\n", MergeArg);

    fRet = CSCMergeShareW(MergeArg, MyCscMergeProcW, HowToRespond);
    if (fRet == FALSE) {
        Error = GetLastError();
        goto AllDone;
    }

    fRet = CSCTransitionServerOnlineW(MergeArg);
    if (fRet == FALSE)
       Error = GetLastError();

AllDone:
    return Error;
}

DWORD
CmdMove(
    PWSTR MoveArg)
{

	DWORD Error = ERROR_FILE_NOT_FOUND;
	LPWSTR  lpszTempName = NULL;

    if (!CSCCopyReplicaW(MoveArg, &lpszTempName)) {
        Error = GetLastError();
    } else {
        Error = ERROR_SUCCESS;
    }

	if (Error == ERROR_SUCCESS)
        MyPrintf(L"Copy is %ws\r\n", lpszTempName);

	return Error;
}


DWORD
CmdEncryptDecrypt(
    BOOL fEncrypt)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD EncryptDecryptType = 0;
    BOOL fRet;
    HMODULE hmodCscDll = LoadLibrary(TEXT("cscdll.dll"));
    CSCENCRYPTDECRYPTDATABASE pCSCEncryptDecryptDatabase = NULL;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdEncryptDecrypt(%ws)\r\n", fEncrypt == TRUE ? L"Encrypt" : L"Decrypt");

    if (hmodCscDll == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    pCSCEncryptDecryptDatabase = (CSCENCRYPTDECRYPTDATABASE) GetProcAddress(
                                                                hmodCscDll,
                                                                "CSCEncryptDecryptDatabase");
    if (pCSCEncryptDecryptDatabase == NULL) {
        Error = GetLastError();
        goto AllDone;
    }

    fRet = (pCSCEncryptDecryptDatabase)(fEncrypt, MyEncryptDecryptProcW, EncryptDecryptType);
    if (fRet == FALSE) {
        Error = GetLastError();
        goto AllDone;
    }

AllDone:

    if (hmodCscDll != NULL)
        FreeLibrary(hmodCscDll);

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdEncryptDecrypt exit %d\r\n", Error);

    return Error;
}

DWORD
CmdFill(
    PWSTR FillArg)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD HowToRespond = CSCPROC_RETURN_CONTINUE;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdFill(%ws, %d)\r\n", FillArg, fSwFull);

    fRet = CSCFillSparseFilesW(FillArg, fSwFull, MyCscFillProcW, HowToRespond);
    if (fRet == FALSE) {
        Error = GetLastError();
    }

    return Error;
}

DWORD
CmdCheck(
    PWSTR CheckArg)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD Speed = 0;
    BOOL fOnline = FALSE;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdCheck(%ws)\r\n", CheckArg);

    fOnline = CSCCheckShareOnlineExW(CheckArg, &Speed);

    MyPrintf(
        L"%ws is %s\r\n",
            CheckArg,
            (fOnline == FALSE) ? L"Offline" : L"Online");

    return Error;
}

DWORD
CmdDBStatus(
    VOID)
{
    GLOBALSTATUS sGS = {0};
    ULONG DBStatus = 0;
    ULONG DBErrorFlags = 0;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDBStatus()\r\n");

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdDBStatus:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_GETGLOBALSTATUS,          // control code
                (LPVOID)&sGS,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdDBStatus:DeviceIoControl IOCTL_GETGLOBALSTATUS failed\n");
        Status = GetLastError();
        goto AllDone;
    }

    DBStatus = sGS.sST.uFlags;
    DBErrorFlags = sGS.uDatabaseErrorFlags;

    if (DBStatus & FLAG_DATABASESTATUS_DIRTY)
        MyPrintf(L"FLAG_DATABASESTATUS_DIRTY\r\n");
    if (DBStatus & FLAG_DATABASESTATUS_UNENCRYPTED)
        MyPrintf(L"FLAG_DATABASESTATUS_UNENCRYPTED\r\n");
    if (DBStatus & FLAG_DATABASESTATUS_PARTIALLY_UNENCRYPTED)
        MyPrintf(L"FLAG_DATABASESTATUS_PARTIALLY_UNENCRYPTED\r\n");
    if (DBStatus & FLAG_DATABASESTATUS_ENCRYPTED)
        MyPrintf(L"FLAG_DATABASESTATUS_ENCRYPTED\r\n");
    if (DBStatus & FLAG_DATABASESTATUS_PARTIALLY_ENCRYPTED)
        MyPrintf(L"FLAG_DATABASESTATUS_PARTIALLY_ENCRYPTED\r\n");
	
	MyPrintf(L"Database Error Flags : 0x%x\r\n", DBErrorFlags);

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDBStatus exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdPurge(
    PWSTR PurgeArg)
{
    ULONG nFiles = 0;
    ULONG nYoungFiles = 0;
    ULONG Status = ERROR_SUCCESS;
    ULONG Timeout = 120;
    BOOL fRet = FALSE;
    HMODULE hmodCscDll = LoadLibrary(TEXT("cscdll.dll"));
    CSCPURGEUNPINNEDFILES pCSCPurgeUnpinnedFiles = NULL;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdPurge(%ws)\r\n", PurgeArg);

    if (hmodCscDll == NULL) {
        Status = GetLastError();
        goto AllDone;
    }

    pCSCPurgeUnpinnedFiles = (CSCPURGEUNPINNEDFILES) GetProcAddress(
                                                        hmodCscDll,
                                                        "CSCPurgeUnpinnedFiles");
    if (pCSCPurgeUnpinnedFiles == NULL) {
        Status = GetLastError();
        goto AllDone;
    }

    if (PurgeArg != NULL)
        swscanf(PurgeArg, L"%d", &Timeout);

    if (fSwDebug == TRUE)
        MyPrintf(L"Timeout=%d seconds\r\n", Timeout);

    fRet = (pCSCPurgeUnpinnedFiles)(Timeout, &nFiles, &nYoungFiles);
    if (fRet == FALSE) {
        Status = GetLastError();
        goto AllDone;
    }
    MyPrintf(L"nFiles = %d nYoungFiles=%d\n", nFiles, nYoungFiles);

AllDone:
    if (hmodCscDll != NULL)
        FreeLibrary(hmodCscDll);
    if (fSwDebug == TRUE)
        MyPrintf(L"CmdPurge exit %d\r\n", Status);

    return Status;
}

DWORD
CmdPQEnum(
    VOID)
{
    PQPARAMS PQP = {0};
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdPQEnum()\r\n");

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdPQEnum:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    MyPrintf(L"  POS SHARE      DIR   SHADOW   STATUS REFPRI   HPRI HINTFLG HINTPRI  VER\r\n");

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SHADOW_BEGIN_PQ_ENUM,     // control code
                (LPVOID)&PQP,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdPQEnum:DeviceIoControl IOCTL_SHADOW_BEGIN_PQ_ENUM failed\n");
        Status = GetLastError();
        goto AllDone;
    }

    do {
        bResult = DeviceIoControl(
                    hDBShadow,                      // device 
                    IOCTL_SHADOW_NEXT_PRI_SHADOW,   // control code
                    (LPVOID)&PQP,                   // in buffer
                    0,                              // inbuffer size
                    NULL,                           // out buffer
                    0,                              // out buffer size
                    &junk,                          // bytes returned
                    NULL);                          // overlapped
        if (bResult) {
           MyPrintf(L"%5d %5x %8x %8x %8x %6d %6x %7d %7d %4d\r\n",
                PQP.uPos,
                PQP.hShare,
                PQP.hDir,
                PQP.hShadow,
                PQP.ulStatus,
                PQP.ulRefPri,
                PQP.ulIHPri,
                PQP.ulHintFlags,
                PQP.ulHintPri,
                PQP.dwPQVersion);
        }
    } while (bResult && PQP.uPos != 0);

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SHADOW_END_PQ_ENUM,       // control code
                (LPVOID)&PQP,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdPQEnum exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

LPWSTR
ConvertGmtTimeToString(
    FILETIME Time,
    LPWSTR OutputBuffer)
{
    FILETIME LocalTime;
    SYSTEMTIME SystemTime;

    static FILETIME ftNone = {0, 0};

    if (memcmp(&Time, &ftNone, sizeof(FILETIME)) == 0) {
        wsprintf (OutputBuffer, L"<none>");
    } else {
        FileTimeToLocalFileTime( &Time , &LocalTime );
        FileTimeToSystemTime( &LocalTime, &SystemTime );
        wsprintf(
            OutputBuffer,
            L"%02u/%02u/%04u %02u:%02u:%02u ",
                    SystemTime.wMonth,
                    SystemTime.wDay,
                    SystemTime.wYear,
                    SystemTime.wHour,
                    SystemTime.wMinute,
                    SystemTime.wSecond );
    }
    return( OutputBuffer );
}

VOID
DumpCscEntryInfo(
    LPWSTR Path,
    PWIN32_FIND_DATA Find32,
    DWORD Status,
    DWORD PinCount,
    DWORD HintFlags,
    PFILETIME OrgTime)
{
    WCHAR TimeBuf1[40];
    WCHAR TimeBuf2[40];
    FILE *fp = NULL;

    if (fSwTouch == TRUE) {
        if (Path == NULL) {
            wsprintf(NameBuf, L"%ws", Find32->cFileName);
        } else {
            wsprintf(NameBuf, L"%ws\\%ws", Path, Find32->cFileName);
        }
        MyPrintf(L"%ws\r\n", NameBuf);
        if ((Find32->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
            fp = _wfopen(NameBuf, L"rb");
            if (fp != NULL)
                fclose(fp);
        }
    } else if (fSwFull == TRUE) {
        WCHAR StatusBuffer[0x100];
        WCHAR HintBuffer[0x100];

        if (Path != NULL)
            MyPrintf(L"Directory %ws\r\n", Path);
        ConvertGmtTimeToString(Find32->ftLastWriteTime, TimeBuf1);
        MyPrintf(
            L"LFN:           %s\r\n"
            L"SFN:           %s\r\n"
            L"Attr:          0x%x\r\n"
            L"Size:          0x%x:0x%x\r\n"
            L"LastWriteTime: %ws\r\n",
                Find32->cFileName,
                Find32->cAlternateFileName,
                Find32->dwFileAttributes,
                Find32->nFileSizeHigh, Find32->nFileSizeLow,
                TimeBuf1);

        if (OrgTime) {
            ConvertGmtTimeToString(*OrgTime, TimeBuf1);
            MyPrintf(L"ORGTime:       %ws\r\n", TimeBuf1);
        }
        StatusBuffer[0] = L'\0';
        HintBuffer[0] = L'\0';
        if (Path == NULL) {
            ShareStatusToEnglish(Status, StatusBuffer);
            HintsToEnglish(HintFlags, HintBuffer);
        } else {
            if (LooksToBeAShare(Path) == TRUE)
                ShareStatusToEnglish(Status, StatusBuffer);
            else
                FileStatusToEnglish(Status, StatusBuffer);
            HintsToEnglish(HintFlags, HintBuffer);
        }
        MyPrintf(
            L"Status:        0x%x %ws\r\n"
            L"PinCount:      %d\r\n"
            L"HintFlags:     0x%x %ws\r\n\r\n",
                Status,
                StatusBuffer,
                PinCount,
                HintFlags,
                HintBuffer);
    } else {
        if (Path == NULL) {
            MyPrintf(L"%ws\r\n", Find32->cFileName);
        } else {
            MyPrintf(L"%ws\\%ws\r\n", Path, Find32->cFileName);
        }
        MyPrintf(L"  Attr=0x%x Size=0x%x:0x%x Status=0x%x PinCount=%d HintFlags=0x%x\r\n",
                Find32->dwFileAttributes,
                Find32->nFileSizeHigh, Find32->nFileSizeLow,
                Status,
                PinCount,
                HintFlags);
        ConvertGmtTimeToString(Find32->ftLastWriteTime, TimeBuf1);
        if (OrgTime)
            ConvertGmtTimeToString(*OrgTime, TimeBuf2);
        else
            wcscpy(TimeBuf2, L"<none>");
        MyPrintf(L"  LastWriteTime: %ws OrgTime: %ws\r\n",
                        TimeBuf1,
                        TimeBuf2);
        MyPrintf(L"\r\n");
    }
}

DWORD
CmdEnum(
    PWSTR EnumArg)
{
	HANDLE hFind;
	DWORD Error = ERROR_SUCCESS;
    DWORD Status = 0;
    DWORD PinCount = 0;
    DWORD HintFlags = 0;
	FILETIME ftOrgTime = {0};
	WIN32_FIND_DATAW sFind32 = {0};
    WCHAR FullPath[MAX_PATH];

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdEnum(%ws)\r\n", EnumArg);

    hFind = CSCFindFirstFileW(EnumArg, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime);
    if (hFind == INVALID_HANDLE_VALUE) {
        Error = GetLastError();
        goto AllDone;
    }
    DumpCscEntryInfo(EnumArg, &sFind32, Status, PinCount, HintFlags, &ftOrgTime);
    if (fSwRecurse == TRUE && (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
        if (EnumArg != NULL) {
            wcscpy(FullPath, EnumArg);
            wcscat(FullPath, L"\\");
        } else {
            wcscpy(FullPath, L"");
        }
        wcscat(FullPath, sFind32.cFileName);
        CmdEnum(FullPath);
    }
    while (CSCFindNextFileW(hFind, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime)) {
        DumpCscEntryInfo(EnumArg, &sFind32, Status, PinCount, HintFlags, &ftOrgTime);
        if (fSwRecurse == TRUE && (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
            if (EnumArg != NULL) {
                wcscpy(FullPath, EnumArg);
                wcscat(FullPath, L"\\");
            } else {
                wcscpy(FullPath, L"");
            }
            wcscat(FullPath, sFind32.cFileName);
            CmdEnum(FullPath);
        }
    }
    CSCFindClose(hFind);

AllDone:
	return (Error);
}

DWORD
CmdEnumForStats(
    PWSTR EnumForStatsArg)
{
    DWORD Error = ERROR_SUCCESS;
    DWORD EnumForStatsType = 0;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdEnumForStats()\r\n");

    fRet = CSCEnumForStatsW(EnumForStatsArg, MyEnumForStatsProcW, EnumForStatsType);
    if (fRet == FALSE) {
        Error = GetLastError();
        goto AllDone;
    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdEnumForStats exit %d\r\n", Error);

    return Error;
}

DWORD
CmdDelete(
    PWSTR DeleteArg)
{
	DWORD Error = ERROR_SUCCESS;
    DWORD Status = 0;
    DWORD PinCount = 0;
    DWORD HintFlags = 0;
	FILETIME ftOrgTime = {0};
    BOOL fResult;
	HANDLE hFind;
	WIN32_FIND_DATAW sFind32 = {0};
    WCHAR FullPath[MAX_PATH];


    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDelete(%ws)\r\n", DeleteArg);

    //
    // Non-recursive delete
    //
    if (fSwRecurse == FALSE) {
        fResult = CSCDeleteW(DeleteArg);
        if (fResult == FALSE)
            Error = GetLastError();
        goto AllDone;
    }
    //
    // Delete recursively, using eumeration
    //
    hFind = CSCFindFirstFileW(DeleteArg, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime);
    if (hFind == INVALID_HANDLE_VALUE) {
        Error = GetLastError();
        goto AllDone;
    }
    if (DeleteArg != NULL) {
        wcscpy(FullPath, DeleteArg);
        wcscat(FullPath, L"\\");
    } else {
        wcscpy(FullPath, L"");
    }
    wcscat(FullPath, sFind32.cFileName);
    if (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        CmdDelete(FullPath);
    MyPrintf(L"CSCDeleteW(%ws) -> %d\r\n", FullPath, CSCDeleteW(FullPath));
    while (CSCFindNextFileW(hFind, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime)) {
        if (DeleteArg != NULL) {
            wcscpy(FullPath, DeleteArg);
            wcscat(FullPath, L"\\");
        } else {
            wcscpy(FullPath, L"");
        }
        wcscat(FullPath, sFind32.cFileName);
        if (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            CmdDelete(FullPath);
        MyPrintf(L"CSCDeleteW(%ws) -> %d\r\n", FullPath, CSCDeleteW(FullPath));
    }
    CSCFindClose(hFind);

AllDone:
	return (Error);
}

DWORD
CmdDeleteShadow(
    PWSTR DeleteShadowArg)
{
    HSHADOW hDir;
    HSHADOW hShadow;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;
    ULONG hShare;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDeleteShadow(%ws)\r\n", DeleteShadowArg);

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdDeleteShadow:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    swscanf(DeleteShadowArg, L"0x%x:0x%x", &hDir, &hShadow);

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDeleteShadow: hDir:0x%x hShadow:0x%x\r\n", hDir, hShadow);

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.hDir = hDir;
    sSI.hShadow = hShadow;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SHADOW_DELETE,            // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(
                L"CmdDeleteShadow:DeviceIoControl IOCTL_SHADOW_DELETE failed 0x%x\n",
                sSI.dwError);
        Status = sSI.dwError;
        goto AllDone;
    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDeleteShadow exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdPinUnPin(
    BOOL fPin,
    PWSTR PinArg)
{
    BOOL fRet;
	HANDLE hFind;
	DWORD Error = ERROR_SUCCESS;
    DWORD Status = 0;
    DWORD PinCount = 0;
    DWORD HintFlags = 0;
	FILETIME ftOrgTime = {0};
	WIN32_FIND_DATAW sFind32 = {0};
    WCHAR FullPath[MAX_PATH];

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdPinUnPin(%d,%ws)\r\n", fPin, PinArg);

    if (fSwUser == TRUE && fSwSystem == TRUE) {
        MyPrintf(L"Can not use both /SYSTEM and /USER\r\n");
        goto AllDone;
    }

    if (fSwUser == TRUE) {
        if (fSwInherit == TRUE)
            HintFlags |= FLAG_CSC_HINT_PIN_INHERIT_USER;
        else
            HintFlags |= FLAG_CSC_HINT_PIN_USER;
    }
    if (fSwSystem == TRUE) {
        if (fSwInherit == TRUE)
            HintFlags |= FLAG_CSC_HINT_PIN_INHERIT_SYSTEM;
        else
            HintFlags |= FLAG_CSC_HINT_PIN_SYSTEM;
    }

    if (fSwRecurse == TRUE && fPin == TRUE) {
        MyPrintf(L"Can not pin recursively.\r\n");
        goto AllDone;
    }
    //
    // Pin/Unpin one file
    //
    if (fSwRecurse == FALSE) {
        if (fPin == TRUE) {
            fRet = CSCPinFileW(PinArg, HintFlags, &Status, &PinCount, &HintFlags);
        } else {
            fRet = CSCUnpinFileW(PinArg, HintFlags, &Status, &PinCount, &HintFlags);
        }

        if (fRet == FALSE) {
            Error = GetLastError();
            goto AllDone;
        }

        MyPrintf(
            L"%ws of %ws:\r\n"
            L"Status:                0x%x\r\n"
            L"PinCount:              %d\r\n"
            L"HintFlags:             0x%x\r\n",
                fPin ? L"Pin" : L"Unpin",
                PinArg,
                Status,
                PinCount,
                HintFlags);

        goto AllDone;
    }
    //
    // Unpin recursively, using eumeration
    //
    hFind = CSCFindFirstFileW(PinArg, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime);
    if (hFind == INVALID_HANDLE_VALUE) {
        Error = GetLastError();
        goto AllDone;
    }
    if (PinArg != NULL) {
        wcscpy(FullPath, PinArg);
        wcscat(FullPath, L"\\");
    } else {
        wcscpy(FullPath, L"");
    }
    wcscat(FullPath, sFind32.cFileName);
    if (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        CmdPinUnPin(fPin, FullPath);
    fRet = CSCUnpinFileW(FullPath, HintFlags, &Status, &PinCount, &HintFlags);
    MyPrintf(L"CSCUnpinFile(%ws) -> %d\r\n", FullPath, fRet);
    while (CSCFindNextFileW(hFind, &sFind32, &Status, &PinCount, &HintFlags, &ftOrgTime)) {
        if (PinArg != NULL) {
            wcscpy(FullPath, PinArg);
            wcscat(FullPath, L"\\");
        } else {
            wcscpy(FullPath, L"");
        }
        wcscat(FullPath, sFind32.cFileName);
        if (sFind32.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            CmdPinUnPin(fPin, FullPath);
        fRet = CSCUnpinFileW(FullPath, HintFlags, &Status, &PinCount, &HintFlags);
        MyPrintf(L"CSCUnpinFile(%ws) -> %d\r\n", FullPath, fRet);
    }
    CSCFindClose(hFind);
                
AllDone:

    return Error;
}

DWORD
MyCscMergeProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext)
{
	if (dwReason == CSCPROC_REASON_BEGIN || dwReason == CSCPROC_REASON_MORE_DATA) {
        if (dwReason == CSCPROC_REASON_BEGIN) {
            MyPrintf( L"BEGIN[%ws][%d/%d]",
                (lpszFullPath) ? lpszFullPath : L"None",
                dwParam1,
                dwParam2);
        } else if (dwReason == CSCPROC_REASON_MORE_DATA) {
            MyPrintf( L"MORE_DATA[%ws][%d/%d]",
                (lpszFullPath) ? lpszFullPath : L"None",
                dwParam1,
                dwParam2);
        }
        if (fSwAbort) {
            MyPrintf(L":Abort\r\n");
            return CSCPROC_RETURN_ABORT;
        } else if (fSwSkip) {
            MyPrintf(L":Skip\r\n");
            return CSCPROC_RETURN_SKIP;
        } else if (fSwRetry) {
            MyPrintf(L"Retry\r\n");
            return CSCPROC_RETURN_RETRY;
        } else if (fSwAsk) {
            MyPrintf(L" - (R)etry/(A)bort/(S)kip/(C)ontinue:");
            return CscMergeFillAsk(lpszFullPath);
        } else {
            MyPrintf(L"Continue\r\n");
            return (DWORD)dwContext;
        }
    }

    MyPrintf( L"END[%ws]:", (lpszFullPath) ? lpszFullPath : L"None");
    if (dwParam2 == ERROR_SUCCESS) {
        MyPrintf(L"SUCCEEDED\r\n");
    } else {
        MyPrintf(L"ERROR=%d \r\n", dwParam2);
    }
    return (DWORD)dwContext;
}

DWORD
MyCscFillProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext)
{
	if (dwReason == CSCPROC_REASON_BEGIN || dwReason == CSCPROC_REASON_MORE_DATA) {
        if (dwReason == CSCPROC_REASON_BEGIN) {
            MyPrintf( L"BEGIN[%ws][%d/%d]",
                (lpszFullPath) ? lpszFullPath : L"None",
                dwParam1,
                dwParam2);
        } else if (dwReason == CSCPROC_REASON_MORE_DATA) {
            MyPrintf( L"MORE_DATA[%ws][%d/%d]",
                (lpszFullPath) ? lpszFullPath : L"None",
                dwParam1,
                dwParam2);
        }
        if (fSwAbort) {
            MyPrintf(L":Abort\r\n");
            return CSCPROC_RETURN_ABORT;
        } else if (fSwSkip) {
            MyPrintf(L":Skip\r\n");
            return CSCPROC_RETURN_SKIP;
        } else if (fSwRetry) {
            MyPrintf(L"Retry\r\n");
            return CSCPROC_RETURN_RETRY;
        } else if (fSwAsk) {
            MyPrintf(L" - (R)etry/(A)bort/(S)kip/(C)ontinue:");
            return CscMergeFillAsk(lpszFullPath);
        } else {
            MyPrintf(L"Continue\r\n");
            return (DWORD)dwContext;
        }
    }

    MyPrintf( L"END[%ws]:", (lpszFullPath) ? lpszFullPath : L"None");
    if (dwParam2 == ERROR_SUCCESS) {
        MyPrintf(L"SUCCEEDED\r\n");
    } else {
        MyPrintf(L"ERROR=%d \r\n", dwParam2);
    }
    return (DWORD)dwContext;
}

DWORD
CscMergeFillAsk(LPCWSTR lpszFullPath)
{
    WCHAR wch;
    ULONG ulid;
    LONG cnt;
    WCHAR rgwch[256];
    PWCHAR lpBuff = NULL;

    do {
        lpBuff = rgwch;
        memset(rgwch, 0, sizeof(rgwch));
        if (!fgetws(rgwch, sizeof(rgwch)/sizeof(WCHAR), stdin))
           break;
        // Chop leading blanks
        if (lpBuff != NULL)
            while (*lpBuff != L'\0' && *lpBuff == L' ')
                lpBuff++;

        cnt = swscanf(lpBuff, L"%c", &wch);

        if (!cnt)
            continue;

        switch (wch) {
            case L's': case L'S':
                return CSCPROC_RETURN_SKIP;
            case L'c': case L'C':
                return CSCPROC_RETURN_CONTINUE;
            case L'a': case L'A':
                return CSCPROC_RETURN_ABORT;
            case L'r': case L'R':
                return CSCPROC_RETURN_RETRY;
        }
    } while (1);

    return CSCPROC_RETURN_CONTINUE;
}

DWORD
MyEncryptDecryptProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext)
{
	if (dwReason == CSCPROC_REASON_BEGIN) {
        return CSCPROC_RETURN_CONTINUE;
	} else if (dwReason == CSCPROC_REASON_MORE_DATA) {
		MyPrintf(L"%ws\r\n", (lpszFullPath != NULL) ? lpszFullPath : L"None");
        return CSCPROC_RETURN_CONTINUE;
    }
    //
    // CSC_PROC_END
    //
    if (dwParam2 == ERROR_SUCCESS) {
        MyPrintf(L"Succeeded\r\n");
    } else {
        MyPrintf(L"Error=%d \r\n", dwParam2);
    }
	return CSCPROC_RETURN_CONTINUE;
}

DWORD
MyEnumForStatsProcW(
     LPCWSTR         lpszFullPath,
     DWORD           dwStatus,
     DWORD           dwHintFlags,
     DWORD           dwPinCount,
     WIN32_FIND_DATAW *lpFind32,
     DWORD           dwReason,
     DWORD           dwParam1,
     DWORD           dwParam2,
     DWORD_PTR       dwContext)
{
	if (dwReason == CSCPROC_REASON_BEGIN) {
		MyPrintf(L"(1)%ws ", (lpszFullPath != NULL) ? lpszFullPath : L"None");
        if (lpFind32 != NULL)
            MyPrintf(L"[%ws]\r\n", lpFind32->cFileName);
        MyPrintf(L" Status=0x%02x HintFlags=0x%02x "
                 L"Pincount=%3d Reason=0x%x Param1=0x%x Param2=0x%x\r\n",
                        dwStatus,
                        dwHintFlags,
                        dwPinCount,
                        dwReason,
                        dwParam1,
                        dwParam2);
        return CSCPROC_RETURN_CONTINUE;
	} else if (dwReason == CSCPROC_REASON_MORE_DATA) {
		MyPrintf(L"(2)%ws ", (lpszFullPath != NULL) ? lpszFullPath : L"None");
        if (lpFind32 != NULL)
            MyPrintf(L" %ws\r\n", lpFind32->cFileName);
        MyPrintf(L" Status=0x%02x HintFlags=0x%02x "
                 L"Pincount=%3d Reason=0x%x Param1=0x%x Param2=0x%x\r\n",
                        dwStatus,
                        dwHintFlags,
                        dwPinCount,
                        dwReason,
                        dwParam1,
                        dwParam2);
        return CSCPROC_RETURN_CONTINUE;
    }
    //
    // CSC_PROC_END
    //
    MyPrintf(L"(3)%ws\r\n", (lpszFullPath != NULL) ? lpszFullPath : L"None");
    if (dwParam2 == ERROR_SUCCESS) {
        MyPrintf(L"Succeeded\r\n");
    } else {
        MyPrintf(L"Error=%d \r\n", dwParam2);
    }
	return CSCPROC_RETURN_CONTINUE;
}

struct {
    DWORD ShareStatus;
    LPWSTR ShareStatusName;
} ShareStatusStuff[] = {
    { FLAG_CSC_SHARE_STATUS_MODIFIED_OFFLINE, L"MODIFIED_OFFLINE " },
    { FLAG_CSC_SHARE_STATUS_CONNECTED, L"CONNECTED " },
    { FLAG_CSC_SHARE_STATUS_FILES_OPEN, L"FILES_OPEN " },
    { FLAG_CSC_SHARE_STATUS_FINDS_IN_PROGRESS, L"FINDS_IN_PROGRESS " },
    { FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP, L"DISCONNECTED_OP " },
    { FLAG_CSC_SHARE_MERGING, L"MERGING " },
    { 0, NULL }
};

DWORD
ShareStatusToEnglish(
    DWORD Status,
    LPWSTR OutputBuffer)
{
    ULONG i;

    OutputBuffer[0] = L'\0';
    for (i = 0; ShareStatusStuff[i].ShareStatusName; i++) {
        if (Status & ShareStatusStuff[i].ShareStatus)
            wcscat(OutputBuffer, ShareStatusStuff[i].ShareStatusName);
    }
    if ((Status & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_MANUAL_REINT)
        wcscat(OutputBuffer, L"MANUAL_REINT ");
    else if ((Status & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_AUTO_REINT)
        wcscat(OutputBuffer, L"AUTO_REINT ");
    else if ((Status & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_VDO)
        wcscat(OutputBuffer, L"VDO ");
    else if ((Status & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_NO_CACHING)
        wcscat(OutputBuffer, L"NO_CACHING ");
    return 0;
}

struct {
    DWORD FileStatus;
    LPWSTR FileStatusName;
} FileStatusStuff[] = {
    { FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED, L"DATA_LOCALLY_MODIFIED " },
    { FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED, L"ATTRIB_LOCALLY_MODIFIED " },
    { FLAG_CSC_COPY_STATUS_TIME_LOCALLY_MODIFIED, L"TIME_LOCALLY_MODIFIED " },
    { FLAG_CSC_COPY_STATUS_STALE, L"STALE " },
    { FLAG_CSC_COPY_STATUS_LOCALLY_DELETED, L"LOCALLY_DELETED " },
    { FLAG_CSC_COPY_STATUS_SPARSE, L"SPARSE " },
    { FLAG_CSC_COPY_STATUS_ORPHAN, L"ORPHAN " },
    { FLAG_CSC_COPY_STATUS_SUSPECT, L"SUSPECT " },
    { FLAG_CSC_COPY_STATUS_LOCALLY_CREATED, L"LOCALLY_CREATED " },
    { 0x00010000, L"USER_READ " },
    { 0x00020000, L"USER_WRITE " },
    { 0x00040000, L"GUEST_READ " },
    { 0x00080000, L"GUEST_WRITE " },
    { 0x00100000, L"OTHER_READ " },
    { 0x00200000, L"OTHER_WRITE " },
    { FLAG_CSC_COPY_STATUS_IS_FILE, L"IS_FILE " },
    { FLAG_CSC_COPY_STATUS_FILE_IN_USE, L"FILE_IN_USE " },
    { 0, NULL }
};

DWORD
FileStatusToEnglish(
    DWORD Status,
    LPWSTR OutputBuffer)
{
    ULONG i;

    OutputBuffer[0] = L'\0';
    for (i = 0; FileStatusStuff[i].FileStatusName; i++) {
        if (Status & FileStatusStuff[i].FileStatus)
            wcscat(OutputBuffer, FileStatusStuff[i].FileStatusName);
    }
    return 0;
}

struct {
    DWORD HintFlag;
    LPWSTR HintName;
} HintStuff[] = {
    { FLAG_CSC_HINT_PIN_USER, L"PIN_USER " },
    { FLAG_CSC_HINT_PIN_INHERIT_USER, L"PIN_INHERIT_USER " },
    { FLAG_CSC_HINT_PIN_INHERIT_SYSTEM, L"PIN_INHERIT_SYSTEM " },
    { FLAG_CSC_HINT_CONSERVE_BANDWIDTH, L"CONSERVE_BANDWIDTH " },
    { FLAG_CSC_HINT_PIN_SYSTEM, L"PIN_SYSTEM " },
    { 0, NULL }
};

DWORD
HintsToEnglish(
    DWORD Hint,
    LPWSTR OutputBuffer)
{
    ULONG i;

    OutputBuffer[0] = L'\0';
    for (i = 0; HintStuff[i].HintName; i++) {
        if (Hint & HintStuff[i].HintFlag)
            wcscat(OutputBuffer, HintStuff[i].HintName);
    }
    return 0;
}

BOOLEAN
LooksToBeAShare(LPWSTR Name)
{
    //
    // See if we have \\server\share or \\server\share\<something>
    // Assume a valid name passed in...
    //

    ULONG Len = wcslen(Name);
    ULONG i;
    ULONG SlashCount = 0;

    // Remove any trailing '\'s
    while (Len >= 1) {
        if (Name[Len-1] == L'\\') {
            Name[Len-1] = L'\0';
            Len--;
        } else {
            break;
        }
    }
    for (i = 0; Name[i]; i++) {
        if (Name[i] == L'\\')
            SlashCount++;
    }
    if (SlashCount > 3)
        return FALSE;

    return TRUE;
}

DWORD
CmdDisconnect(
    PWSTR DisconnectArg)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;
    ULONG hShare;
    PWCHAR wCp;
    ULONG SlashCount = 0;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDisconnect(%ws)\r\n", DisconnectArg);

    GetFileAttributes(DisconnectArg);

    Status = GetLastError();
    if (Status != NO_ERROR)
        goto AllDone;

    SlashCount = 0;
    for (wCp = DisconnectArg; *wCp; wCp++) {
        if (*wCp == L'\\')
            SlashCount++;
        if (SlashCount == 3) {
            *wCp = L'\0';
            break;
        }
    }

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    FILE_EXECUTE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdDisconnect:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.cbBufferSize = (wcslen(DisconnectArg)+1) * sizeof(WCHAR);
    sSI.lpBuffer = DisconnectArg;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_TAKE_SERVER_OFFLINE,      // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdDisconnect:DeviceIoControl IOCTL_TAKE_SERVER_OFFLINE failed\n");
        Status = GetLastError();
        goto AllDone;
    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDisconnect exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdGetShadow(
    PWSTR GetShadowArg)
{
    DWORD Status = 0;
    DWORD dwErr = ERROR_SUCCESS;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    HSHADOW hShadow = 0;
    HSHADOW hDir = 0;
    WIN32_FIND_DATAW sFind32 = {0};
    SHADOWINFO sSI = {0};
    PWCHAR wCp = NULL;
    BOOL bResult = FALSE;
    DWORD junk;
    WCHAR TimeBuf1[40];
    WCHAR TimeBuf2[40];
    WCHAR TimeBuf3[40];

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetShadow(%ws)\r\n", GetShadowArg);

    if (GetShadowArg == NULL) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    swscanf(GetShadowArg, L"%x:", &hDir);
    for (wCp = GetShadowArg; *wCp && *wCp != L':'; wCp++)
        ;
    if (*wCp != L':') {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }
    wCp++;
    if (wcslen(wCp) >= MAX_PATH) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }
    wcscpy(sFind32.cFileName, wCp);

    if (fSwDebug == TRUE)
        MyPrintf(L"hDir=0x%x cFileName=[%ws]\r\n", hDir, sFind32.cFileName);

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdGetShadow:Failed open of shadow device\r\n");
        dwErr = GetLastError();
        goto AllDone;
    }

    sSI.hDir = hDir;
    sSI.lpFind32 = &sFind32;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_GETSHADOW,                // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdGetShadow:DeviceIoControl IOCTL_GETSHADOW failed\n");
        dwErr = GetLastError();
        goto AllDone;
    }

    ConvertGmtTimeToString(sFind32.ftCreationTime, TimeBuf1);
    ConvertGmtTimeToString(sFind32.ftLastAccessTime, TimeBuf2);
    ConvertGmtTimeToString(sFind32.ftLastWriteTime, TimeBuf3);

    MyPrintf(L"\r\n");
    MyPrintf(L"%ws:\r\n"
             L"   Size=0x%x:0x%x Attr=0x%x\r\n"
             L"   CreationTime:   %ws\r\n"
             L"   LastAccessTime: %ws\r\n"
             L"   LastWriteTime:  %ws\r\n",
                sFind32.cFileName,
                sFind32.nFileSizeHigh,
                sFind32.nFileSizeLow,
                sFind32.dwFileAttributes,
                TimeBuf1,
                TimeBuf2,
                TimeBuf3);
    MyPrintf(L"   hShare=0x%x hDir=0x%x hShadow=0x%x Status=0x%x HintFlags=0x%x\r\n",
                    sSI.hShare,
                    sSI.hDir,
                    sSI.hShadow,
                    sSI.uStatus,
                    sSI.ulHintFlags);
    MyPrintf(L"\r\n");

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetShadowArg exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return dwErr;
}

DWORD
CmdGetShadowInfo(
    PWSTR GetShadowInfoArg)
{
    DWORD Status = 0;
    DWORD dwErr = ERROR_SUCCESS;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    HSHADOW hShadow = 0;
    HSHADOW hDir = 0;
    WIN32_FIND_DATAW sFind32 = {0};
    SHADOWINFO sSI = {0};
    BOOL bResult = FALSE;
    DWORD junk;
    WCHAR TimeBuf1[40];
    WCHAR TimeBuf2[40];
    WCHAR TimeBuf3[40];

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetShadowInfo(%ws)\r\n", GetShadowInfoArg);

    if (GetShadowInfoArg == NULL) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    swscanf(GetShadowInfoArg, L"%x:%x", &hDir, &hShadow);

    if (fSwDebug == TRUE)
        MyPrintf(L"hDir=0x%x hShadow=0x%x\r\n", hDir, hShadow);

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdGetShadowInfo:Failed open of shadow device\r\n");
        dwErr = GetLastError();
        goto AllDone;
    }

    sSI.hDir = hDir;
    sSI.hShadow = hShadow;
    sSI.lpFind32 = &sFind32;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SHADOW_GET_SHADOW_INFO,   // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdGetShadowInfo:DeviceIoControl IOCTL_SHADOW_GET_SHADOW_INFO failed\n");
        dwErr = GetLastError();
        goto AllDone;
    }

    ConvertGmtTimeToString(sFind32.ftCreationTime, TimeBuf1);
    ConvertGmtTimeToString(sFind32.ftLastAccessTime, TimeBuf2);
    ConvertGmtTimeToString(sFind32.ftLastWriteTime, TimeBuf3);

    MyPrintf(L"\r\n");
    MyPrintf(L"%ws:\r\n"
             L"   Size=0x%x:0x%x Attr=0x%x\r\n"
             L"   CreationTime:   %ws\r\n"
             L"   LastAccessTime: %ws\r\n"
             L"   LastWriteTime:  %ws\r\n",
                sFind32.cFileName,
                sFind32.nFileSizeHigh,
                sFind32.nFileSizeLow,
                sFind32.dwFileAttributes,
                TimeBuf1,
                TimeBuf2,
                TimeBuf3);
    MyPrintf(L"\r\n");

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetShadowInfoArg exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return dwErr;
}

DWORD
CmdShareId(
    PWSTR ShareIdArg)
{
    DWORD dwErr = ERROR_SUCCESS;
    ULONG ShareId = 0;
    BOOL fRet;
    WCHAR Buffer[100];
    ULONG BufSize = sizeof(Buffer);
    HMODULE hmodCscDll = LoadLibrary(TEXT("cscdll.dll"));
    CSCSHAREIDTOSHARENAME pCSCShareIdToShareName = NULL;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdShareId(%ws)\r\n", ShareIdArg);

    if (ShareIdArg == NULL) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    swscanf(ShareIdArg, L"%x", &ShareId);

    if (fSwDebug == TRUE)
        MyPrintf(L"ShareId=0x%x\r\n", ShareId);

    if (hmodCscDll == NULL) {
        dwErr = GetLastError();
        goto AllDone;
    }

    pCSCShareIdToShareName = (CSCSHAREIDTOSHARENAME) GetProcAddress(
                                                        hmodCscDll,
                                                        "CSCShareIdToShareName");
    if (pCSCShareIdToShareName == NULL) {
        dwErr = GetLastError();
        goto AllDone;
    }

    fRet = (pCSCShareIdToShareName)(ShareId, (PBYTE)Buffer, &BufSize);
    if (fRet == FALSE) {
        dwErr = GetLastError();
        goto AllDone;
    }

    MyPrintf(L"ShareId 0x%x  = %ws\r\n", ShareId, Buffer);

AllDone:

    if (hmodCscDll != NULL)
        FreeLibrary(hmodCscDll);

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdShareIdToShareName exit %d\r\n", dwErr);

    return dwErr;
}

DWORD
CmdExclusionList(
    PWSTR ExclusionListArg)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;
    ULONG hShare;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdExclusionList(%ws)\r\n", ExclusionListArg);

    if (ExclusionListArg == NULL) {
        ExclusionListArg = vtzDefaultExclusionList;
    }
    MyPrintf(L"Setting exclusion list to \"%ws\"\r\n", ExclusionListArg);

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdExclusionList:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.uSubOperation = SHADOW_SET_EXCLUSION_LIST;
    sSI.lpBuffer = ExclusionListArg;
    sSI.cbBufferSize = (wcslen(ExclusionListArg)+1) * sizeof(WCHAR);

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_DO_SHADOW_MAINTENANCE,    // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdExclusionList exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdBWConservationList(
    PWSTR BWConservationListArg)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;
    ULONG hShare;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdBWConservationList(%ws)\r\n", BWConservationListArg);

    if (BWConservationListArg == NULL) {
        BWConservationListArg = L" ";
    }

    MyPrintf(L"Setting BWConservation list to \"%ws\"\r\n", BWConservationListArg);

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdBWConservationList:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.uSubOperation = SHADOW_SET_BW_CONSERVE_LIST;
    sSI.lpBuffer = BWConservationListArg;
    sSI.cbBufferSize = (wcslen(BWConservationListArg)+1) * sizeof(WCHAR);

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_DO_SHADOW_MAINTENANCE,    // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdBWConservationList exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdDetector(
    VOID)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = ERROR_SUCCESS;
    ULONG hShare;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDetector()\r\n");

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdDetector:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.uSubOperation = SHADOW_SPARSE_STALE_DETECTION_COUNTER;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_DO_SHADOW_MAINTENANCE,    // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (bResult)
        MyPrintf(L"%d\r\n", sSI.dwError);
    else
        Status = sSI.dwError;

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdDetector exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdSwitches(
    VOID)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = ERROR_SUCCESS;
    ULONG hShare;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSwitches()\r\n");

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdSwitches:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.uOp = SHADOW_SWITCH_GET_STATE;

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SWITCHES,                 // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (bResult) {
        MyPrintf(L"0x%08x", sSI.uStatus);
        if (sSI.uStatus != 0) {
            MyPrintf(L" (");
            if (sSI.uStatus & SHADOW_SWITCH_SHADOWING)
                MyPrintf(L"SHADOW_SWITCH_SHADOWING ");
            if (sSI.uStatus & SHADOW_SWITCH_LOGGING)
                MyPrintf(L"SHADOW_SWITCH_LOGGING ");
            if (sSI.uStatus & SHADOW_SWITCH_SPEAD_OPTIMIZE)
                MyPrintf(L"SHADOW_SWITCH_SPEAD_OPTIMIZE ");
            MyPrintf(L")");
        }
        if ((sSI.uStatus & SHADOW_SWITCH_SHADOWING) == 0)
            MyPrintf(L" ... csc is disabled");
        else
            MyPrintf(L" ... csc is enabled");
        MyPrintf(L"\r\n");
    } else {
        Status = sSI.dwError;
    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSwitches exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

DWORD
CmdGetSpace(
    VOID)
{
    DWORD Error = ERROR_SUCCESS;
    WCHAR szVolume[MAX_PATH];
    LARGE_INTEGER MaxSpace;
    LARGE_INTEGER CurrentSpace;
    DWORD TotalFiles;
    DWORD TotalDirs;
    BOOL fRet;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetSpace()\r\n");

    fRet = CSCGetSpaceUsageW(
            szVolume,
            sizeof(szVolume)/sizeof(WCHAR),
            &MaxSpace.HighPart,
            &MaxSpace.LowPart,
            &CurrentSpace.HighPart,
            &CurrentSpace.LowPart,
            &TotalFiles,
            &TotalDirs);

    if (fRet == FALSE) {
        Error = GetLastError();
        goto AllDone;
    }

    MyPrintf(
        L"Volume:         %ws\r\n"
        L"MaxSpace:       0x%x:0x%x (%d)\r\n"
        L"CurrentSpace:   0x%x:0x%x (%d)\r\n"
        L"TotalFiles:     %d\r\n"
        L"TotalDirs:      %d\r\n",
            szVolume,
            MaxSpace.HighPart,
            MaxSpace.LowPart,
            MaxSpace.LowPart,
            CurrentSpace.HighPart,
            CurrentSpace.LowPart,
            CurrentSpace.LowPart,
            TotalFiles,
            TotalDirs);

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdGetSpace exit %d\r\n", Error);

    return Error;
}

DWORD
CmdIsServerOffline(
    PWSTR IsServerOfflineArg)
{
    DWORD Error = ERROR_SUCCESS;
    BOOL fRet;
    BOOL fOffline = FALSE;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdIsServerOffline(%ws)\r\n", IsServerOfflineArg);

    fRet = CSCIsServerOfflineW(
            IsServerOfflineArg,
            &fOffline);

    if (fRet == FALSE) {
        Error = GetLastError();
        goto AllDone;
    }

    if (fOffline == TRUE)
        MyPrintf(L"Offline\r\n");
    else
        MyPrintf(L"Online\r\n");

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdIsServerOffline exit %d\r\n", Error);

    return Error;
}

DWORD
CmdSetShareStatus(
    PWSTR SetShareStatusArg)
{
    HSHADOW hDir;
    SHADOWINFO sSI;
    BOOL bResult;
    HANDLE hDBShadow = INVALID_HANDLE_VALUE;
    ULONG junk;
    ULONG Status = 0;
    ULONG StatusToSet = 0;
    ULONG hShare = 0;

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSetShareStatus(%ws)\r\n", SetShareStatusArg);

    if (fSwSet == TRUE && fSwClear == TRUE) {
        MyPrintf(L"Can't have both SET and CLEAR\r\n");
        Status = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    hDBShadow = CreateFile(
                    L"\\\\.\\shadow",
                    GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL);

    if (hDBShadow == INVALID_HANDLE_VALUE) {
        MyPrintf(L"CmdSetShareStatus:Failed open of shadow device\r\n");
        Status = GetLastError();
        goto AllDone;
    }

    swscanf(SetShareStatusArg, L"%x:%x", &hShare, &StatusToSet);

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSetShareStatus hShare=0x%x StatusToSet=0x%x\r\n", hShare, StatusToSet);

    memset(&sSI, 0, sizeof(SHADOWINFO));
    sSI.hShare = hShare;
    if (fSwSet == TRUE) {
        sSI.uStatus = StatusToSet;
        sSI.uOp = SHADOW_FLAGS_OR;
    } else if (fSwClear == TRUE) {
        sSI.uStatus = ~StatusToSet;
        sSI.uOp = SHADOW_FLAGS_AND;
    } else {
        MyPrintf(L"Missing /SET or /CLEAR\r\n");
        Status = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    bResult = DeviceIoControl(
                hDBShadow,                      // device 
                IOCTL_SET_SHARE_STATUS,         // control code
                (LPVOID)&sSI,                   // in buffer
                0,                              // inbuffer size
                NULL,                           // out buffer
                0,                              // out buffer size
                &junk,                          // bytes returned
                NULL);                          // overlapped

    if (!bResult) {
        MyPrintf(L"CmdSetShareStatus:DeviceIoControl IOCTL_SET_SHARE_STATUS failed\n");
        Status = GetLastError();
        goto AllDone;
    }

AllDone:

    if (fSwDebug == TRUE)
        MyPrintf(L"CmdSetShareStatus exit %d\r\n", Status);

    if (hDBShadow != INVALID_HANDLE_VALUE)
        CloseHandle(hDBShadow);

    return Status;
}

#define MAX_OFFSETS 256
#define PAGESIZE 4096
ULONG OffsetArgs[MAX_OFFSETS];

DWORD
CmdRandW(
    PWSTR CmdRandWArg)
{

    DWORD dwError = ERROR_SUCCESS;
    DWORD dwFileSize;
    DWORD dwOffset;
    DWORD dwOffsetHigh;
    UCHAR uchData;
    
    HANDLE  hFile = INVALID_HANDLE_VALUE;
    ULONG i;
    LONG WriteCount = 0;
    LONG PageCount = 0;
    PBYTE wArray = NULL;

    if (fSwDebug == TRUE) {
        MyPrintf(L"CmdRandW(%ws)\r\n", CmdRandWArg);
        if (fArgOffset == TRUE)
            MyPrintf(L"  OFFSET [%ws]\r\n", pwszOffsetArg);
    }

    srand( (unsigned)time( NULL ) );

    hFile = CreateFileW(
                    CmdRandWArg,                         // name
                    GENERIC_READ|GENERIC_WRITE,         // access mode
                    FILE_SHARE_READ|FILE_SHARE_WRITE,   // share mode
                    NULL,                               // security descriptor
                    OPEN_EXISTING,                      // create disposition
                    0,                                  // file statributes if created
                    NULL);                              // template handle
    if (hFile == INVALID_HANDLE_VALUE) {
        dwError = GetLastError();
        goto AllDone;
    }
    dwFileSize = GetFileSize(hFile, NULL);
    MyPrintf(L"File size = %d bytes\r\n", dwFileSize);

    if (fArgOffset == TRUE) {
        WriteCount = CountOffsetArgs(pwszOffsetArg, OffsetArgs);
        if (WriteCount == 0) {
            MyPrintf(L"No offsets specified, or  - nothing to do.\r\n");
            goto AllDone;
        } else if (WriteCount < 0) {
            MyPrintf(L"Error in offset list.  Exiting.\r\n");
            dwError = ERROR_INVALID_PARAMETER;
            goto AllDone;
        }
    } else {
        PageCount =  (dwFileSize / PAGESIZE);
        WriteCount = rand() % PageCount;
    }
    if (fSwDebug == TRUE) {
        wArray = calloc(1, PageCount * sizeof(BYTE));
        MyPrintf(L"There are %d pages in the file\r\n", PageCount);
    }
            
    if (dwFileSize == -1) {
        dwError = GetLastError();
        if (fSwDebug == TRUE)
            MyPrintf(L"GetFileSize() failed %d\r\n", dwError);
        goto AllDone;
    }
    if (dwFileSize == 0) {
        MyPrintf(L"0 sized file - nothing to do.\r\n");
        goto AllDone;
    }
    MyPrintf(L"Writing %d times\r\n", WriteCount);
    for (i = 0; i < (ULONG)WriteCount; ++i) {
        DWORD   dwReturn;
        
        if (fArgOffset == TRUE)
            dwOffset = OffsetArgs[i];
        else
            dwOffset = ((rand() % PageCount) * PAGESIZE) + (rand() % PAGESIZE);
        uchData = (UCHAR)rand();
        if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
            dwError = GetLastError();
            if (fSwDebug == TRUE)
                MyPrintf(L"SetFilePointer() failed %d\r\n", dwError);
            goto AllDone;
        }
        MyPrintf(L"writing 0x02%x Page %d offset %d (offset %d(0x%x))\r\n", uchData,
                        dwOffset / PAGESIZE,
                        dwOffset % PAGESIZE,
                        dwOffset,
                        dwOffset);
        if (wArray)
            wArray[dwOffset/PAGESIZE]++;
        if (!WriteFile(hFile, &uchData, 1, &dwReturn, NULL)) {
            dwError = GetLastError();
            if (fSwDebug == TRUE)
                MyPrintf(L"WriteFile() failed %d\r\n", dwError);
            goto AllDone;
        }
    }

    if (wArray) {
        for (i = 0; i < (ULONG)PageCount; i++) {
            MyPrintf(L"%d", wArray[i]);
            if ((i % 50) == 0)
                MyPrintf(L"\r\n");
        }
        MyPrintf(L"\r\n");
    }

    // If EOF is specified, truncate the file to a random length
    if (fSwEof == TRUE) {
        ULONG xx = rand() % 5;
        ULONG NewLen = (rand() * rand()) % (dwFileSize * 2);
        if (xx == 0 || xx == 1) {
            MyPrintf(L"New EOF = %d\r\n", NewLen);
            SetFilePointer(hFile, NewLen, 0, FILE_BEGIN);
            SetEndOfFile(hFile);
        } else {
            MyPrintf(L"No EOF change.\r\n");
        }
    }

AllDone:
    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);
    if (wArray)
        free(wArray);
    if (fSwDebug == TRUE)
        MyPrintf(L"CmdRandW exit %d\r\n", dwError);
    return dwError;
}




LONG
CountOffsetArgs(
    PWSTR OffsetArg,
    ULONG OffsetArray[])
{
    // Expect a string of the form "N,N,N,N" where each N can be hex OR decimal.
    // Store results in OffsetArray[]
    // Limit is MAX_OFFSETS offsets, to make things easy.

    ULONG i;
    PWCHAR wCp = OffsetArg;
    PWCHAR wNum = NULL;
    ULONG iRet;

    for (i = 0; i < MAX_OFFSETS; i++) {
        if (*wCp == L'\0')
            break;
        wNum = wCp;
        while (*wCp != L',' && *wCp != L'\0')
            wCp++;
        if (*wCp == L',')
            *wCp++ = L'\0';
        iRet = swscanf(wNum, L"%Li", &OffsetArray[i]);
        if (iRet <= 0)
            return -1;
    }
    if (fSwDebug == TRUE) {
        ULONG j;
        for (j = 0; j < i; j++)
            MyPrintf(L"[%d]-0x%x(%d)\r\n", j, OffsetArray[j], OffsetArray[j]);
        MyPrintf(L"CountOffsetArgs returning %d\r\n", i);
    }
    return i;
}

#if defined(CSCUTIL_INTERNAL)
DWORD
CmdBitcopy(
    PWSTR BitcopyArg)
{
	DWORD Error = ERROR_FILE_NOT_FOUND;
	LPWSTR  lpszTempName = NULL;

    if (!CSCCopyReplicaW(BitcopyArg, &lpszTempName)) {
        Error = GetLastError();
    } else {
        Error = ERROR_SUCCESS;
    }

	if (Error == ERROR_SUCCESS) {
        Error = DumpBitMap(lpszTempName);
        DeleteFile(lpszTempName);
    }

	return Error;
}
#endif // CSCUTIL_INTERNAL