|
|
/*
* expandft.c - Routines for expanding folder twins to object twins. */
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
/* Constants
************/
/* for subtree folder searching */
#define STAR_DOT_STAR TEXT("*.*")
/* Macros
*********/
/* name component macros used by NameComponentsIntersect() */
#define COMPONENT_CHARS_MATCH(ch1, ch2) (CharLower((PTSTR)(DWORD_PTR)ch1) == CharLower((PTSTR)(DWORD_PTR)ch2) || (ch1) == QMARK || (ch2) == QMARK)
#define IS_COMPONENT_TERMINATOR(ch) (! (ch) || (ch) == PERIOD || (ch) == ASTERISK)
/* Types
********/
/* find structure used by ExpandSubtree() */
typedef struct _findstate { HANDLE hff;
WIN32_FIND_DATA wfd; } FINDSTATE; DECLARE_STANDARD_TYPES(FINDSTATE);
/* information structure passed to GenerateObjectTwinFromFolderTwinProc() */
typedef struct _expandsubtreetwininfo { PFOLDERPAIR pfp;
UINT ucbSubtreeRootPathLen;
HCLSIFACECACHE hcic;
CREATERECLISTPROC crlp;
LPARAM lpCallbackData;
TWINRESULT tr; } EXPANDSUBTREETWININFO; DECLARE_STANDARD_TYPES(EXPANDSUBTREETWININFO);
/* Module Variables
*******************/
/*
* folder names to be avoided during subtree expansion (comparison is * case-insensitive) */
PRIVATE_DATA CONST LPCTSTR MrgcpcszFoldersToAvoid[] = { TEXT("."), TEXT("..") };
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE BOOL SetObjectTwinFileStamp(POBJECTTWIN, PVOID); PRIVATE_CODE void MarkFolderTwinDeletionPending(PFOLDERPAIR); PRIVATE_CODE void UnmarkFolderTwinDeletionPending(PFOLDERPAIR); PRIVATE_CODE TWINRESULT ExpandFolderTwin(PFOLDERPAIR, HCLSIFACECACHE, CREATERECLISTPROC, LPARAM); PRIVATE_CODE BOOL GenerateObjectTwinFromFolderTwinProc(LPCTSTR, PCWIN32_FIND_DATA, PVOID); PRIVATE_CODE TWINRESULT ExpandSubtreeTwin(PFOLDERPAIR, HCLSIFACECACHE, CREATERECLISTPROC, LPARAM); PRIVATE_CODE BOOL IsFolderToExpand(LPCTSTR); PRIVATE_CODE TWINRESULT FakeObjectTwinFromFolderTwin(PCFOLDERPAIR, LPCTSTR, LPCTSTR, HCLSIFACECACHE, POBJECTTWIN *, POBJECTTWIN *); PRIVATE_CODE TWINRESULT AddFolderObjectTwinFromFolderTwin(PCFOLDERPAIR, LPCTSTR, HCLSIFACECACHE); PRIVATE_CODE TWINRESULT AddFileObjectTwinFromFolderTwin(PCFOLDERPAIR, LPCTSTR, PCWIN32_FIND_DATA, HCLSIFACECACHE); PRIVATE_CODE BOOL NameComponentsIntersect(LPCTSTR, LPCTSTR); PRIVATE_CODE BOOL AttributesMatch(DWORD, DWORD); PRIVATE_CODE void PrepareForFolderTwinExpansion(HBRFCASE); PRIVATE_CODE TWINRESULT MyExpandIntersectingFolderTwins(PFOLDERPAIR, HCLSIFACECACHE, CREATERECLISTPROC, LPARAM); PRIVATE_CODE TWINRESULT HalfExpandIntersectingFolderTwins(PFOLDERPAIR, HCLSIFACECACHE, CREATERECLISTPROC, LPARAM);
#ifdef DEBUG
PRIVATE_CODE BOOL IsValidPCEXPANDSUBTREETWININFO(PCEXPANDSUBTREETWININFO);
#endif
/*
** SetObjectTwinFileStampCondition() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL SetObjectTwinFileStampCondition(POBJECTTWIN pot, PVOID fscond) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(IsValidFILESTAMPCONDITION((FILESTAMPCONDITION)PtrToUlong(fscond)));
ZeroMemory(&(pot->fsCurrent), sizeof(pot->fsCurrent)); pot->fsCurrent.fscond = (FILESTAMPCONDITION)PtrToUlong(fscond);
SetStubFlag(&(pot->stub), STUB_FL_FILE_STAMP_VALID);
return(TRUE); }
/*
** MarkFolderTwinDeletionPending() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void MarkFolderTwinDeletionPending(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (IsStubFlagClear(&(pfp->stub), STUB_FL_DELETION_PENDING)) { TCHAR rgchRootPath[MAX_PATH_LEN];
GetPathRootString(pfp->hpath, rgchRootPath, ARRAYSIZE(rgchRootPath));
if (PathExists(rgchRootPath)) { SetStubFlag(&(pfp->stub), STUB_FL_DELETION_PENDING);
TRACE_OUT((TEXT("MarkFolderTwinDeletionPending(): Folder twin deletion pending for deleted folder %s."), DebugGetPathString(pfp->hpath))); } else WARNING_OUT((TEXT("MarkFolderTwinDeletionPending(): Root path %s of folder %s does not exist."), rgchRootPath, DebugGetPathString(pfp->hpath))); }
return; }
/*
** UnmarkFolderTwinDeletionPending() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void UnmarkFolderTwinDeletionPending(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (IsStubFlagSet(&(pfp->stub), STUB_FL_DELETION_PENDING)) WARNING_OUT((TEXT("UnmarkFolderTwinDeletionPending(): Folder twin %s was deleted but has been recreated."), DebugGetPathString(pfp->hpath)));
ClearStubFlag(&(pfp->stub), STUB_FL_DELETION_PENDING);
return; }
/*
** ExpandFolderTwin() ** ** Expands a single folder of half of a folder pair into object twins. ** ** Arguments: pfp - pointer to folder pair whose folder is to be expanded ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT ExpandFolderTwin(PFOLDERPAIR pfp, HCLSIFACECACHE hcic, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr = TR_SUCCESS; TCHAR rgchSearchSpec[MAX_PATH_LEN];
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
ASSERT(IsPathVolumeAvailable(pfp->hpath)); ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_SUBTREE)); ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_USED));
/* Build search specification. */
GetPathString(pfp->hpath, rgchSearchSpec, ARRAYSIZE(rgchSearchSpec));
if (PathExists(rgchSearchSpec)) { WIN32_FIND_DATA wfd; HANDLE hff;
UnmarkFolderTwinDeletionPending(pfp);
TRACE_OUT((TEXT("ExpandFolderTwin(): Expanding folder %s for objects matching %s."), rgchSearchSpec, GetString(pfp->pfpd->hsName)));
tr = AddFolderObjectTwinFromFolderTwin(pfp, EMPTY_STRING, hcic);
if (tr == TR_SUCCESS) { CatPath(rgchSearchSpec, GetString(pfp->pfpd->hsName), ARRAYSIZE(rgchSearchSpec));
hff = FindFirstFile(rgchSearchSpec, &wfd);
/* Did we find a matching object? */
if (hff != INVALID_HANDLE_VALUE) { /* Yes. */
do { /* Ping. */
if (NotifyCreateRecListStatus(crlp, CRLS_DELTA_CREATE_REC_LIST, 0, lpCallbackData)) { if (AttributesMatch(pfp->pfpd->dwAttributes, wfd.dwFileAttributes)) { TRACE_OUT((TEXT("ExpandFolderTwin(): Found matching object %s."), &(wfd.cFileName)));
tr = AddFileObjectTwinFromFolderTwin(pfp, EMPTY_STRING, &wfd, hcic);
if (tr != TR_SUCCESS) break; } } else tr = TR_ABORT;
} while (FindNextFile(hff, &wfd)); }
if (hff != INVALID_HANDLE_VALUE) FindClose(hff); }
TRACE_OUT((TEXT("ExpandFolderTwin(): Folder expansion complete."))); } else MarkFolderTwinDeletionPending(pfp);
return(tr); }
/*
** GenerateObjectTwinFromFolderTwinProc() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL GenerateObjectTwinFromFolderTwinProc(LPCTSTR pcszFolder, PCWIN32_FIND_DATA pcwfd, PVOID pvpesti) { TWINRESULT tr; PEXPANDSUBTREETWININFO pesti = pvpesti;
ASSERT(IsCanonicalPath(pcszFolder)); ASSERT(IS_VALID_READ_PTR(pcwfd, CWIN32_FIND_DATA)); ASSERT(IS_VALID_STRUCT_PTR(pesti, CEXPANDSUBTREETWININFO));
/* Ping. */
if (NotifyCreateRecListStatus(pesti->crlp, CRLS_DELTA_CREATE_REC_LIST, 0, pesti->lpCallbackData)) { if (IS_ATTR_DIR(pcwfd->dwFileAttributes)) { TCHAR rgchFolder[MAX_PATH_LEN];
/* Add any folder as a folder object twin. */
ComposePath(rgchFolder, pcszFolder, pcwfd->cFileName, ARRAYSIZE(rgchFolder)); ASSERT(lstrlen(rgchFolder) < ARRAYSIZE(rgchFolder));
tr = AddFolderObjectTwinFromFolderTwin( pesti->pfp, rgchFolder + (pesti->ucbSubtreeRootPathLen / sizeof(TCHAR)), pesti->hcic); } else { /* Does this file match the requested attributes? */
if (NamesIntersect(pcwfd->cFileName, GetString(pesti->pfp->pfpd->hsName)) && AttributesMatch(pesti->pfp->pfpd->dwAttributes, pcwfd->dwFileAttributes)) { /* Yes. Twin it. */
TRACE_OUT((TEXT("GenerateObjectTwinFromFolderTwinProc(): Found matching object %s in subfolder %s."), pcwfd->cFileName, pcszFolder));
tr = AddFileObjectTwinFromFolderTwin( pesti->pfp, pcszFolder + (pesti->ucbSubtreeRootPathLen / sizeof(TCHAR)), pcwfd, pesti->hcic); } else { TRACE_OUT((TEXT("GenerateObjectTwinFromFolderTwinProc(): Skipping unmatched object %s in subfolder %s."), pcwfd->cFileName, pcszFolder));
tr = TR_SUCCESS; } } } else tr = TR_ABORT;
pesti->tr = tr;
ASSERT(IS_VALID_STRUCT_PTR(pvpesti, CEXPANDSUBTREETWININFO));
return(tr == TR_SUCCESS); }
/*
** ExpandSubtreeTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT ExpandSubtreeTwin(PFOLDERPAIR pfp, HCLSIFACECACHE hcic, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr = TR_SUCCESS; TCHAR rgchPath[MAX_PATH_LEN];
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
ASSERT(IsPathVolumeAvailable(pfp->hpath)); ASSERT(IsStubFlagSet(&(pfp->stub), STUB_FL_SUBTREE)); ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_USED));
GetPathString(pfp->hpath, rgchPath, ARRAYSIZE(rgchPath));
if (PathExists(rgchPath)) { UnmarkFolderTwinDeletionPending(pfp);
tr = AddFolderObjectTwinFromFolderTwin(pfp, EMPTY_STRING, hcic);
if (tr == TR_SUCCESS) { EXPANDSUBTREETWININFO esti;
esti.pfp = pfp; esti.ucbSubtreeRootPathLen = lstrlen(rgchPath) * sizeof(TCHAR); // UNICODE really cb?
esti.hcic = hcic; esti.crlp = crlp; esti.lpCallbackData = lpCallbackData; esti.tr = TR_SUCCESS;
tr = ExpandSubtree(pfp->hpath, &GenerateObjectTwinFromFolderTwinProc, &esti);
ASSERT(tr != TR_SUCCESS || esti.tr == TR_SUCCESS);
if (tr == TR_SUCCESS || tr == TR_ABORT) tr = esti.tr; } } else MarkFolderTwinDeletionPending(pfp);
return(tr); }
/*
** IsFolderToExpand() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE BOOL IsFolderToExpand(LPCTSTR pcszFolder) { BOOL bExpandMe = TRUE; int i;
for (i = 0; i < ARRAY_ELEMENTS(MrgcpcszFoldersToAvoid); i++) { if (ComparePathStrings(pcszFolder, MrgcpcszFoldersToAvoid[i]) == CR_EQUAL) { bExpandMe = FALSE; break; } }
return(bExpandMe); }
/*
** FakeObjectTwinFromFolderTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT FakeObjectTwinFromFolderTwin(PCFOLDERPAIR pcfp, LPCTSTR pcszSubPath, LPCTSTR pcszName, HCLSIFACECACHE hcic, POBJECTTWIN *ppot1, POBJECTTWIN *ppot2) { TWINRESULT tr = TR_OUT_OF_MEMORY; HPATHLIST hpl; HPATH hpath1;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(IS_VALID_WRITE_PTR(ppot1, POBJECTTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppot2, POBJECTTWIN));
/* If the common sub path is non-empty, append it to the path strings. */
hpl = GetBriefcasePathList(pcfp->pfpd->hbr);
if (AddChildPath(hpl, pcfp->hpath, pcszSubPath, &hpath1)) { HPATH hpath2;
if (AddChildPath(hpl, pcfp->pfpOther->hpath, pcszSubPath, &hpath2)) { /* Add the two object twins. */
tr = TwinObjects(pcfp->pfpd->hbr, hcic, hpath1, hpath2, pcszName, ppot1, ppot2);
DeletePath(hpath2); }
DeletePath(hpath1); }
return(tr); }
/*
** AddFolderObjectTwinFromFolderTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT AddFolderObjectTwinFromFolderTwin(PCFOLDERPAIR pcfp, LPCTSTR pcszSubPath, HCLSIFACECACHE hcic) { TWINRESULT tr; POBJECTTWIN pot1; POBJECTTWIN pot2;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE));
/* Add the two object twins. */
tr = FakeObjectTwinFromFolderTwin(pcfp, pcszSubPath, EMPTY_STRING, hcic, &pot1, &pot2);
/* An attempted redundant add is ok. */
if (tr == TR_DUPLICATE_TWIN) tr = TR_SUCCESS;
if (tr == TR_SUCCESS) /* Cache folder object twin file stamps. */ SetObjectTwinFileStampCondition(pot1, IntToPtr(FS_COND_EXISTS));
return(tr); }
/*
** AddFileObjectTwinFromFolderTwin() ** ** Adds a pair of object twins generated by a folder twin. ** ** Arguments: pfp - pointer to folder pair that generated the two object ** twins ** pcszSubPath - common path off of folder pair roots describing ** object's location ** pcszName - name of object twins ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT AddFileObjectTwinFromFolderTwin(PCFOLDERPAIR pcfp, LPCTSTR pcszSubPath, PCWIN32_FIND_DATA pcwfd, HCLSIFACECACHE hcic) { TWINRESULT tr; POBJECTTWIN pot1; POBJECTTWIN pot2;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_STRING_PTR(pcszSubPath, CSTR)); ASSERT(IS_VALID_READ_PTR(pcwfd, CWIN32_FIND_DATA)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE));
/* Add the two object twins. */
tr = FakeObjectTwinFromFolderTwin(pcfp, pcszSubPath, pcwfd->cFileName, hcic, &pot1, &pot2);
/* An attempted redundant add is ok. */
if (tr == TR_DUPLICATE_TWIN) tr = TR_SUCCESS;
if (tr == TR_SUCCESS) { /* Cache object twin file stamp. */
CopyFileStampFromFindData(pcwfd, &(pot1->fsCurrent));
SetStubFlag(&(pot1->stub), STUB_FL_FILE_STAMP_VALID); }
return(tr); }
/*
** NameComponentsIntersect() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL NameComponentsIntersect(LPCTSTR pcszComponent1, LPCTSTR pcszComponent2) { BOOL bIntersect;
ASSERT(IS_VALID_STRING_PTR(pcszComponent1, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszComponent2, CSTR));
while (! IS_COMPONENT_TERMINATOR(*pcszComponent1) && ! IS_COMPONENT_TERMINATOR(*pcszComponent2) && COMPONENT_CHARS_MATCH(*pcszComponent1, *pcszComponent2)) { pcszComponent1 = CharNext(pcszComponent1); pcszComponent2 = CharNext(pcszComponent2); }
if (*pcszComponent1 == ASTERISK || *pcszComponent2 == ASTERISK || *pcszComponent1 == *pcszComponent2) bIntersect = TRUE; else { LPCTSTR pcszTrailer;
if (! *pcszComponent1 || *pcszComponent1 == PERIOD) pcszTrailer = pcszComponent2; else pcszTrailer = pcszComponent1;
while (*pcszTrailer == QMARK) pcszTrailer++;
if (IS_COMPONENT_TERMINATOR(*pcszTrailer)) bIntersect = TRUE; else bIntersect = FALSE; }
return(bIntersect); }
/*
** AttributesMatch() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none ** ** An object's attributes match the master attributes iff the object's ** attributes do not contain any set bits that are not also set in the master ** attributes. */ PRIVATE_CODE BOOL AttributesMatch(DWORD dwMasterAttributes, DWORD dwObjectAttributes) { // We don't consider a difference in compression to be enough to call
// the file different, especially since that attribute is impossible
// to reconcile in some cases.
dwObjectAttributes &= ~(FILE_ATTRIBUTE_COMPRESSED);
return(! (dwObjectAttributes & (~dwMasterAttributes))); }
/*
** PrepareForFolderTwinExpansion() ** ** ** ** Arguments: ** ** Returns: void ** ** Side Effects: none ** ** N.b., this function should be called before the outermost call to ** MyExpandIntersectingFolderTwins(). */ PRIVATE_CODE void PrepareForFolderTwinExpansion(HBRFCASE hbr) { ASSERT(IS_VALID_HANDLE(hbr, BRFCASE));
ClearFlagInArrayOfStubs(GetBriefcaseFolderPairPtrArray(hbr), STUB_FL_USED);
EVAL(EnumObjectTwins(hbr, (ENUMGENERATEDOBJECTTWINSPROC)&ClearStubFlagWrapper, IntToPtr(STUB_FL_FILE_STAMP_VALID)));
return; }
/*
** MyExpandIntersectingFolderTwins() ** ** Expands all folder twins intersecting a pair of folder twins. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: Marks expanded folder pairs used. ** ** N.b., PrepareForFolderTwinExpansion(pfp->pfpd->hbr) should be called before ** the first time this function is called. */ PRIVATE_CODE TWINRESULT MyExpandIntersectingFolderTwins(PFOLDERPAIR pfp, HCLSIFACECACHE hcic, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
/*
* N.b., pfp may already be marked used here, but may intersect folder twins * that have not yet been expanded. */
tr = HalfExpandIntersectingFolderTwins(pfp, hcic, crlp, lpCallbackData);
if (tr == TR_SUCCESS) { ASSERT(IsStubFlagSet(&(pfp->stub), STUB_FL_USED));
tr = HalfExpandIntersectingFolderTwins(pfp->pfpOther, hcic, crlp, lpCallbackData); }
return(tr); }
/*
** HalfExpandIntersectingFolderTwins() ** ** Expands all folder twins intersecting one half of a pair of folder twins. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: Marks expanded folder pairs used. ** ** N.b., this function is only meant to be called from ** MyExpandIntersectingFolderTwins(). */ PRIVATE_CODE TWINRESULT HalfExpandIntersectingFolderTwins( PFOLDERPAIR pfp, HCLSIFACECACHE hcic, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr = TR_SUCCESS;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hcic, CLSIFACECACHE)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
if (IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED)) { BOOL bArgIsSubtree; HPTRARRAY hpaFolderPairs; ARRAYINDEX ai; ARRAYINDEX aicFolderPairs;
bArgIsSubtree = IsStubFlagSet(&(pfp->stub), STUB_FL_SUBTREE);
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr); aicFolderPairs = GetPtrCount(hpaFolderPairs);
for (ai = 0; ai < aicFolderPairs; ai++) { PFOLDERPAIR pfpCur;
pfpCur = (PFOLDERPAIR)GetPtr(hpaFolderPairs, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfpCur, CFOLDERPAIR));
if (IsStubFlagClear(&(pfpCur->stub), STUB_FL_USED) && NamesIntersect(GetString(pfp->pfpd->hsName), GetString(pfpCur->pfpd->hsName))) { BOOL bCurIsSubtree; BOOL bExpand = FALSE;
bCurIsSubtree = IsStubFlagSet(&(pfpCur->stub), STUB_FL_SUBTREE);
if (bCurIsSubtree) { if (bArgIsSubtree) bExpand = SubtreesIntersect(pfp->hpath, pfpCur->hpath); else bExpand = IsPathPrefix(pfp->hpath, pfpCur->hpath); } else { if (bArgIsSubtree) bExpand = IsPathPrefix(pfpCur->hpath, pfp->hpath); else bExpand = (ComparePaths(pfp->hpath, pfpCur->hpath) == CR_EQUAL); }
/* Expand folder twin and mark it used. */
if (bExpand) { /*
* Mark all generated object twins as non-existent or unavailable. * Expand available folder twin. */
if (IsPathVolumeAvailable(pfp->hpath)) { EVAL(EnumGeneratedObjectTwins(pfp, &SetObjectTwinFileStampCondition, IntToPtr(FS_COND_DOES_NOT_EXIST)));
if (bCurIsSubtree) tr = ExpandSubtreeTwin(pfpCur, hcic, crlp, lpCallbackData); else tr = ExpandFolderTwin(pfpCur, hcic, crlp, lpCallbackData);
if (tr != TR_SUCCESS) break; } else { EVAL(EnumGeneratedObjectTwins(pfp, &SetObjectTwinFileStampCondition, IntToPtr(FS_COND_UNAVAILABLE)));
WARNING_OUT((TEXT("HalfExpandIntersectingFolderTwins(): Unavailable folder %s skipped."), DebugGetPathString(pfp->hpath))); }
SetStubFlag(&(pfp->stub), STUB_FL_USED); } } } }
return(tr); }
#ifdef DEBUG
/*
** IsValidPCEXPANDSUBTREETWININFO() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCEXPANDSUBTREETWININFO(PCEXPANDSUBTREETWININFO pcesi) { /* lpCallbackData may be any value. */
return(IS_VALID_READ_PTR(pcesi, CEXPANDSUBTREETWININFO) && IS_VALID_STRUCT_PTR(pcesi->pfp, CFOLDERPAIR) && EVAL(pcesi->ucbSubtreeRootPathLen > 0) && IS_VALID_HANDLE(pcesi->hcic, CLSIFACECACHE) && IsValidTWINRESULT(pcesi->tr)); }
#endif
/****************************** Public Functions *****************************/
/*
** ExpandSubtree() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT ExpandSubtree(HPATH hpathRoot, EXPANDSUBTREEPROC esp, PVOID pvRefData) { TWINRESULT tr; PFINDSTATE pfs;
/* pvRefData may be any value. */
ASSERT(IS_VALID_HANDLE(hpathRoot, PATH)); ASSERT(IS_VALID_CODE_PTR(esp, EXPANDSUBTREEPROC));
ASSERT(IsPathVolumeAvailable(hpathRoot));
if (AllocateMemory(MAX_FOLDER_DEPTH * sizeof(pfs[0]), &pfs)) { /* Copy subtree root folder to beginning of search path buffer. */
TCHAR rgchSearchSpec[MAX_PATH_LEN]; LPTSTR pszPathSuffix; int iFind; LPTSTR pszStartOfSubPath; BOOL bFound; #ifdef DEBUG
/* Are we leaking WIN32_FIND_DATA structures? */ ULONG ulcOpenFinds = 0; #endif
rgchSearchSpec[0] = TEXT('\0'); GetPathRootString(hpathRoot, rgchSearchSpec, ARRAYSIZE(rgchSearchSpec)); pszPathSuffix = rgchSearchSpec + lstrlen(rgchSearchSpec); GetPathSuffixString(hpathRoot, pszPathSuffix);
pszStartOfSubPath = rgchSearchSpec + lstrlen(rgchSearchSpec);
TRACE_OUT((TEXT("ExpandSubtree(): Expanding subtree rooted at %s."), rgchSearchSpec));
/* Append *.* file specification. */
CatPath(rgchSearchSpec, STAR_DOT_STAR, ARRAYSIZE(rgchSearchSpec));
/* Begin search at subtree root. */
iFind = 0;
pfs[iFind].hff = FindFirstFile(rgchSearchSpec, &(pfs[iFind].wfd));
#ifdef DEBUG
if (pfs[iFind].hff != INVALID_HANDLE_VALUE) ulcOpenFinds++; #endif
bFound = (pfs[iFind].hff != INVALID_HANDLE_VALUE);
/* Rip off *.*. */
DeleteLastPathElement(pszPathSuffix);
/* Search subtree depth first. */
tr = TR_SUCCESS;
while (bFound && tr == TR_SUCCESS) { /* Did we find a directory to expand? */
if (IS_ATTR_DIR(pfs[iFind].wfd.dwFileAttributes)) { if (IsFolderToExpand(pfs[iFind].wfd.cFileName)) { /* Yes. Dive down into it. */
/* Append the new directory to the current search path. */
CatPath(rgchSearchSpec, pfs[iFind].wfd.cFileName, ARRAYSIZE(rgchSearchSpec));
TRACE_OUT((TEXT("ExpandSubtree(): Diving into subfolder %s."), rgchSearchSpec));
/* Append *.* file specification. */
CatPath(rgchSearchSpec, STAR_DOT_STAR, ARRAYSIZE(rgchSearchSpec));
/* Start search in the new directory. */
ASSERT(iFind < INT_MAX); iFind++; pfs[iFind].hff = FindFirstFile(rgchSearchSpec, &(pfs[iFind].wfd));
bFound = (pfs[iFind].hff != INVALID_HANDLE_VALUE);
#ifdef DEBUG
if (bFound) ulcOpenFinds++; #endif
/* Rip off *.*. */
DeleteLastPathElement(pszPathSuffix); } else /* Continue search in this directory. */ bFound = FindNextFile(pfs[iFind].hff, &(pfs[iFind].wfd)); } else { /* Found a file. */
TRACE_OUT((TEXT("ExpandSubtree(): Found file %s\\%s."), rgchSearchSpec, pfs[iFind].wfd.cFileName));
if ((*esp)(rgchSearchSpec, &(pfs[iFind].wfd), pvRefData)) bFound = FindNextFile(pfs[iFind].hff, &(pfs[iFind].wfd)); else tr = TR_ABORT; }
if (tr == TR_SUCCESS) { while (! bFound) { /* Find failed. Climb back up one directory level. */
if (pfs[iFind].hff != INVALID_HANDLE_VALUE) { FindClose(pfs[iFind].hff); #ifdef DEBUG
ulcOpenFinds--; #endif
}
if (iFind > 0) { DeleteLastPathElement(pszPathSuffix); iFind--;
if (IsFolderToExpand(pfs[iFind].wfd.cFileName)) { TRACE_OUT((TEXT("ExpandSubtree(): Found folder %s\\%s."), rgchSearchSpec, pfs[iFind].wfd.cFileName));
if (! (*esp)(rgchSearchSpec, &(pfs[iFind].wfd), pvRefData)) { tr = TR_ABORT; break; } }
bFound = FindNextFile(pfs[iFind].hff, &(pfs[iFind].wfd)); } else { ASSERT(! iFind); break; } } } }
if (tr != TR_SUCCESS) { /* Close all open find operations on failure. */
while (iFind >= 0) { if (pfs[iFind].hff != INVALID_HANDLE_VALUE) { FindClose(pfs[iFind].hff); iFind--; #ifdef DEBUG
ulcOpenFinds--; #endif
} } }
ASSERT(! ulcOpenFinds);
FreeMemory(pfs);
TRACE_OUT((TEXT("ExpandSubtree(): Subtree expansion complete."))); } else tr = TR_OUT_OF_MEMORY;
return(tr); }
/*
** ClearStubFlagWrapper() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL ClearStubFlagWrapper(PSTUB pstub, PVOID dwFlags) { ASSERT(IS_VALID_STRUCT_PTR(pstub, CSTUB)); ASSERT(FLAGS_ARE_VALID(PtrToUlong(dwFlags), ALL_STUB_FLAGS));
ClearStubFlag(pstub, PtrToUlong(dwFlags));
return(TRUE); }
/*
** SetStubFlagWrapper() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL SetStubFlagWrapper(PSTUB pstub, PVOID dwFlags) { ASSERT(IS_VALID_STRUCT_PTR(pstub, CSTUB)); ASSERT(FLAGS_ARE_VALID(PtrToUlong(dwFlags), ALL_STUB_FLAGS));
SetStubFlag(pstub, PtrToUlong(dwFlags));
return(TRUE); }
/*
** ExpandIntersectingFolderTwins() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: Leaves only the folder pairs expanded marked used. */ PUBLIC_CODE TWINRESULT ExpandIntersectingFolderTwins(PFOLDERPAIR pfp, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr; HCLSIFACECACHE hcic;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED));
if (CreateClassInterfaceCache(&hcic)) { /* Prepare for call to MyExpandIntersectingFolderTwins(). */
PrepareForFolderTwinExpansion(pfp->pfpd->hbr);
tr = MyExpandIntersectingFolderTwins(pfp, hcic, crlp, lpCallbackData);
DestroyClassInterfaceCache(hcic); } else tr = TR_OUT_OF_MEMORY;
return(tr); }
/*
** ExpandFolderTwinsIntersectingTwinList() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: Leaves only the folder pairs expanded marked used. */ PUBLIC_CODE TWINRESULT ExpandFolderTwinsIntersectingTwinList( HTWINLIST htl, CREATERECLISTPROC crlp, LPARAM lpCallbackData) { TWINRESULT tr; HCLSIFACECACHE hcic;
/* lpCallbackData may be any value. */
ASSERT(IS_VALID_HANDLE(htl, TWINLIST)); ASSERT(! crlp || IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
if (CreateClassInterfaceCache(&hcic)) { ARRAYINDEX aicTwins; ARRAYINDEX ai;
tr = TR_SUCCESS;
/* Prepare for calls to MyExpandIntersectingFolderTwins(). */
PrepareForFolderTwinExpansion(GetTwinListBriefcase(htl));
aicTwins = GetTwinListCount(htl);
for (ai = 0; ai < aicTwins; ai++) { HTWIN htwin;
htwin = GetTwinFromTwinList(htl, ai);
/* Expand only live folder twins. */
if (((PCSTUB)htwin)->st == ST_FOLDERPAIR) { tr = MyExpandIntersectingFolderTwins((PFOLDERPAIR)htwin, hcic, crlp, lpCallbackData);
if (tr != TR_SUCCESS) break; } }
DestroyClassInterfaceCache(hcic); } else tr = TR_OUT_OF_MEMORY;
return(tr); }
/*
** TryToGenerateObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT TryToGenerateObjectTwin(HBRFCASE hbr, HPATH hpathFolder, LPCTSTR pcszName, PBOOL pbGenerated, POBJECTTWIN *ppot) { TWINRESULT tr; HCLSIFACECACHE hcic;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR)); ASSERT(IS_VALID_WRITE_PTR(pbGenerated, BOOL)); ASSERT(IS_VALID_WRITE_PTR(ppot, POBJECTTWIN));
if (CreateClassInterfaceCache(&hcic)) { HPTRARRAY hpaFolderPairs; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
tr = TR_SUCCESS; *pbGenerated = FALSE;
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(hbr);
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++) { PCFOLDERPAIR pcfp;
pcfp = GetPtr(hpaFolderPairs, ai);
if (FolderTwinGeneratesObjectTwin(pcfp, hpathFolder, pcszName)) { TCHAR rgchSubPath[MAX_PATH_LEN]; LPCTSTR pcszSubPath; POBJECTTWIN potOther;
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE)) pcszSubPath = FindChildPathSuffix(pcfp->hpath, hpathFolder, rgchSubPath); else pcszSubPath = EMPTY_STRING;
tr = FakeObjectTwinFromFolderTwin(pcfp, pcszSubPath, pcszName, hcic, ppot, &potOther);
if (tr == TR_SUCCESS) *pbGenerated = TRUE; else ASSERT(tr != TR_DUPLICATE_TWIN);
break; } }
DestroyClassInterfaceCache(hcic); } else tr = TR_OUT_OF_MEMORY;
ASSERT(tr != TR_SUCCESS || ! *pbGenerated || IS_VALID_STRUCT_PTR(*ppot, COBJECTTWIN));
return(tr); }
/*
** NamesIntersect() ** ** Determines whether or not two names may refer to the same object. Both ** names may contain wildcards ('*' or '?'). ** ** Arguments: pcszName1 - first name ** pcszName2 - second name ** ** Returns: TRUE if the two names intersect. FALSE if not. ** ** Side Effects: none ** ** A "name" is broken up into two components: a "base" and an optional ** "extension", e.g., "BASE" or "BASE.EXT". ** ** "Intersecting names" are defined as follows: ** ** 1) An asterisk matches 0 or more characters in the base or extension. ** 2) Any characters after an asterisk in the base or extension are ignored. ** 3) A question mark matches exactly one character, or no character if it ** appears at the end of the base or extension. ** ** N.b., this function does not perform any checking on the validity of the two ** names. */ PUBLIC_CODE BOOL NamesIntersect(LPCTSTR pcszName1, LPCTSTR pcszName2) { BOOL bIntersect = FALSE;
ASSERT(IS_VALID_STRING_PTR(pcszName1, CSTR)); ASSERT(IS_VALID_STRING_PTR(pcszName2, CSTR));
if (NameComponentsIntersect(pcszName1, pcszName2)) { LPCTSTR pcszExt1; LPCTSTR pcszExt2;
/* Get extensions, skipping leading periods. */
pcszExt1 = ExtractExtension(pcszName1); if (*pcszExt1 == PERIOD) pcszExt1 = CharNext(pcszExt1);
pcszExt2 = ExtractExtension(pcszName2); if (*pcszExt2 == PERIOD) pcszExt2 = CharNext(pcszExt2);
bIntersect = NameComponentsIntersect(pcszExt1, pcszExt2); }
return(bIntersect); }
#ifdef DEBUG
/*
** IsValidTWINRESULT() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidTWINRESULT(TWINRESULT tr) { BOOL bResult;
switch (tr) { case TR_SUCCESS: case TR_RH_LOAD_FAILED: case TR_SRC_OPEN_FAILED: case TR_SRC_READ_FAILED: case TR_DEST_OPEN_FAILED: case TR_DEST_WRITE_FAILED: case TR_ABORT: case TR_UNAVAILABLE_VOLUME: case TR_OUT_OF_MEMORY: case TR_FILE_CHANGED: case TR_DUPLICATE_TWIN: case TR_DELETED_TWIN: case TR_HAS_FOLDER_TWIN_SRC: case TR_INVALID_PARAMETER: case TR_REENTERED: case TR_SAME_FOLDER: case TR_SUBTREE_CYCLE_FOUND: case TR_NO_MERGE_HANDLER: case TR_MERGE_INCOMPLETE: case TR_TOO_DIFFERENT: case TR_BRIEFCASE_LOCKED: case TR_BRIEFCASE_OPEN_FAILED: case TR_BRIEFCASE_READ_FAILED: case TR_BRIEFCASE_WRITE_FAILED: case TR_CORRUPT_BRIEFCASE: case TR_NEWER_BRIEFCASE: case TR_NO_MORE: bResult = TRUE; break;
default: bResult = FALSE; ERROR_OUT((TEXT("IsValidTWINRESULT(): Invalid TWINRESULT %d."), tr)); break; }
return(bResult); }
#endif
|