|
|
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: twin.c
//
// This file contains special twin handling functions.
//
// (Even though we've moved to a briefcase metaphor,
// we still refer to twins internally...)
//
// History:
// 08-06-93 ScottH Transferred from twin code
//
//---------------------------------------------------------------------------
#include "brfprv.h" // common headers
#include "res.h"
#include "recact.h"
// APPCOMPAT: due to a compiler bug, we need to declare this structure
// as a 1-element array because it has pointers to functions in it
// and it is in another datasegment.
VTBLENGINE g_vtblEngine[1] = { { 0 } }; // per-instance v-table
#define GetFunction(rgtable, name, type) \
((rgtable).##name = (type)GetProcAddress((rgtable).hinst, #name)); \ ASSERT((rgtable).##name)
#ifdef DEBUG
#define SzTR(tr) #tr,
#endif
#define MAX_RANGE 0x7fff
// Recitem dwUser values
#define RIU_CHANGED 1
#define RIU_SKIP 2
#define RIU_SHOWSTATUS 3
/*----------------------------------------------------------
Purpose: Compare two structures by folder name Returns: -1 if <, 0 if ==, 1 if > Cond: -- */ int CALLBACK _export NCompareFolders( LPVOID lpv1, LPVOID lpv2, LPARAM lParam) // One of: CMP_RECNODES, CMP_FOLDERTWINS
{ switch (lParam) { case CMP_RECNODES: return lstrcmpi(((PRECNODE)lpv1)->pcszFolder, ((PRECNODE)lpv2)->pcszFolder); case CMP_FOLDERTWINS: return lstrcmpi(((PCFOLDERTWIN)lpv1)->pcszOtherFolder, ((PCFOLDERTWIN)lpv2)->pcszOtherFolder); default: ASSERT(0); // should never get here
} return 0; }
//---------------------------------------------------------------------------
// Choose side functions
//---------------------------------------------------------------------------
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Dump a CHOOSESIDE structure Returns: -- Cond: -- */ void PRIVATE ChooseSide_Dump( PCHOOSESIDE pchside) { BOOL bDump; TCHAR szBuf[MAXMSGLEN]; ASSERT(pchside); #define szDumpLabel TEXT(" *** ")
#define szDumpMargin TEXT(" ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_CHOOSESIDE); } LEAVEEXCLUSIVE(); if (bDump) { wsprintf(szBuf, TEXT("%s.pszFolder = {%s}\r\n"), (LPTSTR)szDumpLabel, pchside->pszFolder); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.dwFlags = 0x%lx\r\n"), (LPTSTR)szDumpMargin, pchside->dwFlags); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.nRank = %ld\r\n"), (LPTSTR)szDumpMargin, pchside->nRank); OutputDebugString(szBuf); } #undef szDumpLabel
#undef szDumpMargin
}
/*----------------------------------------------------------
Purpose: Dump a CHOOSESIDE list Returns: -- Cond: -- */ void PUBLIC ChooseSide_DumpList( HDSA hdsa) { BOOL bDump; TCHAR szBuf[MAXMSGLEN]; ASSERT(hdsa); #define szDumpLabel TEXT("Dump CHOOSESIDE list: ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_CHOOSESIDE); } LEAVEEXCLUSIVE(); if (bDump) { int i; int cel = DSA_GetItemCount(hdsa); PCHOOSESIDE pchside; wsprintf(szBuf, TEXT("%s.count = %lu\r\n"), (LPTSTR)szDumpLabel, cel); OutputDebugString(szBuf); if (NULL != (pchside = DSA_GetItemPtr(hdsa, 0))) { if (IsFlagSet(pchside->dwFlags, CSF_INSIDE)) OutputDebugString(TEXT("Rank for inside\r\n")); else OutputDebugString(TEXT("Rank for outside\r\n")); } for (i = 0; i < cel; i++) { pchside = DSA_GetItemPtr(hdsa, i); ChooseSide_Dump(pchside); } } #undef szDumpLabel
}
#endif
/*----------------------------------------------------------
Purpose: Initialize an array of CHOOSESIDE elements from a recitem list. Array is unsorted.
Returns: -- Cond: The contents of the array are safe as long as the recitem list lives. */ void PUBLIC ChooseSide_InitAsFile( HDSA hdsa, PRECITEM pri) { CHOOSESIDE chside; PRECNODE prn; ASSERT(hdsa); ASSERT(pri); DSA_DeleteAllItems(hdsa); // All entries start with these values
chside.dwFlags = 0; chside.nRank = 0; // Add each recnode
for (prn = pri->prnFirst; prn; prn = prn->prnNext) { chside.htwin = (HTWIN)prn->hObjectTwin; chside.hvid = prn->hvid; chside.pszFolder = prn->pcszFolder; chside.prn = prn; DSA_InsertItem(hdsa, 0x7fff, &chside); } }
/*----------------------------------------------------------
Purpose: Create an array of CHOOSESIDE elements from a recitem list. Array is unsorted.
Returns: standard result Cond: The contents of the array are safe as long as the recitem list lives. */ HRESULT PUBLIC ChooseSide_CreateAsFile( HDSA * phdsa, PRECITEM pri) { HRESULT hres; HDSA hdsa; ASSERT(phdsa); ASSERT(pri); hdsa = DSA_Create(sizeof(CHOOSESIDE), (int)pri->ulcNodes); if (hdsa) { ChooseSide_InitAsFile(hdsa, pri); hres = NOERROR; } else hres = E_OUTOFMEMORY; *phdsa = hdsa; return hres; }
/*----------------------------------------------------------
Purpose: Create an empty array of CHOOSESIDE elements.
Returns: standard result Cond: -- */ HRESULT PUBLIC ChooseSide_CreateEmpty( HDSA * phdsa) { HRESULT hres; HDSA hdsa; ASSERT(phdsa); hdsa = DSA_Create(sizeof(CHOOSESIDE), 4); if (hdsa) { hres = NOERROR; } else hres = E_OUTOFMEMORY; *phdsa = hdsa; return hres; }
/*----------------------------------------------------------
Purpose: Create an array of CHOOSESIDE elements from a foldertwin list. Array is unsorted.
Returns: standard result Cond: The contents of the array are safe as long as the foldertwin list lives. */ HRESULT PUBLIC ChooseSide_CreateAsFolder( HDSA * phdsa, PFOLDERTWINLIST pftl) { HRESULT hres; HDSA hdsa; CHOOSESIDE chside; ASSERT(pftl); hdsa = DSA_Create(sizeof(chside), (int)pftl->ulcItems); if (hdsa) { PCFOLDERTWIN pft; LPCTSTR pszFolderLast = NULL; // All entries start with these values
chside.dwFlags = CSF_FOLDER; chside.nRank = 0; chside.prn = NULL; // Special case the source folder
chside.htwin = (HTWIN)pftl->pcftFirst->hftSrc; chside.hvid = pftl->pcftFirst->hvidSrc; chside.pszFolder = pftl->pcftFirst->pcszSrcFolder; // (Don't care if this fails)
DSA_InsertItem(hdsa, 0x7fff, &chside); // Add the other folders (duplicates skipped)
for (pft = pftl->pcftFirst; pft; pft = pft->pcftNext) { // Duplicate?
if (pszFolderLast && IsSzEqual(pszFolderLast, pft->pcszOtherFolder)) continue; // Yes (hack: the engine gives us a sorted list)
chside.htwin = (HTWIN)pft->hftOther; chside.hvid = pft->hvidOther; chside.pszFolder = pft->pcszOtherFolder; DSA_InsertItem(hdsa, 0x7fff, &chside); pszFolderLast = pft->pcszOtherFolder; } *phdsa = hdsa; hres = NOERROR; } else hres = E_OUTOFMEMORY; return hres; }
/*----------------------------------------------------------
Purpose: Reset the ranks
Returns: -- Cond: -- */ void PRIVATE ChooseSide_ResetRanks( HDSA hdsa) { int i; int cel; ASSERT(hdsa); cel = DSA_GetItemCount(hdsa); for (i = 0; i < cel; i++) { PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i); pchside->nRank = 0; } }
/*----------------------------------------------------------
Purpose: Determine ranks based on whether each element in the array is inside the briefcase.
Returns: -- Cond: -- */ void PRIVATE ChooseSide_RankForInside( HDSA hdsa, LPCTSTR pszBrfPath, // Root path of briefcase
LPCTSTR pszFolder) // If NULL, choose best outside element
{ int i; int cel; int cchLast = 0; PCHOOSESIDE pchsideLast; ASSERT(hdsa); ASSERT(pszBrfPath); ASSERT(pszFolder); ASSERT(PathIsPrefix(pszBrfPath, pszFolder)); cel = DSA_GetItemCount(hdsa); for (i = 0; i < cel; i++) { PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i); DEBUG_CODE( SetFlag(pchside->dwFlags, CSF_INSIDE); ) // Is this item inside this briefcase?
if (PathIsPrefix(pszBrfPath, pchside->pszFolder)) pchside->nRank++; // Yes
// Is this item inside this folder?
if (PathIsPrefix(pszFolder, pchside->pszFolder)) { int cch = lstrlen(pchside->pszFolder); pchside->nRank++; // Yes; even better
if (0 == cchLast) { cchLast = cch; pchsideLast = pchside; } else { // Is this path deeper than the last prefix-matching path?
// (the path closer to the top is better)
if (cch > cchLast) { // Yes; demote this one
pchside->nRank--; } else { // No; demote previous one
ASSERT(pchsideLast); pchsideLast->nRank--; cchLast = cch; pchsideLast = pchside; } } } } }
/*----------------------------------------------------------
Purpose: Determine ranks based on whether each element in the array is outside the briefcase.
Returns: -- Cond: -- */ void PRIVATE ChooseSide_RankForOutside( HDSA hdsa, LPCTSTR pszBrfPath) // Root path of briefcase
{ int i; int cel; ASSERT(hdsa); ASSERT(pszBrfPath); cel = DSA_GetItemCount(hdsa); for (i = 0; i < cel; i++) { PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i); DEBUG_CODE( ClearFlag(pchside->dwFlags, CSF_INSIDE); ) // Is this item NOT in this briefcase?
if ( !PathIsPrefix(pszBrfPath, pchside->pszFolder) ) { // Yes
int nDriveType = DRIVE_UNKNOWN; int ndrive = PathGetDriveNumber(pchside->pszFolder); if (-1 != ndrive) { nDriveType = DriveType(ndrive); } pchside->nRank += 2; if (IsFlagClear(pchside->dwFlags, CSF_FOLDER)) { // Is the file unavailable?
if (RNS_UNAVAILABLE == pchside->prn->rnstate || FS_COND_UNAVAILABLE == pchside->prn->fsCurrent.fscond) { // Yes; demote
pchside->nRank--; } } else { // Is the folder unavailable?
FOLDERTWINSTATUS uStatus; Sync_GetFolderTwinStatus((HFOLDERTWIN)pchside->htwin, NULL, 0, &uStatus); if (FTS_UNAVAILABLE == uStatus) { // Yes; demote
pchside->nRank--; } } // Rank on locality of disk (the closer the better)
if (DRIVE_REMOVABLE == nDriveType || DRIVE_CDROM == nDriveType) ; // Floppy/removable (do nothing)
else if (PathIsUNC(pchside->pszFolder) || IsNetDrive(ndrive)) pchside->nRank++; // Net
else pchside->nRank += 2; // Fixed disk
} } }
/*----------------------------------------------------------
Purpose: Choose the element with the highest rank.
Returns: TRUE if any element distinguished itself Cond: -- */ BOOL PRIVATE ChooseSide_GetBestRank( HDSA hdsa, PCHOOSESIDE * ppchside) { BOOL bRet; int i; int cel; int nRankCur = 0; // (start at 0 since 0 is not good enough to pass muster)
DEBUG_CODE( BOOL bDbgDup = FALSE; ) ASSERT(hdsa); ASSERT(ppchside); *ppchside = NULL; cel = DSA_GetItemCount(hdsa); for (i = 0; i < cel; i++) { PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i); #ifdef DEBUG
if (0 < nRankCur && nRankCur == pchside->nRank) bDbgDup = TRUE; #endif
if (nRankCur < pchside->nRank) { *ppchside = pchside; nRankCur = pchside->nRank; DEBUG_CODE( bDbgDup = FALSE; ) // Reset
} } #ifdef DEBUG
// We shouldn't get duplicate highest ranks
if (bDbgDup) { // Dump the chooseside list if there are duplicate highest ranks
ChooseSide_DumpList(hdsa); } ASSERT(FALSE == bDbgDup); #endif
bRet = 0 < nRankCur; ASSERT(bRet && *ppchside || !bRet && !*ppchside); return bRet; }
/*----------------------------------------------------------
Purpose: Get the best candidate element (inside or outside). If the pszFolder is NULL, this function gets the best outside path.
Returns: TRUE if an element was found Cond: -- */ BOOL PUBLIC ChooseSide_GetBest( HDSA hdsa, LPCTSTR pszBrfPath, // Root path of briefcase
LPCTSTR pszFolder, // If NULL, choose best outside element
PCHOOSESIDE * ppchside) { ASSERT(hdsa); ASSERT(0 < DSA_GetItemCount(hdsa)); ASSERT(pszBrfPath); ASSERT(ppchside); ChooseSide_ResetRanks(hdsa); // Are we ranking for inside paths?
if (pszFolder) { // Yes; inside wins
ChooseSide_RankForInside(hdsa, pszBrfPath, pszFolder); } else { // No; outside wins
ChooseSide_RankForOutside(hdsa, pszBrfPath); } return ChooseSide_GetBestRank(hdsa, ppchside); }
/*----------------------------------------------------------
Purpose: Get the next best candidate element (inside or outside). ChooseSide_GetBest must have been previously called.
Returns: TRUE if an element was found Cond: -- */ BOOL PUBLIC ChooseSide_GetNextBest( HDSA hdsa, PCHOOSESIDE * ppchside) { PCHOOSESIDE pchside; ASSERT(hdsa); ASSERT(0 < DSA_GetItemCount(hdsa)); ASSERT(ppchside); // Get the best rank and reset it
ChooseSide_GetBestRank(hdsa, &pchside); pchside->nRank = 0; // Now get the next best rank
return ChooseSide_GetBestRank(hdsa, ppchside); }
/*----------------------------------------------------------
Purpose: Frees an array of CHOOSESIDE elements.
Returns: -- Cond: -- */ void PUBLIC ChooseSide_Free( HDSA hdsa) { if (hdsa) { DSA_Destroy(hdsa); } }
//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: Determine which node is inside and outside a briefcase.
This function takes a list of recnodes and determines which node is "inside" a briefcase and which one is "outside" a briefcase. "Inside" means the file exists somewhere underneath the briefcase path indicated by atomBrf. "Outside" is anywhere else (but may be in a different briefcase as well). Returns: -- Cond: -- */ HRESULT PUBLIC Sync_GetNodePair( PRECITEM pri, LPCTSTR pszBrfPath, LPCTSTR pszInsideDir, // Which folder inside the briefcase to consider
PRECNODE * pprnInside, PRECNODE * pprnOutside) { HRESULT hres; HDSA hdsa; ASSERT(pri); ASSERT(pszBrfPath); ASSERT(pszInsideDir); ASSERT(pprnInside); ASSERT(pprnOutside); ASSERT(PathIsPrefix(pszBrfPath, pszInsideDir)); hres = ChooseSide_CreateAsFile(&hdsa, pri); if (SUCCEEDED(hres)) { PCHOOSESIDE pchside; // Get inside folder
if (ChooseSide_GetBest(hdsa, pszBrfPath, pszInsideDir, &pchside)) { *pprnInside = pchside->prn; } else { ASSERT(0); *pprnInside = NULL; hres = E_FAIL; } // Get outside folder
if (ChooseSide_GetBest(hdsa, pszBrfPath, NULL, &pchside)) { *pprnOutside = pchside->prn; } else { ASSERT(0); *pprnOutside = NULL; hres = E_FAIL; } #ifdef DEBUG
if (SUCCEEDED(hres) && IsFlagSet(g_uDumpFlags, DF_PATHS)) { TRACE_MSG(TF_ALWAYS, TEXT("Choosing pairs: %s and %s"), (*pprnInside)->pcszFolder, (*pprnOutside)->pcszFolder); } #endif
ChooseSide_Free(hdsa); } else { *pprnInside = NULL; *pprnOutside = NULL; } return hres; }
/*----------------------------------------------------------
Purpose: Checks if we've loaded the sync engine Returns: TRUE if loaded Cond: -- */ BOOL PUBLIC Sync_IsEngineLoaded() { BOOL bRet; ENTEREXCLUSIVE(); { bRet = g_vtblEngine[0].hinst != NULL; } LEAVEEXCLUSIVE(); return bRet; }
/*----------------------------------------------------------
Purpose: Load the SYNCENG.DLL and initialize the v-table. Returns: TRUE on success Cond: -- */ BOOL PUBLIC Sync_QueryVTable(void) { BOOL bRet = TRUE; HINSTANCE hinst; ENTEREXCLUSIVE(); { hinst = g_vtblEngine[0].hinst; } LEAVEEXCLUSIVE(); // We want to assure that the engine is loaded the same
// number of times that SYNCUI is (by a process). This prevents
// Kernel from nuking the engine prematurely (if a
// process is terminated).
//
// We go thru these hoops simply because SYNCUI does not
// load SYNCENG immediately upon PROCESS_ATTACH. We wait
// until we *really* need to load it the first time.
// Once we finally do load it, we need to keep the load
// count current.
//
// Kernel frees SYNCUI and SYNCENG for us.
//
if (NULL == hinst) { VTBLENGINE vtbl; DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Loading %s (cProcess = %d)"), (LPCTSTR)c_szEngineDLL, g_cProcesses); ) ZeroInit(&vtbl, sizeof(VTBLENGINE)); // Don't be in the critical section when we load the DLL
// or call GetProcAddress, since our LibMain can block on
// this critical section.
ASSERT_NOT_EXCLUSIVE(); hinst = LoadLibrary(c_szEngineDLL); if ( ISVALIDHINSTANCE(hinst) ) { // We are loading for the first time. Fill the vtable.
//
vtbl.hinst = hinst; // Get all the function addresses
//
GetFunction(vtbl, OpenBriefcase, OPENBRIEFCASEINDIRECT); GetFunction(vtbl, SaveBriefcase, SAVEBRIEFCASEINDIRECT); GetFunction(vtbl, CloseBriefcase, CLOSEBRIEFCASEINDIRECT); GetFunction(vtbl, ClearBriefcaseCache, CLEARBRIEFCASECACHEINDIRECT); GetFunction(vtbl, DeleteBriefcase, DELETEBRIEFCASEINDIRECT); GetFunction(vtbl, GetOpenBriefcaseInfo, GETOPENBRIEFCASEINFOINDIRECT); GetFunction(vtbl, FindFirstBriefcase, FINDFIRSTBRIEFCASEINDIRECT); GetFunction(vtbl, FindNextBriefcase, FINDNEXTBRIEFCASEINDIRECT); GetFunction(vtbl, FindBriefcaseClose, FINDBRIEFCASECLOSEINDIRECT); GetFunction(vtbl, AddObjectTwin, ADDOBJECTTWININDIRECT); GetFunction(vtbl, AddFolderTwin, ADDFOLDERTWININDIRECT); GetFunction(vtbl, ReleaseTwinHandle, RELEASETWINHANDLEINDIRECT); GetFunction(vtbl, DeleteTwin, DELETETWININDIRECT); GetFunction(vtbl, GetObjectTwinHandle, GETOBJECTTWINHANDLEINDIRECT); GetFunction(vtbl, IsFolderTwin, ISFOLDERTWININDIRECT); GetFunction(vtbl, CreateFolderTwinList, CREATEFOLDERTWINLISTINDIRECT); GetFunction(vtbl, DestroyFolderTwinList, DESTROYFOLDERTWINLISTINDIRECT); GetFunction(vtbl, GetFolderTwinStatus, GETFOLDERTWINSTATUSINDIRECT); GetFunction(vtbl, IsOrphanObjectTwin, ISORPHANOBJECTTWININDIRECT); GetFunction(vtbl, CountSourceFolderTwins, COUNTSOURCEFOLDERTWINSINDIRECT); GetFunction(vtbl, AnyTwins, ANYTWINSINDIRECT); GetFunction(vtbl, CreateTwinList, CREATETWINLISTINDIRECT); GetFunction(vtbl, DestroyTwinList, DESTROYTWINLISTINDIRECT); GetFunction(vtbl, AddTwinToTwinList, ADDTWINTOTWINLISTINDIRECT); GetFunction(vtbl, AddAllTwinsToTwinList, ADDALLTWINSTOTWINLISTINDIRECT); GetFunction(vtbl, RemoveTwinFromTwinList, REMOVETWINFROMTWINLISTINDIRECT); GetFunction(vtbl, RemoveAllTwinsFromTwinList, REMOVEALLTWINSFROMTWINLISTINDIRECT); GetFunction(vtbl, CreateRecList, CREATERECLISTINDIRECT); GetFunction(vtbl, DestroyRecList, DESTROYRECLISTINDIRECT); GetFunction(vtbl, ReconcileItem, RECONCILEITEMINDIRECT); GetFunction(vtbl, BeginReconciliation, BEGINRECONCILIATIONINDIRECT); GetFunction(vtbl, EndReconciliation, ENDRECONCILIATIONINDIRECT); GetFunction(vtbl, IsPathOnVolume, ISPATHONVOLUMEINDIRECT); GetFunction(vtbl, GetVolumeDescription, GETVOLUMEDESCRIPTIONINDIRECT); } else { bRet = FALSE; } ENTEREXCLUSIVE(); { g_vtblEngine[0] = vtbl; } LEAVEEXCLUSIVE(); } return bRet; }
/*----------------------------------------------------------
Purpose: Free the engine DLL if it is loaded Returns: -- Cond: -- */ void PUBLIC Sync_ReleaseVTable() { HINSTANCE hinst; ENTEREXCLUSIVE(); { hinst = g_vtblEngine[0].hinst; } LEAVEEXCLUSIVE(); if (NULL != hinst) { DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Freeing %s (cProcess = %d)"), (LPCTSTR)c_szEngineDLL, g_cProcesses); ) // We must call FreeLibrary() on the sync engine even during a
// PROCESS_DETACH. We may be getting detached from a process even
// though the process isn't being terminated. If we don't unload
// the sync engine now, it won't be unloaded until the process is
// terminated.
//
FreeLibrary(hinst); ENTEREXCLUSIVE(); { ZeroInit(&g_vtblEngine[0], sizeof(VTBLENGINE)); } LEAVEEXCLUSIVE(); } #ifdef DEBUG
ENTEREXCLUSIVE(); { ASSERT(g_vtblEngine[0].hinst == NULL); } LEAVEEXCLUSIVE(); #endif
}
/*----------------------------------------------------------
Purpose: Set the last sync error. Returns: same twinresult Cond: -- */ TWINRESULT PUBLIC Sync_SetLastError( TWINRESULT tr) { ENTEREXCLUSIVE(); { ASSERTEXCLUSIVE(); MySetTwinResult(tr); } LEAVEEXCLUSIVE(); return tr; }
/*----------------------------------------------------------
Purpose: Get the last sync error. Returns: twinresult Cond: -- */ TWINRESULT PUBLIC Sync_GetLastError() { TWINRESULT tr; ENTEREXCLUSIVE(); { ASSERTEXCLUSIVE(); tr = MyGetTwinResult(); } LEAVEEXCLUSIVE(); return tr; }
/*----------------------------------------------------------
Purpose: Returns the number of recitems that would require some reconciliation.
Returns: see above Cond: -- */ ULONG PUBLIC CountActionItems( PRECLIST prl) { PRECITEM pri; ULONG ulc; for (pri = prl->priFirst, ulc = 0; pri; pri = pri->priNext) { if (IsFileRecItem(pri) && RIU_SKIP != pri->dwUser && RIA_NOTHING != pri->riaction && RIA_BROKEN_MERGE != pri->riaction) { ulc++; pri->dwUser = RIU_SHOWSTATUS; } } return ulc; }
/*----------------------------------------------------------
Purpose: Displays appropriate error message on update errors Returns: -- Cond: -- */ void PRIVATE HandleUpdateErrors( HWND hwndOwner, HRESULT hres, UINT uFlags) // RF_*
{ // Is this an update while adding files?
if (IsFlagSet(uFlags, RF_ONADD)) { // Yes
static SETbl const c_rgseUpdateOnAdd[] = { // The out of memory message should be handled by caller
{ E_TR_DEST_OPEN_FAILED, IDS_ERR_ADD_READONLY, MB_WARNING }, { E_TR_DEST_WRITE_FAILED, IDS_ERR_ADD_FULLDISK, MB_WARNING }, { E_TR_UNAVAILABLE_VOLUME, IDS_ERR_ADD_UNAVAIL_VOL, MB_WARNING }, { E_TR_SRC_OPEN_FAILED, IDS_ERR_ADD_SOURCE_FILE, MB_WARNING }, }; SEMsgBox(hwndOwner, IDS_CAP_ADD, hres, c_rgseUpdateOnAdd, ARRAYSIZE(c_rgseUpdateOnAdd)); } else { // No
static SETbl const c_rgseUpdate[] = { { E_TR_OUT_OF_MEMORY, IDS_OOM_UPDATE, MB_ERROR }, { E_TR_DEST_OPEN_FAILED, IDS_ERR_READONLY, MB_INFO }, { E_TR_DEST_WRITE_FAILED, IDS_ERR_FULLDISK, MB_WARNING }, { E_TR_UNAVAILABLE_VOLUME, IDS_ERR_UPD_UNAVAIL_VOL,MB_WARNING }, { E_TR_FILE_CHANGED, IDS_ERR_FILE_CHANGED, MB_INFO }, { E_TR_SRC_OPEN_FAILED, IDS_ERR_SOURCE_FILE, MB_WARNING }, }; SEMsgBox(hwndOwner, IDS_CAP_UPDATE, hres, c_rgseUpdate, ARRAYSIZE(c_rgseUpdate)); } }
typedef struct tagPROGPARAM { HWND hwndProgress; WORD wPosMax; WORD wPosBase; WORD wPosPrev; BOOL bSkip; } PROGPARAM, * PPROGPARAM;
/*----------------------------------------------------------
Purpose: Status procedure that is called during a single ReconcileItem call.
Returns: varies Cond: -- */ BOOL CALLBACK RecStatusProc( RECSTATUSPROCMSG msg, LPARAM lParam, LPARAM lParamUser) { BOOL bRet; PRECSTATUSUPDATE prsu = (PRECSTATUSUPDATE)lParam; PPROGPARAM pprogparam = (PPROGPARAM)lParamUser; HWND hwndProgress = pprogparam->hwndProgress; WORD wPos; bRet = !UpdBar_QueryAbort(hwndProgress); switch (msg) { case RS_BEGIN_COPY: case RS_DELTA_COPY: case RS_END_COPY: case RS_BEGIN_MERGE: case RS_DELTA_MERGE: case RS_END_MERGE: #ifdef NEW_REC
case RS_BEGIN_DELETE: case RS_DELTA_DELETE: case RS_END_DELETE: #endif
TRACE_MSG(TF_PROGRESS, TEXT("Reconcile progress = %lu of %lu"), prsu->ulProgress, prsu->ulScale); ASSERT(prsu->ulProgress <= prsu->ulScale); if (0 < prsu->ulScale && !pprogparam->bSkip) { wPos = LOWORD(LODWORD( (((__int64)pprogparam->wPosMax * prsu->ulProgress) / prsu->ulScale) )); TRACE_MSG(TF_PROGRESS, TEXT("Max wPos = %u, new wPos = %u, old wPos = %u"), pprogparam->wPosMax, wPos, pprogparam->wPosPrev); if (wPos > pprogparam->wPosPrev && wPos < pprogparam->wPosMax) { WORD wPosReal = pprogparam->wPosBase + wPos; TRACE_MSG(TF_PROGRESS, TEXT("Setting real position = %u"), wPosReal); UpdBar_SetPos(hwndProgress, wPosReal); pprogparam->wPosPrev = wPos; } } break; default: ASSERT(0); break; } return bRet; }
/*----------------------------------------------------------
Purpose: Decides the description string while updating. The string is something like "Copying from 'Foo' to 'Bar'" or "Merging files in 'Foo' and 'Bar'"
Returns: string in pszBuf Cond: -- */ void PRIVATE DecideDescString( LPCTSTR pszBrfPath, PRECITEM pri, LPTSTR pszBuf, int cbBuf, LPTSTR pszPathBuf) { HRESULT hres; RA_ITEM * pitem; ASSERT(pszBrfPath); ASSERT(pri); ASSERT(pszBuf); hres = RAI_CreateFromRecItem(&pitem, pszBrfPath, pri); if (SUCCEEDED(hres)) { UINT ids; LPTSTR pszMsg; LPCTSTR pszFrom; LPCTSTR pszTo; lstrcpy(pszPathBuf, pitem->siInside.pszDir); PathAppend(pszPathBuf, pitem->pszName); switch (pitem->uAction) { case RAIA_TOOUT: ids = IDS_UPDATE_Copy; pszFrom = PathFindFileName(pitem->siInside.pszDir); pszTo = PathFindFileName(pitem->siOutside.pszDir); break; case RAIA_TOIN: ids = IDS_UPDATE_Copy; pszFrom = PathFindFileName(pitem->siOutside.pszDir); pszTo = PathFindFileName(pitem->siInside.pszDir); break; case RAIA_MERGE: ids = IDS_UPDATE_Merge; // (Arbitrary)
pszFrom = PathFindFileName(pitem->siInside.pszDir); pszTo = PathFindFileName(pitem->siOutside.pszDir); break; case RAIA_DELETEOUT: ids = IDS_UPDATE_Delete; pszFrom = PathFindFileName(pitem->siOutside.pszDir); pszTo = NULL; break; case RAIA_DELETEIN: ids = IDS_UPDATE_Delete; pszFrom = PathFindFileName(pitem->siInside.pszDir); pszTo = NULL; break; default: ASSERT(0); ids = 0; break; } if (ConstructMessage(&pszMsg, g_hinst, MAKEINTRESOURCE(ids), pszFrom, pszTo)) { lstrcpyn(pszBuf, pszMsg, cbBuf); GFree(pszMsg); } else *pszBuf = 0; } else *pszBuf = 0; }
/*----------------------------------------------------------
Purpose: Reconcile a given reclist
Returns: standard result Cond: -- */ HRESULT PUBLIC Sync_ReconcileRecList( PRECLIST prl, // ptr to reclist
LPCTSTR pszBrfPath, HWND hwndProgress, UINT uFlags) // RF_*
{ HRESULT hres; if (prl) { HWND hwndOwner = GetParent(hwndProgress); HWND hwndStatusText = UpdBar_GetStatusWindow(hwndProgress); TCHAR szPath[MAX_PATH]; TCHAR sz[MAXBUFLEN]; TWINRESULT tr; PRECITEM pri; PROGPARAM progparam; ULONG ulcItems; WORD wDelta; DEBUG_CODE( Sync_DumpRecList(TR_SUCCESS, prl, TEXT("Updating")); ) hres = NOERROR; // assume success
// Grab the mutex to delay any further calculation in any
// Briefcase views' secondary threads until we're done
// processing here.
Delay_Own(); // Determine the range of the progress bar
UpdBar_SetRange(hwndProgress, MAX_RANGE); ulcItems = CountActionItems(prl); if (0 < ulcItems) wDelta = (WORD)(MAX_RANGE / ulcItems); else wDelta = 0; progparam.hwndProgress = hwndProgress; // Start updating
Sync_BeginRec(prl->hbr); ulcItems = 0; for (pri = prl->priFirst; pri; pri = pri->priNext) { // Did the user explicitly skip this recitem or
// is this a broken merge?
if (RIU_SKIP == pri->dwUser || RIA_BROKEN_MERGE == pri->riaction) { // Yes; don't call ReconcileItem
continue; } // Is something going to be done to this recitem?
if (RIU_SHOWSTATUS == pri->dwUser) { // Yes; update the name of the file we're updating
UpdBar_SetName(hwndProgress, pri->pcszName); DecideDescString(pszBrfPath, pri, sz, ARRAYSIZE(sz), szPath); UpdBar_SetDescription(hwndProgress, sz); ASSERT(0 < wDelta); progparam.wPosBase = (WORD)(wDelta * ulcItems); progparam.wPosMax = wDelta; progparam.wPosPrev = 0; progparam.bSkip = FALSE; } else { progparam.bSkip = TRUE; } // Call ReconcileItem even for nops, so the recnode states
// will be updated by the engine.
tr = Sync_ReconcileItem(pri, RecStatusProc, (LPARAM)&progparam, RI_FL_FEEDBACK_WINDOW_VALID, hwndProgress, hwndStatusText); if (TR_SUCCESS != tr && IsFileRecItem(pri)) // ignore folder recitem errors
{ // On some conditions, stop updating completely
hres = HRESULT_FROM_TR(tr); switch (hres) { case E_TR_OUT_OF_MEMORY: case E_TR_RH_LOAD_FAILED: { // Couldn't load the merge handler. Tell the user but
// continue on...
int id = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_ERR_NO_MERGE_HANDLER), MAKEINTRESOURCE(IDS_CAP_UPDATE), NULL, MB_WARNING | MB_OKCANCEL, PathGetDisplayName(szPath, sz)); if (IDOK == id) break; // continue updating other files
} goto StopUpdating; case E_TR_DELETED_TWIN: // Allow the updating to continue.
break; case E_TR_DEST_OPEN_FAILED: case E_TR_FILE_CHANGED: if (IsFlagClear(uFlags, RF_ONADD)) { // Allow the updating to continue. Remember the
// latest error for the end.
break; } // Fall thru
// | |
// v v
default: goto StopUpdating; } } // Was something done to this recitem?
if (RIU_SHOWSTATUS == pri->dwUser) { // Yes; update the progress bar
UpdBar_SetPos(hwndProgress, (WORD)(wDelta * ++ulcItems)); } // Check if the Cancel button was pressed
if (UpdBar_QueryAbort(hwndProgress)) { hres = E_ABORT; break; } } StopUpdating: if (FAILED(hres)) { Sync_DumpRecItem(tr, pri, NULL); HandleUpdateErrors(hwndOwner, hres, uFlags); if (IsFlagSet(uFlags, RF_ONADD)) { // Hack: since the caller also handles some error messages,
// return a generic failure code to prevent repeated
// error messages.
hres = E_FAIL; } } // Were there any items at all?
else if (0 == prl->ulcItems) { // No
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_MSG_NoMatchingFiles), MAKEINTRESOURCE(IDS_CAP_UPDATE), NULL, MB_INFO); } Sync_EndRec(prl->hbr); Delay_Release(); } else hres = E_INVALIDARG; return hres; } /*----------------------------------------------------------
Purpose: Status procedure that is called during a single ReconcileItem call.
Returns: varies Cond: -- */ BOOL CALLBACK CreateRecListProc( CREATERECLISTPROCMSG msg, LPARAM lParam, LPARAM lParamUser) { return !AbortEvt_Query((PABORTEVT)lParamUser); } /*----------------------------------------------------------
Purpose: Creates a reclist and optionally shows a progress bar during the creation.
Returns: standard result Cond: -- */ HRESULT PUBLIC Sync_CreateRecListEx( HTWINLIST htl, PABORTEVT pabortevt, PRECLIST * pprl) { TWINRESULT tr; ASSERT(pprl); tr = Sync_CreateRecList(htl, CreateRecListProc, (LPARAM)pabortevt, pprl); return HRESULT_FROM_TR(tr); }
/*----------------------------------------------------------
Purpose: Return true if the file or folder is a twin.
There are some cases when this function cannot successfully determine this unless the caller first tells it explicitly whether the object is a file or folder. Otherwise this function will attempt to determine this on its own. Returns: S_OK if it is a twin S_FALSE if it is not any other is an error Cond: -- */ HRESULT PUBLIC Sync_IsTwin( HBRFCASE hbrfcase, LPCTSTR pszPath, UINT uFlags) // SF_* flags
{ HRESULT hres; TWINRESULT tr; ASSERT(pszPath); // The caller may already know whether this is a twin or not.
// Remind him.
if (IsFlagSet(uFlags, SF_ISTWIN)) return S_OK; else if (IsFlagSet(uFlags, SF_ISNOTTWIN)) return S_FALSE; // Is this a folder?
if (IsFlagSet(uFlags, SF_ISFOLDER) || PathIsDirectory(pszPath)) { // Yes; is it a twin?
BOOL bRet; tr = Sync_IsFolder(hbrfcase, pszPath, &bRet); if (TR_SUCCESS == tr) { // Yes/no
hres = bRet ? S_OK : S_FALSE; } else { // Error
hres = HRESULT_FROM_TR(tr); } } else { // No
HOBJECTTWIN hot; TCHAR szDir[MAX_PATH]; lstrcpy(szDir, pszPath); PathRemoveFileSpec(szDir); tr = Sync_GetObject(hbrfcase, szDir, PathFindFileName(pszPath), &hot); if (TR_SUCCESS == tr) { // Is it a twin?
if (NULL != hot) { // Yes
Sync_ReleaseTwin(hot); hres = S_OK; } else { // No; (no need to release a null handle)
hres = S_FALSE; } } else { // Error
hres = HRESULT_FROM_TR(tr); } } return hres; }
/*----------------------------------------------------------
Purpose: Create a reclist with everything in it.
Returns: standard result Cond: Caller must destroy the reclist */ HRESULT PUBLIC Sync_CreateCompleteRecList( HBRFCASE hbrf, PABORTEVT pabortevt, PRECLIST * pprl) { HRESULT hres = E_OUTOFMEMORY; HTWINLIST htl; ASSERT(pprl); *pprl = NULL; if (TR_SUCCESS == Sync_CreateTwinList(hbrf, &htl)) { Sync_AddAllToTwinList(htl); hres = Sync_CreateRecListEx(htl, pabortevt, pprl); Sync_DestroyTwinList(htl); } return hres; }
/*----------------------------------------------------------
Purpose: Add a twin to the twinlist given the pathname. If the pathname is not a twin, we don't add it.
Returns: TRUE on success, even when this isn't a twin Cond: Caller must destroy the folder list if lplpftl is not NULL */ BOOL PUBLIC Sync_AddPathToTwinList( HBRFCASE hbrf, HTWINLIST htl, LPCTSTR lpcszPath, // Path
PFOLDERTWINLIST * lplpftl) // May be NULL
{ BOOL bRet = FALSE; ASSERT(lpcszPath); ASSERT(htl); if (lplpftl) *lplpftl = NULL; if (lpcszPath) { if (PathIsDirectory(lpcszPath)) { BOOL fIsTwin = FALSE; PFOLDERTWINLIST lpftl; // We only want to return false if we couldn't mark something
// that should have been marked. If this isn't a twin,
// we still succeed.
bRet = TRUE; Sync_IsFolder(hbrf, lpcszPath, &fIsTwin); if (fIsTwin) // Is this actually twinned?
{ // This is a folder twin. Add to reclist "the folder way".
//
if (Sync_CreateFolderList(hbrf, lpcszPath, &lpftl) != TR_SUCCESS) bRet = FALSE; else { PCFOLDERTWIN lpcfolder; ASSERT(lpftl->pcftFirst); // only mark the ones that aren't in other briefcases
//
lpcfolder = lpftl->pcftFirst; while (lpcfolder) { Sync_AddToTwinList(htl, lpcfolder->hftOther); lpcfolder = lpcfolder->pcftNext; } if (lplpftl) *lplpftl = lpftl; else Sync_DestroyFolderList(lpftl); } } } else { HOBJECTTWIN hot = NULL; TCHAR szDir[MAX_PATH]; // Add the twins to the reclist "the object way"
//
lstrcpy(szDir, lpcszPath); PathRemoveFileSpec(szDir); Sync_GetObject(hbrf, szDir, PathFindFileName(lpcszPath), &hot); if (hot) // Is this actually a twin?
{ // yep
Sync_AddToTwinList(htl, hot); Sync_ReleaseTwin(hot); } if (lplpftl) *lplpftl = NULL; bRet = TRUE; } } return bRet; }
/*----------------------------------------------------------
Purpose: Asks the user to confirm splitting one or more files.
Returns: IDYES or IDNO Cond: -- */ int PRIVATE ConfirmSplit( HWND hwndOwner, LPCTSTR pszPath, UINT cFiles) { int idRet; ASSERT(pszPath); ASSERT(1 <= cFiles); // Multiple files?
if (1 < cFiles) { // Yes
idRet = MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_MSG_ConfirmMultiSplit), MAKEINTRESOURCE(IDS_CAP_ConfirmMultiSplit), LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_MULT)), MB_QUESTION, cFiles); } else { // No
UINT ids; UINT idi; TCHAR szName[MAX_PATH]; if (PathIsDirectory(pszPath)) { ids = IDS_MSG_ConfirmFolderSplit; idi = IDI_SPLIT_FOLDER; } else { ids = IDS_MSG_ConfirmFileSplit; idi = IDI_SPLIT_FILE; } idRet = MsgBox(hwndOwner, MAKEINTRESOURCE(ids), MAKEINTRESOURCE(IDS_CAP_ConfirmSplit), LoadIcon(g_hinst, MAKEINTRESOURCE(idi)), MB_QUESTION, PathGetDisplayName(pszPath, szName)); } return idRet; }
/*----------------------------------------------------------
Purpose: Splits a path from its sync copy. Private function called by Sync_Split.
Returns: standard result S_OK if it is split Cond: -- */ HRESULT PRIVATE SplitPath( HBRFCASE hbrf, LPCTSTR pszPath, HWND hwndOwner, UINT uFlags) // SF_* flags
{ HRESULT hres; TWINRESULT tr; TCHAR sz[MAX_PATH]; if (pszPath) { // Is the object a folder?
if (IsFlagSet(uFlags, SF_ISFOLDER) || PathIsDirectory(pszPath)) { // Yup
BOOL bIsTwin; if (IsFlagSet(uFlags, SF_ISTWIN)) // Optimization
{ tr = TR_SUCCESS; bIsTwin = TRUE; } else if (IsFlagSet(uFlags, SF_ISNOTTWIN)) // Optimization
{ tr = TR_SUCCESS; bIsTwin = FALSE; } else { tr = Sync_IsFolder(hbrf, pszPath, &bIsTwin); } // Is this folder a twin?
if (TR_SUCCESS == tr) { if (bIsTwin) { // Yes; delete all the twin handles associated with it
PFOLDERTWINLIST lpftl; tr = Sync_CreateFolderList(hbrf, pszPath, &lpftl); if (TR_SUCCESS == tr) { PCFOLDERTWIN lpcfolder; ASSERT(lpftl); for (lpcfolder = lpftl->pcftFirst; lpcfolder; lpcfolder = lpcfolder->pcftNext) { Sync_DeleteTwin(lpcfolder->hftOther); } Sync_DestroyFolderList(lpftl); if (IsFlagClear(uFlags, SF_QUIET)) { // Send a notification so it is redrawn.
PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE); } hres = NOERROR; } } else if (IsFlagClear(uFlags, SF_QUIET)) { // No
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_MSG_FolderAlreadyOrphan), MAKEINTRESOURCE(IDS_CAP_Split), LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FOLDER)), MB_INFO, PathGetDisplayName(pszPath, sz)); hres = S_FALSE; } else { hres = S_FALSE; } } } else { // No; it is a file
HOBJECTTWIN hot; ULONG ulc; lstrcpy(sz, pszPath); PathRemoveFileSpec(sz); // Is this file a twin?
// (We need the twin handle below, so we cannot take
// advantage of SF_ISTWIN or SF_ISNOTTWIN.)
tr = Sync_GetObject(hbrf, sz, PathFindFileName(pszPath), &hot); if (TR_SUCCESS == tr) { if (hot) { // Yes; is this inside a folder twin?
// (If we remove this check, the engine needs to be able
// to exclude specific files from a folder twin.)
tr = Sync_CountSourceFolders(hot, &ulc); if (TR_SUCCESS == tr) { if (0 < ulc) { // Yes; can't do it
if (IsFlagClear(uFlags, SF_QUIET)) { UINT rgids[2] = { IDS_ERR_1_CantSplit, IDS_ERR_2_CantSplit }; LPTSTR psz; if (FmtString(&psz, IDS_ERR_F_CantSplit, rgids, ARRAYSIZE(rgids))) { // This object twin belongs to a folder twin. We can't
// allow this action.
MsgBox(hwndOwner, psz, MAKEINTRESOURCE(IDS_CAP_STATUS), LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FILE)), MB_ERROR); GFree(psz); } } hres = S_FALSE; } else { // No; delete the twin
Sync_DeleteTwin(hot); if (IsFlagClear(uFlags, SF_QUIET)) { // Send a notification so it's redrawn immediately.
PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE); } hres = NOERROR; } } Sync_ReleaseTwin(hot); } else if (IsFlagClear(uFlags, SF_QUIET)) { // No
MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_MSG_FileAlreadyOrphan), MAKEINTRESOURCE(IDS_CAP_Split), LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FILE)), MB_INFO, PathGetDisplayName(pszPath, sz)); hres = S_FALSE; } } } if (TR_SUCCESS != tr) hres = HRESULT_FROM_TR(tr); } else hres = S_FALSE; return hres; }
/*----------------------------------------------------------
Purpose: Deletes a series of twins from the engine database. The user is optionally asked to confirm the action.
If a file is an orphan, the user is optionally notified. The user is also optionally notified of any errors. Returns: standard result S_OK if anything was deleted
Cond: -- */ HRESULT PUBLIC Sync_Split( HBRFCASE hbrf, LPCTSTR pszList, UINT cFiles, HWND hwndOwner, UINT uFlags) { HRESULT hres; UINT id; TCHAR szCanon[MAX_PATH]; TCHAR sz[MAX_PATH]; ASSERT(pszList); ASSERT(0 < cFiles); // Special precondition: is it a single file?
if (1 == cFiles) { // Yes; is it a twin?
BrfPathCanonicalize(pszList, szCanon); hres = Sync_IsTwin(hbrf, szCanon, uFlags); if (S_FALSE == hres) { // No; tell the user. Don't bother confirming the action first.
if (IsFlagClear(uFlags, SF_QUIET)) { UINT ids; UINT idi; if (IsFlagSet(uFlags, SF_ISFOLDER) || PathIsDirectory(szCanon)) { ids = IDS_MSG_FolderAlreadyOrphan; idi = IDI_SPLIT_FOLDER; } else { ids = IDS_MSG_FileAlreadyOrphan; idi = IDI_SPLIT_FILE; } MsgBox(hwndOwner, MAKEINTRESOURCE(ids), MAKEINTRESOURCE(IDS_CAP_Split), LoadIcon(g_hinst, MAKEINTRESOURCE(idi)), MB_INFO, PathGetDisplayName(szCanon, sz)); } } else if (S_OK == hres) { // Yes
if (IsFlagClear(uFlags, SF_NOCONFIRM)) id = ConfirmSplit(hwndOwner, szCanon, 1); else id = IDYES; if (IDYES == id) { hres = SplitPath(hbrf, szCanon, hwndOwner, uFlags); if (IsFlagClear(uFlags, SF_QUIET)) { SHChangeNotifyHandleEvents(); } } else hres = S_FALSE; } } // Multiselection: ask the user first
else { if (IsFlagClear(uFlags, SF_NOCONFIRM)) id = ConfirmSplit(hwndOwner, pszList, cFiles); else id = IDYES; if (IDYES == id) { // Remove all the files from the engine database
LPCTSTR psz; UINT i; HRESULT hresT; hres = S_FALSE; // assume success but nothing done
for (i = 0, psz = pszList; i < cFiles; i++) { // Get dragged file/folder name
//
BrfPathCanonicalize(psz, szCanon); hresT = SplitPath(hbrf, szCanon, hwndOwner, uFlags); if (S_OK == hresT) hres = S_OK; // (Don't set back to FALSE once it is TRUE)
else if (FAILED(hresT)) { hres = hresT; break; } DataObj_NextFile(psz); // Set psz to next file in list
} if (IsFlagClear(uFlags, SF_QUIET)) { SHChangeNotifyHandleEvents(); // (Do this after the loop)
} } else hres = S_FALSE; } return hres; }
/*----------------------------------------------------------
Purpose: Change the recitem action and the two recnodes of importance to the specified action. Returns: -- Cond: -- */ void PUBLIC Sync_ChangeRecItemAction( PRECITEM pri, LPCTSTR pszBrfPath, LPCTSTR pszInsideDir, // Folder inside the briefcase
UINT riaction) // One of RAIA_* values to change to
{ HRESULT hres; PRECNODE prnInside; PRECNODE prnOutside; // Determine which node is inside the briefcase and which one is
// outside.
//
hres = Sync_GetNodePair(pri, pszBrfPath, pszInsideDir, &prnInside, &prnOutside); if (SUCCEEDED(hres)) { ASSERT(prnInside); ASSERT(prnOutside); switch(riaction) { case RAIA_TOIN: pri->dwUser = RIU_CHANGED; pri->riaction = RIA_COPY; prnInside->rnaction = RNA_COPY_TO_ME; prnOutside->rnaction = RNA_COPY_FROM_ME; break; case RAIA_TOOUT: pri->dwUser = RIU_CHANGED; pri->riaction = RIA_COPY; prnInside->rnaction = RNA_COPY_FROM_ME; prnOutside->rnaction = RNA_COPY_TO_ME; break; case RAIA_SKIP: pri->dwUser = RIU_SKIP; break; case RAIA_MERGE: pri->dwUser = RIU_CHANGED; pri->riaction = RIA_MERGE; prnInside->rnaction = RNA_MERGE_ME; prnOutside->rnaction = RNA_MERGE_ME; break; #ifdef NEW_REC
case RAIA_DONTDELETE: if (RNA_DELETE_ME == prnInside->rnaction) { pri->dwUser = RIU_CHANGED; pri->riaction = RIA_NOTHING; prnInside->rnaction = RNA_NOTHING; } else if (RNA_DELETE_ME == prnOutside->rnaction) { pri->dwUser = RIU_CHANGED; pri->riaction = RIA_NOTHING; prnOutside->rnaction = RNA_NOTHING; } break; case RAIA_DELETEIN: pri->dwUser = RIU_CHANGED; pri->riaction = RIA_DELETE; prnInside->rnaction = RNA_DELETE_ME; prnOutside->rnaction = RNA_NOTHING; break; case RAIA_DELETEOUT: pri->dwUser = RIU_CHANGED; pri->riaction = RIA_DELETE; prnInside->rnaction = RNA_NOTHING; prnOutside->rnaction = RNA_DELETE_ME; break; #endif
default: // (The other values don't make sense here)
ASSERT(0); break; } } }
///////////////////////////////////////////////////// PRIVATE FUNCTIONS
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Dumps the contents of the given twin structure to to debug out Returns: -- Cond: -- */ void PUBLIC Sync_FnDump( LPVOID lpvBuf, UINT cbBuf) { int bDump; #define szDumpTwin TEXT("Dump TWIN: ")
#define szDumpSp TEXT(" ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_CREATETWIN); } LEAVEEXCLUSIVE(); if (!bDump) return ; if (cbBuf == sizeof(NEWOBJECTTWIN)) { PNEWOBJECTTWIN lpnot = (PNEWOBJECTTWIN)lpvBuf; TRACE_MSG(TF_ALWAYS, TEXT("%s.Folder1 = {%s}\r\n%s.Folder2 = {%s}\r\n%s.Name = {%s}\r\n"), (LPTSTR)szDumpTwin, lpnot->pcszFolder1, (LPTSTR)szDumpSp, lpnot->pcszFolder2, (LPTSTR)szDumpSp, lpnot->pcszName); } else if (cbBuf == sizeof(NEWFOLDERTWIN)) { PNEWFOLDERTWIN lpnft = (PNEWFOLDERTWIN)lpvBuf; TRACE_MSG(TF_ALWAYS, TEXT("%s.Folder1 = {%s}\r\n%s.Folder2 = {%s}\r\n%s.Name = {%s}\r\n%s.dwFlags = 0x%04lx\r\n"), (LPTSTR)szDumpTwin, lpnft->pcszFolder1, (LPTSTR)szDumpSp, lpnft->pcszFolder2, (LPTSTR)szDumpSp, lpnft->pcszName, (LPTSTR)szDumpSp, (DWORD)lpnft->dwFlags); } }
/*----------------------------------------------------------
Purpose: Return English form of RIA_ flags Returns: Cond: -- */ LPTSTR PRIVATE LpszFromItemAction( ULONG riaction) { switch (riaction) { DEBUG_CASE_STRING( RIA_NOTHING ); DEBUG_CASE_STRING( RIA_COPY ); DEBUG_CASE_STRING( RIA_MERGE ); DEBUG_CASE_STRING( RIA_BROKEN_MERGE ); #ifdef NEW_REC
DEBUG_CASE_STRING( RIA_DELETE ); #endif
default: return TEXT("RIA unknown"); } }
/*----------------------------------------------------------
Purpose: Return English form of RNA_ flags Returns: Cond: -- */ LPTSTR PRIVATE LpszFromNodeAction( ULONG rnaction) { switch (rnaction) { DEBUG_CASE_STRING( RNA_NOTHING ); DEBUG_CASE_STRING( RNA_COPY_TO_ME ); DEBUG_CASE_STRING( RNA_COPY_FROM_ME ); DEBUG_CASE_STRING( RNA_MERGE_ME ); #ifdef NEW_REC
DEBUG_CASE_STRING( RNA_DELETE_ME ); #endif
default: return TEXT("RNA unknown"); } }
/*----------------------------------------------------------
Purpose: Return English form of RNS_ flags Returns: Cond: -- */ LPTSTR PRIVATE LpszFromNodeState( ULONG rnstate) { switch (rnstate) { #ifdef NEW_REC
DEBUG_CASE_STRING( RNS_NEVER_RECONCILED ); #endif
DEBUG_CASE_STRING( RNS_UNAVAILABLE ); DEBUG_CASE_STRING( RNS_DOES_NOT_EXIST ); DEBUG_CASE_STRING( RNS_DELETED ); DEBUG_CASE_STRING( RNS_NOT_RECONCILED ); DEBUG_CASE_STRING( RNS_UP_TO_DATE ); DEBUG_CASE_STRING( RNS_CHANGED ); default: return TEXT("RNS unknown"); } }
/*----------------------------------------------------------
Purpose: Dump the RECNODE Returns: Cond: -- */ void PUBLIC Sync_DumpRecNode( TWINRESULT tr, PRECNODE lprn) { BOOL bDump; TCHAR szBuf[MAXMSGLEN]; #define szDumpLabel TEXT("\tDump RECNODE: ")
#define szDumpMargin TEXT("\t ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_RECNODE); } LEAVEEXCLUSIVE(); if (!bDump || lprn == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER) return ; wsprintf(szBuf, TEXT("%s.Folder = {%s}\r\n"), (LPTSTR)szDumpLabel, lprn->pcszFolder); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.hObjectTwin = %lx\r\n"), (LPTSTR)szDumpMargin, lprn->hObjectTwin); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.rnstate = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromNodeState(lprn->rnstate)); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.rnaction = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromNodeAction(lprn->rnaction)); OutputDebugString(szBuf); OutputDebugString(TEXT("\r\n")); #undef szDumpLabel
#undef szDumpMargin
} /*----------------------------------------------------------
Purpose: Dump the RECITEM Returns: Cond: -- */ void PUBLIC Sync_DumpRecItem( TWINRESULT tr, PRECITEM lpri, LPCTSTR pszMsg) { BOOL bDump; PRECNODE lprn; TCHAR szBuf[MAXMSGLEN]; #define szDumpLabel TEXT("Dump RECITEM: ")
#define szDumpMargin TEXT(" ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_RECITEM); } LEAVEEXCLUSIVE(); if (!bDump || lpri == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER) return ; if (pszMsg) TRACE_MSG(TF_ALWAYS, pszMsg); wsprintf(szBuf, TEXT("tr = %s\r\n"), (LPTSTR)SzFromTR(tr)); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.Name = {%s}\r\n"), (LPTSTR)szDumpLabel, lpri->pcszName); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.hTwinFamily = %lx\r\n"), (LPTSTR)szDumpMargin, lpri->hTwinFamily); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.ulcNodes = %lu\r\n"), (LPTSTR)szDumpMargin, lpri->ulcNodes); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.riaction = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromItemAction(lpri->riaction)); OutputDebugString(szBuf); lprn = lpri->prnFirst; while (lprn) { Sync_DumpRecNode(tr, lprn); lprn = lprn->prnNext; } #undef szDumpLabel
#undef szDumpMargin
}
/*----------------------------------------------------------
Purpose: Dump the RECLIST Returns: Cond: -- */ void PUBLIC Sync_DumpRecList( TWINRESULT tr, PRECLIST lprl, LPCTSTR pszMsg) { BOOL bDump; PRECITEM lpri; TCHAR szBuf[MAXMSGLEN]; #define szDumpLabel TEXT("Dump RECLIST: ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_RECLIST); } LEAVEEXCLUSIVE(); if (!bDump) return ; if (pszMsg) TRACE_MSG(TF_ALWAYS, pszMsg); // Note we only dump on TR_SUCCESS
//
wsprintf(szBuf, TEXT("tr = %s\r\n"), (LPTSTR)SzFromTR(tr)); OutputDebugString(szBuf); if (lprl == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER) return ; wsprintf(szBuf, TEXT("%s.ulcItems = %lu\r\n"), (LPTSTR)szDumpLabel, lprl->ulcItems); OutputDebugString(szBuf); lpri = lprl->priFirst; while (lpri) { Sync_DumpRecItem(TR_SUCCESS, lpri, NULL); lpri = lpri->priNext; } #undef szDumpLabel
}
/*----------------------------------------------------------
Purpose: Dump the FOLDERTWIN Returns: -- Cond: -- */ void PUBLIC Sync_DumpFolderTwin( PCFOLDERTWIN pft) { BOOL bDump; TCHAR szBuf[MAXMSGLEN]; #define szDumpLabel TEXT("Dump FOLDERTWIN: ")
#define szDumpMargin TEXT(" ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_FOLDERTWIN); } LEAVEEXCLUSIVE(); if (!bDump || pft == NULL) return ; wsprintf(szBuf, TEXT("%s.Name = {%s}\r\n"), (LPTSTR)szDumpLabel, pft->pcszName); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.pszSrcFolder = {%s}\r\n"), (LPTSTR)szDumpMargin, pft->pcszSrcFolder); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.pszOtherFolder = {%s}\r\n"), (LPTSTR)szDumpMargin, pft->pcszOtherFolder); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.dwFlags = %lx\r\n"), (LPTSTR)szDumpMargin, pft->dwFlags); OutputDebugString(szBuf); wsprintf(szBuf, TEXT("%s.dwUser = %lx\r\n"), (LPTSTR)szDumpMargin, pft->dwUser); OutputDebugString(szBuf); #undef szDumpLabel
#undef szDumpMargin
}
/*----------------------------------------------------------
Purpose: Dump the FOLDERTWINLIST Returns: -- Cond: -- */ void PUBLIC Sync_DumpFolderTwinList( PFOLDERTWINLIST pftl, LPCTSTR pszMsg) { BOOL bDump; PCFOLDERTWIN pft; TCHAR szBuf[MAXMSGLEN]; #define szDumpLabel TEXT("Dump FOLDERTWINLIST: ")
ENTEREXCLUSIVE(); { bDump = IsFlagSet(g_uDumpFlags, DF_FOLDERTWIN); } LEAVEEXCLUSIVE(); if (!bDump) return ; if (pszMsg) TRACE_MSG(TF_ALWAYS, pszMsg); if (pftl == NULL) return ; wsprintf(szBuf, TEXT("%s.ulcItems = %lu\r\n"), (LPTSTR)szDumpLabel, pftl->ulcItems); OutputDebugString(szBuf); for (pft = pftl->pcftFirst; pft; pft = pft->pcftNext) { Sync_DumpFolderTwin(pft); } #undef szDumpLabel
}
#endif
|