|
|
/*
* foldtwin.c - Folder twin ADT module. */
/* Headers
**********/
#include "project.h"
#pragma hdrstop
#include "stub.h"
#include "subcycle.h"
/* Constants
************/
/* pointer array allocation constants */
#define NUM_START_FOLDER_TWIN_PTRS (16)
#define NUM_FOLDER_TWIN_PTRS_TO_ADD (16)
/* Types
********/
/* internal new folder twin description */
typedef struct _inewfoldertwin { HPATH hpathFirst; HPATH hpathSecond; HSTRING hsName; DWORD dwAttributes; HBRFCASE hbr; DWORD dwFlags; } INEWFOLDERTWIN; DECLARE_STANDARD_TYPES(INEWFOLDERTWIN);
/* database folder twin list header */
typedef struct _dbfoldertwinlistheader { LONG lcFolderPairs; } DBFOLDERTWINLISTHEADER; DECLARE_STANDARD_TYPES(DBFOLDERTWINLISTHEADER);
/* database folder twin structure */
typedef struct _dbfoldertwin { /* shared stub flags */
DWORD dwStubFlags;
/* old handle to first folder path */
HPATH hpath1;
/* old handle to second folder path */
HPATH hpath2;
/* old handle to name string */
HSTRING hsName;
/* attributes to match */
DWORD dwAttributes; } DBFOLDERTWIN; DECLARE_STANDARD_TYPES(DBFOLDERTWIN);
/***************************** Private Functions *****************************/
/* Module Prototypes
********************/
PRIVATE_CODE TWINRESULT MakeINewFolderTwin(HBRFCASE, PCNEWFOLDERTWIN, PINEWFOLDERTWIN); PRIVATE_CODE void ReleaseINewFolderTwin(PINEWFOLDERTWIN); PRIVATE_CODE TWINRESULT TwinFolders(PCINEWFOLDERTWIN, PFOLDERPAIR *); PRIVATE_CODE BOOL FindFolderPair(PCINEWFOLDERTWIN, PFOLDERPAIR *); PRIVATE_CODE BOOL CreateFolderPair(PCINEWFOLDERTWIN, PFOLDERPAIR *); PRIVATE_CODE BOOL CreateHalfOfFolderPair(HPATH, HBRFCASE, PFOLDERPAIR *); PRIVATE_CODE void DestroyHalfOfFolderPair(PFOLDERPAIR); PRIVATE_CODE BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN, PFOLDERPAIRDATA *); PRIVATE_CODE void DestroySharedFolderPairData(PFOLDERPAIRDATA); PRIVATE_CODE COMPARISONRESULT FolderPairSortCmp(PCVOID, PCVOID); PRIVATE_CODE COMPARISONRESULT FolderPairSearchCmp(PCVOID, PCVOID); PRIVATE_CODE BOOL RemoveSourceFolderTwin(POBJECTTWIN, PVOID); PRIVATE_CODE void UnlinkHalfOfFolderPair(PFOLDERPAIR); PRIVATE_CODE BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR, HPATH); PRIVATE_CODE TWINRESULT CreateListOfFolderTwins(HBRFCASE, ARRAYINDEX, HPATH, PFOLDERTWIN *, PARRAYINDEX); PRIVATE_CODE void DestroyListOfFolderTwins(PFOLDERTWIN); PRIVATE_CODE TWINRESULT AddFolderTwinToList(PFOLDERPAIR, PFOLDERTWIN, PFOLDERTWIN *); PRIVATE_CODE TWINRESULT TransplantFolderPair(PFOLDERPAIR, HPATH, HPATH); PRIVATE_CODE TWINRESULT WriteFolderPair(HCACHEDFILE, PFOLDERPAIR); PRIVATE_CODE TWINRESULT ReadFolderPair(HCACHEDFILE, HBRFCASE, HHANDLETRANS, HHANDLETRANS);
#ifdef VSTF
PRIVATE_CODE BOOL IsValidPCNEWFOLDERTWIN(PCNEWFOLDERTWIN); PRIVATE_CODE BOOL IsValidPCFOLDERTWINLIST(PCFOLDERTWINLIST); PRIVATE_CODE BOOL IsValidPCFOLDERTWIN(PCFOLDERTWIN); PRIVATE_CODE BOOL IsValidFolderPairHalf(PCFOLDERPAIR); PRIVATE_CODE BOOL IsValidPCFOLDERPAIRDATA(PCFOLDERPAIRDATA);
#endif
#ifdef DEBUG
PRIVATE_CODE BOOL IsValidPCINEWFOLDERTWIN(PCINEWFOLDERTWIN); PRIVATE_CODE BOOL AreFolderPairsValid(HPTRARRAY);
#endif
/*
** MakeINewFolderTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT MakeINewFolderTwin(HBRFCASE hbr, PCNEWFOLDERTWIN pcnftSrc, PINEWFOLDERTWIN pinftDest) { TWINRESULT tr;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_STRUCT_PTR(pcnftSrc, CNEWFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(pinftDest, CINEWFOLDERTWIN));
if (AddString(pcnftSrc->pcszName, GetBriefcaseNameStringTable(hbr), GetHashBucketIndex, &(pinftDest->hsName))) { HPATHLIST hpl;
hpl = GetBriefcasePathList(hbr);
tr = TranslatePATHRESULTToTWINRESULT( AddPath(hpl, pcnftSrc->pcszFolder1, &(pinftDest->hpathFirst)));
if (tr == TR_SUCCESS) { tr = TranslatePATHRESULTToTWINRESULT( AddPath(hpl, pcnftSrc->pcszFolder2, &(pinftDest->hpathSecond)));
if (tr == TR_SUCCESS) { pinftDest->dwAttributes = pcnftSrc->dwAttributes; pinftDest->dwFlags = pcnftSrc->dwFlags; pinftDest->hbr = hbr; } else { DeletePath(pinftDest->hpathFirst); MAKEINEWFOLDERTWIN_BAIL: DeleteString(pinftDest->hsName); } } else goto MAKEINEWFOLDERTWIN_BAIL; } else tr = TR_OUT_OF_MEMORY;
ASSERT(tr != TR_SUCCESS || IS_VALID_STRUCT_PTR(pinftDest, CINEWFOLDERTWIN));
return(tr); }
/*
** ReleaseINewFolderTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void ReleaseINewFolderTwin(PINEWFOLDERTWIN pinft) { ASSERT(IS_VALID_STRUCT_PTR(pinft, CINEWFOLDERTWIN));
DeletePath(pinft->hpathFirst); DeletePath(pinft->hpathSecond); DeleteString(pinft->hsName);
return; }
/*
** TwinFolders() ** ** Twins two folders. ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT TwinFolders(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp) { PFOLDERPAIR pfp; TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Are the two folders the same? */
if (ComparePaths(pcinft->hpathFirst, pcinft->hpathSecond) != CR_EQUAL) { /* Look for the two folders in existing folder pairs. */
if (FindFolderPair(pcinft, &pfp)) { /* Found a existing matching folder pair. Complain. */
*ppfp = pfp;
tr = TR_DUPLICATE_TWIN; } else { /*
* No existing matching folder pairs found. Only allowing twinning to * paths whose roots are available. */
if (IsPathVolumeAvailable(pcinft->hpathFirst) && IsPathVolumeAvailable(pcinft->hpathSecond)) { /*
* If this is a new folder subtree pair, check to see if it would * create a cycle. */
if (IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE)) tr = CheckForSubtreeCycles( GetBriefcaseFolderPairPtrArray(pcinft->hbr), pcinft->hpathFirst, pcinft->hpathSecond, pcinft->hsName); else tr = TR_SUCCESS;
if (tr == TR_SUCCESS) { if (CreateFolderPair(pcinft, &pfp)) { *ppfp = pfp;
TRACE_OUT((TEXT("TwinFolders(): Creating %s twin pair %s and %s, files %s."), IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE) ? TEXT("subtree") : TEXT("folder"), DebugGetPathString(pcinft->hpathFirst), DebugGetPathString(pcinft->hpathSecond), GetString(pcinft->hsName))); } else tr = TR_OUT_OF_MEMORY; } } else tr = TR_UNAVAILABLE_VOLUME; } } else tr = TR_SAME_FOLDER;
return(tr); }
/*
** FindFolderPair() ** ** Looks for a folder pair matching the given description. ** ** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair to ** search for ** ** Returns: Pointer to PFOLDERPAIR if found. NULL if not found. ** ** Side Effects: none */ PRIVATE_CODE BOOL FindFolderPair(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp) { ARRAYINDEX aiFirst;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/*
* Search all folder pairs containing the first folder. Then scan all these * folder pairs for the second folder. */
*ppfp = NULL;
if (SearchSortedArray(GetBriefcaseFolderPairPtrArray(pcinft->hbr), &FolderPairSearchCmp, pcinft->hpathFirst, &aiFirst)) { ARRAYINDEX aicPtrs; HPTRARRAY hpaFolderPairs; LONG ai; PFOLDERPAIR pfp;
/*
* aiFirst holds the index of the first folder pair that * contains the first folder name. */
/*
* Now search each of these folder pairs for all paired folders * using the second folder name. */
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcinft->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(aicPtrs > 0); ASSERT(! (aicPtrs % 2)); ASSERT(aiFirst >= 0); ASSERT(aiFirst < aicPtrs);
for (ai = aiFirst; ai < aicPtrs; ai++) { pfp = GetPtr(hpaFolderPairs, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Does this folder pair match the proposed folder pair? */
if (ComparePaths(pfp->hpath, pcinft->hpathFirst) == CR_EQUAL) { /*
* An existing pair of folder twins is considered the same as a * proposed new pair of folder twins when the two pairs of folder * twins share the same: * 1) pair of PATHs * 2) name specification * 3) file attributes * 4) subtree flag setting */
if (ComparePaths(pfp->pfpOther->hpath, pcinft->hpathSecond) == CR_EQUAL && CompareNameStringsByHandle(pfp->pfpd->hsName, pcinft->hsName) == CR_EQUAL && pfp->pfpd->dwAttributes == pcinft->dwAttributes && ((IS_FLAG_SET(pfp->stub.dwFlags, STUB_FL_SUBTREE) && IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE)) || (IS_FLAG_CLEAR(pfp->stub.dwFlags, STUB_FL_SUBTREE) && IS_FLAG_CLEAR(pcinft->dwFlags, NFT_FL_SUBTREE)))) { /* Yes. */
*ppfp = pfp; break; } } else break; } }
return(*ppfp != NULL); }
/*
** CreateFolderPair() ** ** Creates a new folder pair, and adds them to a briefcase's list of folder ** pairs. ** ** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair to ** create ** ppfp - pointer to PFOLDERPAIR to be filled in with pointer to ** half of new folder pair representing ** pcnft->pcszFolder1 ** ** Returns: ** ** Side Effects: Adds the new folder pair to the global array of folder pairs. ** ** N.b., this function does not first check to see if the folder pair already ** exists in the global list of folder pairs. */ PRIVATE_CODE BOOL CreateFolderPair(PCINEWFOLDERTWIN pcinft, PFOLDERPAIR *ppfp) { BOOL bResult = FALSE; PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Try to create the shared folder data structure. */
if (CreateSharedFolderPairData(pcinft, &pfpd)) { PFOLDERPAIR pfpNew1; BOOL bPtr1Loose = TRUE;
if (CreateHalfOfFolderPair(pcinft->hpathFirst, pcinft->hbr, &pfpNew1)) { PFOLDERPAIR pfpNew2;
if (CreateHalfOfFolderPair(pcinft->hpathSecond, pcinft->hbr, &pfpNew2)) { HPTRARRAY hpaFolderPairs; ARRAYINDEX ai1;
/* Combine the two folder pair halves. */
pfpNew1->pfpd = pfpd; pfpNew1->pfpOther = pfpNew2;
pfpNew2->pfpd = pfpd; pfpNew2->pfpOther = pfpNew1;
/* Set flags. */
if (IS_FLAG_SET(pcinft->dwFlags, NFT_FL_SUBTREE)) { SetStubFlag(&(pfpNew1->stub), STUB_FL_SUBTREE); SetStubFlag(&(pfpNew2->stub), STUB_FL_SUBTREE); }
/*
* Try to add the two folder pairs to the global list of folder * pairs. */
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcinft->hbr);
if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew1, &ai1)) { ARRAYINDEX ai2;
bPtr1Loose = FALSE;
if (AddPtr(hpaFolderPairs, FolderPairSortCmp, pfpNew2, &ai2)) { ASSERT(IS_VALID_STRUCT_PTR(pfpNew1, CFOLDERPAIR)); ASSERT(IS_VALID_STRUCT_PTR(pfpNew2, CFOLDERPAIR));
if (ApplyNewFolderTwinsToTwinFamilies(pfpNew1)) { *ppfp = pfpNew1; bResult = TRUE; } else { DeletePtr(hpaFolderPairs, ai2);
CREATEFOLDERPAIR_BAIL1: DeletePtr(hpaFolderPairs, ai1);
CREATEFOLDERPAIR_BAIL2: /*
* Don't try to remove pfpNew2 from the global list of * folder pairs here since it was never added * successfully. */ DestroyHalfOfFolderPair(pfpNew2);
CREATEFOLDERPAIR_BAIL3: /*
* Don't try to remove pfpNew1 from the global list of * folder pairs here since it was never added * successfully. */ DestroyHalfOfFolderPair(pfpNew1);
CREATEFOLDERPAIR_BAIL4: DestroySharedFolderPairData(pfpd); } } else goto CREATEFOLDERPAIR_BAIL1; } else goto CREATEFOLDERPAIR_BAIL2; } else goto CREATEFOLDERPAIR_BAIL3; } else goto CREATEFOLDERPAIR_BAIL4; }
return(bResult); }
/*
** CreateHalfOfFolderPair() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL CreateHalfOfFolderPair(HPATH hpathFolder, HBRFCASE hbr, PFOLDERPAIR *ppfp) { BOOL bResult = FALSE; PFOLDERPAIR pfpNew;
ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_WRITE_PTR(ppfp, PFOLDERPAIR));
/* Try to create a new FOLDERPAIR structure. */
if (AllocateMemory(sizeof(*pfpNew), &pfpNew)) { /* Try to add the folder string to the folder string table. */
if (CopyPath(hpathFolder, GetBriefcasePathList(hbr), &(pfpNew->hpath))) { /* Fill in the fields of the new FOLDERPAIR structure. */
InitStub(&(pfpNew->stub), ST_FOLDERPAIR);
*ppfp = pfpNew; bResult = TRUE; } else FreeMemory(pfpNew); }
return(bResult); }
/*
** DestroyHalfOfFolderPair() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE void DestroyHalfOfFolderPair(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
TRACE_OUT((TEXT("DestroyHalfOfFolderPair(): Destroying folder twin %s."), DebugGetPathString(pfp->hpath)));
/* Has the other half of this folder pair already been destroyed? */
if (IsStubFlagClear(&(pfp->stub), STUB_FL_BEING_DELETED)) /* No. Indicate that this half has already been deleted. */ SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_BEING_DELETED);
/* Destroy FOLDERPAIR fields. */
DeletePath(pfp->hpath); FreeMemory(pfp);
return; }
/*
** CreateSharedFolderPairData() ** ** Creates a shared folder pair data structure. ** ** Arguments: pcinft - pointer to INEWFOLDERTWIN describing folder pair ** being created ** ** Returns: Pointer to new folder pair data structure if successful. ** NULL if unsuccessful. ** ** Side Effects: none */ PRIVATE_CODE BOOL CreateSharedFolderPairData(PCINEWFOLDERTWIN pcinft, PFOLDERPAIRDATA *ppfpd) { PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pcinft, CINEWFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppfpd, PFOLDERPAIRDATA));
/* Try to allocate a new shared folder pair data data structure. */
*ppfpd = NULL;
if (AllocateMemory(sizeof(*pfpd), &pfpd)) { /* Fill in the FOLDERPAIRDATA structure fields. */
LockString(pcinft->hsName); pfpd->hsName = pcinft->hsName;
pfpd->dwAttributes = pcinft->dwAttributes; pfpd->hbr = pcinft->hbr;
ASSERT(! IS_ATTR_DIR(pfpd->dwAttributes));
CLEAR_FLAG(pfpd->dwAttributes, FILE_ATTRIBUTE_DIRECTORY);
*ppfpd = pfpd;
ASSERT(IS_VALID_STRUCT_PTR(*ppfpd, CFOLDERPAIRDATA)); }
return(*ppfpd != NULL); }
/*
** DestroySharedFolderPairData() ** ** Destroys shared folder pair data. ** ** Arguments: pfpd - pointer to shared folder pair data to destroy ** ** Returns: void ** ** Side Effects: none */ PRIVATE_CODE void DestroySharedFolderPairData(PFOLDERPAIRDATA pfpd) { ASSERT(IS_VALID_STRUCT_PTR(pfpd, CFOLDERPAIRDATA));
/* Destroy FOLDERPAIRDATA fields. */
DeleteString(pfpd->hsName); FreeMemory(pfpd);
return; }
/*
** FolderPairSortCmp() ** ** Pointer comparison function used to sort the global array of folder pairs. ** ** Arguments: pcfp1 - pointer to FOLDERPAIR describing first folder pair ** pcfp2 - pointer to FOLDERPAIR describing second folder pair ** ** Returns: ** ** Side Effects: none ** ** Folder pairs are sorted by: ** 1) path ** 2) pointer value */ PRIVATE_CODE COMPARISONRESULT FolderPairSortCmp(PCVOID pcfp1, PCVOID pcfp2) { COMPARISONRESULT cr;
ASSERT(IS_VALID_STRUCT_PTR(pcfp1, CFOLDERPAIR)); ASSERT(IS_VALID_STRUCT_PTR(pcfp2, CFOLDERPAIR));
cr = ComparePaths(((PCFOLDERPAIR)pcfp1)->hpath, ((PCFOLDERPAIR)pcfp2)->hpath);
if (cr == CR_EQUAL) cr = ComparePointers(pcfp1, pcfp2);
return(cr); }
/*
** FolderPairSearchCmp() ** ** Pointer comparison function used to search the global array of folder pairs ** for the first folder pair for a given folder. ** ** Arguments: hpath - folder pair to search for ** pcfp - pointer to FOLDERPAIR to examine ** ** Returns: ** ** Side Effects: none ** ** Folder pairs are searched by: ** 1) path */ PRIVATE_CODE COMPARISONRESULT FolderPairSearchCmp(PCVOID hpath, PCVOID pcfp) { ASSERT(IS_VALID_HANDLE((HPATH)hpath, PATH)); ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR));
return(ComparePaths((HPATH)hpath, ((PCFOLDERPAIR)pcfp)->hpath)); }
/*
** RemoveSourceFolderTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */
#pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
PRIVATE_CODE BOOL RemoveSourceFolderTwin(POBJECTTWIN pot, PVOID pv) { ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN)); ASSERT(! pv);
if (EVAL(pot->ulcSrcFolderTwins > 0)) pot->ulcSrcFolderTwins--;
/*
* If there are no more source folder twins for this object twin, and this * object twin is not a separate "orphan" object twin, wipe it out. */
if (! pot->ulcSrcFolderTwins && IsStubFlagClear(&(pot->stub), STUB_FL_FROM_OBJECT_TWIN)) EVAL(DestroyStub(&(pot->stub)) == TR_SUCCESS);
return(TRUE); }
#pragma warning(default:4100) /* "unreferenced formal parameter" warning */
/*
** UnlinkHalfOfFolderPair() ** ** Unlinks one half of a pair of folder twins. ** ** Arguments: pfp - pointer to folder pair half to unlink ** ** Returns: void ** ** Side Effects: Removes a source folder twin from each of the object twin's ** in the folder pair's list of generated object twins. May ** cause object twins and twin families to be destroyed. */ PRIVATE_CODE void UnlinkHalfOfFolderPair(PFOLDERPAIR pfp) { HPTRARRAY hpaFolderPairs; ARRAYINDEX aiUnlink;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
TRACE_OUT((TEXT("UnlinkHalfOfFolderPair(): Unlinking folder twin %s."), DebugGetPathString(pfp->hpath)));
/* Search for the folder pair to be unlinked. */
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr);
if (EVAL(SearchSortedArray(hpaFolderPairs, &FolderPairSortCmp, pfp, &aiUnlink))) { /* Unlink folder pair. */
ASSERT(GetPtr(hpaFolderPairs, aiUnlink) == pfp);
DeletePtr(hpaFolderPairs, aiUnlink);
/*
* Don't mark folder pair stub unlinked here. Let caller do that after * both folder pair halves have been unlinked. */
/* Remove a source folder twin from all generated object twins. */
EVAL(EnumGeneratedObjectTwins(pfp, &RemoveSourceFolderTwin, NULL)); }
return; }
/*
** FolderTwinIntersectsFolder() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL FolderTwinIntersectsFolder(PCFOLDERPAIR pcfp, HPATH hpathFolder) { BOOL bResult;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH));
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE)) bResult = IsPathPrefix(hpathFolder, pcfp->hpath); else bResult = (ComparePaths(hpathFolder, pcfp->hpath) == CR_EQUAL);
return(bResult); }
/*
** CreateListOfFolderTwins() ** ** Creates a list of folder twins from a block of folder pairs. ** ** Arguments: aiFirst - index of first folder pair in the array of folder ** pairs ** hpathFolder - folder that list of folder twins is to be ** created for ** ppftHead - pointer to PFOLDERTWIN to be filled in with ** pointer to first folder twin in new list ** paic - pointer to ARRAYINDEX to be filled in with number of ** folder twins in new list ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT CreateListOfFolderTwins(HBRFCASE hbr, ARRAYINDEX aiFirst, HPATH hpathFolder, PFOLDERTWIN *ppftHead, PARRAYINDEX paic) { TWINRESULT tr; PFOLDERPAIR pfp; HPATH hpath; ARRAYINDEX aicPtrs; ARRAYINDEX ai; PFOLDERTWIN pftHead; HPTRARRAY hpaFolderTwins;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_WRITE_PTR(ppftHead, PFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(paic, ARRAYINDEX));
/*
* Get the handle to the common folder that the list of folder twins is * being prepared for. */
hpaFolderTwins = GetBriefcaseFolderPairPtrArray(hbr);
pfp = GetPtr(hpaFolderTwins, aiFirst);
hpath = pfp->hpath;
/*
* Add the other half of each matching folder pair to the folder twin list * as a folder twin. */
aicPtrs = GetPtrCount(hpaFolderTwins); ASSERT(aicPtrs > 0); ASSERT(! (aicPtrs % 2)); ASSERT(aiFirst >= 0); ASSERT(aiFirst < aicPtrs);
/* Start with an empty list of folder twins. */
pftHead = NULL;
/*
* A pointer to the first folder pair is already in pfp, but we'll look it * up again. */
TRACE_OUT((TEXT("CreateListOfFolderTwins(): Creating list of folder twins of folder %s."), DebugGetPathString(hpath)));
tr = TR_SUCCESS;
for (ai = aiFirst; ai < aicPtrs && tr == TR_SUCCESS; ai++) { pfp = GetPtr(hpaFolderTwins, ai);
if (ComparePaths(pfp->hpath, hpathFolder) == CR_EQUAL) tr = AddFolderTwinToList(pfp, pftHead, &pftHead); else break; }
TRACE_OUT((TEXT("CreateListOfFolderTwins(): Finished creating list of folder twins of folder %s."), DebugGetPathString(hpath)));
if (tr == TR_SUCCESS) { /* Success! Fill in the result parameters. */
*ppftHead = pftHead; *paic = ai - aiFirst; } else /* Free any folder twins that have been added to the list. */ DestroyListOfFolderTwins(pftHead);
return(tr); }
/*
** DestroyListOfFolderTwins() ** ** Wipes out the folder twins in a folder twin list. ** ** Arguments: pftHead - pointer to first folder twin in list ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE void DestroyListOfFolderTwins(PFOLDERTWIN pftHead) { while (pftHead) { PFOLDERTWIN pftOldHead;
ASSERT(IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN));
UnlockStub(&(((PFOLDERPAIR)(pftHead->hftSrc))->stub)); UnlockStub(&(((PFOLDERPAIR)(pftHead->hftOther))->stub));
pftOldHead = pftHead; pftHead = (PFOLDERTWIN)(pftHead->pcftNext);
FreeMemory((LPTSTR)(pftOldHead->pcszSrcFolder)); FreeMemory((LPTSTR)(pftOldHead->pcszOtherFolder));
FreeMemory(pftOldHead); }
return; }
/*
** AddFolderTwinToList() ** ** Adds a folder twin to a list of folder twins. ** ** Arguments: pfpSrc - pointer to source folder pair to be added ** pftHead - pointer to head of folder twin list, may be NULL ** ppft - pointer to PFOLDERTWIN to be filled in with pointer ** to new folder twin, ppft may be &pftHead ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT AddFolderTwinToList(PFOLDERPAIR pfpSrc, PFOLDERTWIN pftHead, PFOLDERTWIN *ppft) { TWINRESULT tr = TR_OUT_OF_MEMORY; PFOLDERTWIN pftNew;
ASSERT(IS_VALID_STRUCT_PTR(pfpSrc, CFOLDERPAIR)); ASSERT(! pftHead || IS_VALID_STRUCT_PTR(pftHead, CFOLDERTWIN)); ASSERT(IS_VALID_WRITE_PTR(ppft, PFOLDERTWIN));
/* Try to create a new FOLDERTWIN structure. */
if (AllocateMemory(sizeof(*pftNew), &pftNew)) { LPTSTR pszFirstFolder;
if (AllocatePathString(pfpSrc->hpath, &pszFirstFolder)) { LPTSTR pszSecondFolder;
if (AllocatePathString(pfpSrc->pfpOther->hpath, &pszSecondFolder)) { /* Fill in FOLDERTWIN structure fields. */
pftNew->pcftNext = pftHead; pftNew->hftSrc = (HFOLDERTWIN)pfpSrc; pftNew->hvidSrc = (HVOLUMEID)(pfpSrc->hpath); pftNew->pcszSrcFolder = pszFirstFolder; pftNew->hftOther = (HFOLDERTWIN)(pfpSrc->pfpOther); pftNew->hvidOther = (HVOLUMEID)(pfpSrc->pfpOther->hpath); pftNew->pcszOtherFolder = pszSecondFolder; pftNew->pcszName = GetString(pfpSrc->pfpd->hsName);
pftNew->dwFlags = 0;
if (IsStubFlagSet(&(pfpSrc->stub), STUB_FL_SUBTREE)) pftNew->dwFlags = FT_FL_SUBTREE;
LockStub(&(pfpSrc->stub)); LockStub(&(pfpSrc->pfpOther->stub));
*ppft = pftNew; tr = TR_SUCCESS;
TRACE_OUT((TEXT("AddFolderTwinToList(): Added folder twin %s of folder %s matching objects %s."), pftNew->pcszSrcFolder, pftNew->pcszOtherFolder, pftNew->pcszName)); } else { FreeMemory(pszFirstFolder); ADDFOLDERTWINTOLIST_BAIL: FreeMemory(pftNew); } } else goto ADDFOLDERTWINTOLIST_BAIL; }
ASSERT(tr != TR_SUCCESS || IS_VALID_STRUCT_PTR(*ppft, CFOLDERTWIN));
return(tr); }
/*
** TransplantFolderPair() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT TransplantFolderPair(PFOLDERPAIR pfp, HPATH hpathOldFolder, HPATH hpathNewFolder) { TWINRESULT tr;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hpathOldFolder, PATH)); ASSERT(IS_VALID_HANDLE(hpathNewFolder, PATH));
/* Is this folder pair rooted in the renamed folder's subtree? */
if (IsPathPrefix(pfp->hpath, hpathOldFolder)) { TCHAR rgchPathSuffix[MAX_PATH_LEN]; LPCTSTR pcszSubPath; HPATH hpathNew;
/* Yes. Change the folder pair's root. */
pcszSubPath = FindChildPathSuffix(hpathOldFolder, pfp->hpath, rgchPathSuffix);
if (AddChildPath(GetBriefcasePathList(pfp->pfpd->hbr), hpathNewFolder, pcszSubPath, &hpathNew)) { if (IsStubFlagSet(&(pfp->stub), STUB_FL_SUBTREE)) { ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_SUBTREE));
BeginTranslateFolder(pfp);
tr = CheckForSubtreeCycles( GetBriefcaseFolderPairPtrArray(pfp->pfpd->hbr), hpathNew, pfp->pfpOther->hpath, pfp->pfpd->hsName);
EndTranslateFolder(pfp); } else tr = TR_SUCCESS;
if (tr == TR_SUCCESS) { TRACE_OUT((TEXT("TransplantFolderPair(): Transplanted folder twin %s to %s."), DebugGetPathString(pfp->hpath), DebugGetPathString(hpathNew)));
DeletePath(pfp->hpath); pfp->hpath = hpathNew; } else DeletePath(hpathNew); } else tr = TR_OUT_OF_MEMORY; } else tr = TR_SUCCESS;
return(tr); }
/*
** WriteFolderPair() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT WriteFolderPair(HCACHEDFILE hcf, PFOLDERPAIR pfp) { TWINRESULT tr; DBFOLDERTWIN dbft;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Set up folder pair database structure. */
dbft.dwStubFlags = (pfp->stub.dwFlags & DB_STUB_FLAGS_MASK); dbft.hpath1 = pfp->hpath; dbft.hpath2 = pfp->pfpOther->hpath; dbft.hsName = pfp->pfpd->hsName; dbft.dwAttributes = pfp->pfpd->dwAttributes;
/* Save folder pair database structure. */
if (WriteToCachedFile(hcf, (PCVOID)&dbft, sizeof(dbft), NULL)) tr = TR_SUCCESS; else tr = TR_BRIEFCASE_WRITE_FAILED;
return(tr); }
/*
** ReadFolderPair() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PRIVATE_CODE TWINRESULT ReadFolderPair(HCACHEDFILE hcf, HBRFCASE hbr, HHANDLETRANS hhtFolderTrans, HHANDLETRANS hhtNameTrans) { TWINRESULT tr = TR_CORRUPT_BRIEFCASE; DBFOLDERTWIN dbft; DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS)); ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &dbft, sizeof(dbft), &dwcbRead) && dwcbRead == sizeof(dbft)) { INEWFOLDERTWIN inft;
if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath1), (PHGENERIC)&(inft.hpathFirst))) { if (TranslateHandle(hhtFolderTrans, (HGENERIC)(dbft.hpath2), (PHGENERIC)&(inft.hpathSecond))) { if (TranslateHandle(hhtNameTrans, (HGENERIC)(dbft.hsName), (PHGENERIC)&(inft.hsName))) { PFOLDERPAIR pfp;
inft.dwAttributes = dbft.dwAttributes; inft.hbr = hbr;
if (IS_FLAG_SET(dbft.dwStubFlags, STUB_FL_SUBTREE)) inft.dwFlags = NFT_FL_SUBTREE; else inft.dwFlags = 0;
if (CreateFolderPair(&inft, &pfp)) tr = TR_SUCCESS; else tr = TR_OUT_OF_MEMORY; } } } }
return(tr); }
#ifdef VSTF
/*
** IsValidPCNEWFOLDERTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCNEWFOLDERTWIN(PCNEWFOLDERTWIN pcnft) { return(IS_VALID_READ_PTR(pcnft, CNEWFOLDERTWIN) && EVAL(pcnft->ulSize == sizeof(*pcnft)) && IS_VALID_STRING_PTR(pcnft->pcszFolder1, CSTR) && IS_VALID_STRING_PTR(pcnft->pcszFolder2, CSTR) && IS_VALID_STRING_PTR(pcnft->pcszName, CSTR) && FLAGS_ARE_VALID(pcnft->dwAttributes, ALL_FILE_ATTRIBUTES) && FLAGS_ARE_VALID(pcnft->dwFlags, ALL_NFT_FLAGS)); }
/*
** IsValidPCFOLDERTWINLIST() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCFOLDERTWINLIST(PCFOLDERTWINLIST pcftl) { BOOL bResult = FALSE;
if (IS_VALID_READ_PTR(pcftl, CFOLDERTWINLIST) && IS_VALID_HANDLE(pcftl->hbr, BRFCASE)) { PCFOLDERTWIN pcft; ULONG ulcFolderTwins = 0;
for (pcft = pcftl->pcftFirst; pcft && IS_VALID_STRUCT_PTR(pcft, CFOLDERTWIN); pcft = pcft->pcftNext) { ASSERT(ulcFolderTwins < ULONG_MAX); ulcFolderTwins++; }
if (! pcft && EVAL(ulcFolderTwins == pcftl->ulcItems)) bResult = TRUE; }
return(bResult); }
/*
** IsValidPCFOLDERTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCFOLDERTWIN(PCFOLDERTWIN pcft) { /* dwUser may be any value. */
return(IS_VALID_READ_PTR(pcft, CFOLDERTWIN) && IS_VALID_HANDLE(pcft->hftSrc, FOLDERTWIN) && IS_VALID_HANDLE(pcft->hvidSrc, VOLUMEID) && IS_VALID_STRING_PTR(pcft->pcszSrcFolder, CSTR) && IS_VALID_HANDLE(pcft->hftOther, FOLDERTWIN) && IS_VALID_HANDLE(pcft->hvidOther, VOLUMEID) && IS_VALID_STRING_PTR(pcft->pcszOtherFolder, CSTR) && IS_VALID_STRING_PTR(pcft->pcszName, CSTR) && FLAGS_ARE_VALID(pcft->dwFlags, ALL_FT_FLAGS)); }
/*
** IsValidFolderPairHalf() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidFolderPairHalf(PCFOLDERPAIR pcfp) { return(IS_VALID_READ_PTR(pcfp, CFOLDERPAIR) && IS_VALID_STRUCT_PTR(&(pcfp->stub), CSTUB) && FLAGS_ARE_VALID(GetStubFlags(&(pcfp->stub)), ALL_FOLDER_TWIN_FLAGS) && IS_VALID_HANDLE(pcfp->hpath, PATH) && IS_VALID_STRUCT_PTR(pcfp->pfpd, CFOLDERPAIRDATA) && (IsStubFlagSet(&(pcfp->stub), STUB_FL_BEING_DELETED) || IS_VALID_READ_PTR(pcfp->pfpOther, CFOLDERPAIR))); }
/*
** IsValidPCFOLDERPAIRDATA() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCFOLDERPAIRDATA(PCFOLDERPAIRDATA pcfpd) { /* Don't validate hbr. */
return(IS_VALID_READ_PTR(pcfpd, CFOLDERPAIRDATA) && IS_VALID_HANDLE(pcfpd->hsName, STRING)); }
#endif
#ifdef DEBUG
/*
** IsValidPCINEWFOLDERTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL IsValidPCINEWFOLDERTWIN(PCINEWFOLDERTWIN pcinft) { return(IS_VALID_READ_PTR(pcinft, CINEWFOLDERTWIN) && IS_VALID_HANDLE(pcinft->hpathFirst, PATH) && IS_VALID_HANDLE(pcinft->hpathSecond, PATH) && IS_VALID_HANDLE(pcinft->hsName, STRING) && FLAGS_ARE_VALID(pcinft->dwAttributes, ALL_FILE_ATTRIBUTES) && FLAGS_ARE_VALID(pcinft->dwFlags, ALL_NFT_FLAGS) && IS_VALID_HANDLE(pcinft->hbr, BRFCASE)); }
/*
** AreFolderPairsValid() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PRIVATE_CODE BOOL AreFolderPairsValid(HPTRARRAY hpaFolderPairs) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs && IS_VALID_STRUCT_PTR(GetPtr(hpaFolderPairs, ai), CFOLDERPAIR); ai++) ;
return(ai == aicPtrs); }
#endif
/****************************** Public Functions *****************************/
/*
** CreateFolderPairPtrArray() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL CreateFolderPairPtrArray(PHPTRARRAY phpa) { NEWPTRARRAY npa;
ASSERT(IS_VALID_WRITE_PTR(phpa, HPTRARRAY));
/* Try to create a folder pair pointer array. */
npa.aicInitialPtrs = NUM_START_FOLDER_TWIN_PTRS; npa.aicAllocGranularity = NUM_FOLDER_TWIN_PTRS_TO_ADD; npa.dwFlags = NPA_FL_SORTED_ADD;
return(CreatePtrArray(&npa, phpa)); }
/*
** DestroyFolderPairPtrArray() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void DestroyFolderPairPtrArray(HPTRARRAY hpa) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
/* Free all folder pairs in pointer array. */
aicPtrs = GetPtrCount(hpa); ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++) { PFOLDERPAIR pfp; PFOLDERPAIR pfpOther; PFOLDERPAIRDATA pfpd; BOOL bDeleteFolderPairData;
pfp = GetPtr(hpa, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Copy fields needed after folder pair half's demise. */
pfpOther = pfp->pfpOther; pfpd = pfp->pfpd; bDeleteFolderPairData = IsStubFlagSet(&(pfp->stub), STUB_FL_BEING_DELETED);
DestroyHalfOfFolderPair(pfp);
/* Has the other half of this folder pair already been destroyed? */
if (bDeleteFolderPairData) /* Yes. Destroy the pair's shared data. */ DestroySharedFolderPairData(pfpd); }
/* Now wipe out the pointer array. */
DestroyPtrArray(hpa);
return; }
/*
** LockFolderPair() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void LockFolderPair(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED)); ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
ASSERT(pfp->stub.ulcLock < ULONG_MAX); pfp->stub.ulcLock++;
ASSERT(pfp->pfpOther->stub.ulcLock < ULONG_MAX); pfp->pfpOther->stub.ulcLock++;
return; }
/*
** UnlockFolderPair() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE void UnlockFolderPair(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (EVAL(pfp->stub.ulcLock > 0)) pfp->stub.ulcLock--;
if (EVAL(pfp->pfpOther->stub.ulcLock > 0)) pfp->pfpOther->stub.ulcLock--;
if (! pfp->stub.ulcLock && IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED)) { ASSERT(! pfp->pfpOther->stub.ulcLock); ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
DestroyFolderPair(pfp); }
return; }
/*
** UnlinkFolderPair() ** ** Unlinks a folder pair. ** ** Arguments: pfp - pointer to folder pair to be unlinked ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT UnlinkFolderPair(PFOLDERPAIR pfp) { ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_UNLINKED)); ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_UNLINKED));
/* Unlink both halves of the folder pair. */
UnlinkHalfOfFolderPair(pfp); UnlinkHalfOfFolderPair(pfp->pfpOther);
SetStubFlag(&(pfp->stub), STUB_FL_UNLINKED); SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_UNLINKED);
return(TR_SUCCESS); }
/*
** DestroyFolderPair() ** ** Destroys a folder pair. ** ** Arguments: pfp - pointer to folder pair to destroy ** ** Returns: void ** ** Side Effects: none */ PUBLIC_CODE void DestroyFolderPair(PFOLDERPAIR pfp) { PFOLDERPAIRDATA pfpd;
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
/* Destroy both FOLDERPAIR halves, and shared data. */
pfpd = pfp->pfpd;
DestroyHalfOfFolderPair(pfp->pfpOther); DestroyHalfOfFolderPair(pfp);
DestroySharedFolderPairData(pfpd);
return; }
/*
** MyTranslateFolder() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT MyTranslateFolder(HBRFCASE hbr, HPATH hpathOld, HPATH hpathNew) { TWINRESULT tr = TR_SUCCESS; HPTRARRAY hpaFolderPairs; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hpathOld, PATH)); ASSERT(IS_VALID_HANDLE(hpathNew, PATH));
/*
* Change folders of all folder pairs rooted in pcszOldFolder's subtree to * being rooted in pcszNewFolder's subtree. */
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(hbr); aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++) { tr = TransplantFolderPair(GetPtr(hpaFolderPairs, ai), hpathOld, hpathNew);
if (tr != TR_SUCCESS) break; }
if (tr == TR_SUCCESS) { HPTRARRAY hpaTwinFamilies;
/* Restore folder pair array to sorted order. */
SortPtrArray(hpaFolderPairs, &FolderPairSortCmp);
TRACE_OUT((TEXT("MyTranslateFolder(): Sorted folder pair array after folder translation.")));
/*
* Change folders of all object twins in pcszOldFolder's old subtree to * being in pcszNewFolder's subtree. */
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
aicPtrs = GetPtrCount(hpaTwinFamilies);
for (ai = 0; ai < aicPtrs; ai++) { PTWINFAMILY ptf; BOOL bContinue; HNODE hnode;
ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
/*
* Walk each twin family's list of object twins looking for object * twins in the translated folder's subtree. */
for (bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnode); bContinue; bContinue = GetNextNode(hnode, &hnode)) { POBJECTTWIN pot;
pot = (POBJECTTWIN)GetNodeData(hnode);
tr = TransplantObjectTwin(pot, hpathOld, hpathNew);
if (tr != TR_SUCCESS) break; }
if (tr != TR_SUCCESS) break; }
/* Twin family array is still in sorted order. */ }
return(tr); }
/*
** ApplyNewObjectTwinsToFolderTwins() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: Adds new spin-off object twins to hlistNewObjectTwins as they ** are created. ** ** N.b., new object twins may have been added to hlistNewObjectTwins even if ** FALSE is returned. Clean-up of these new object twins in case of failure is ** the caller's responsibility. ** ** A new object twin may generate more new object twins, implied by existing ** folder twins. E.g., consider the following scenario: ** ** 1) Folder twins (c:\, d:\, *.*) and (d:\, e:\, *.*) exist. ** 2) Files c:\foo, d:\foo, and e:\foo do not exist. ** 3) File e:\foo is created. ** 4) New object twin e:\foo is added. ** 5) d:\foo must be added as a new object twin from the e:\foo object twin ** because of the (d:\, e:\, *.*) folder twin. ** 6) c:\foo must be added as a new object twin from the d:\foo object twin ** because of the (c:\, d:\, *.*) folder twin. ** ** So we see that new object twin e:\foo must generate two more new object ** twins, d:\foo and c:\foo, implied by the two existing folder twins, ** (c:\, d:\, *.*) and (d:\, e:\, *.*). */ PUBLIC_CODE BOOL ApplyNewObjectTwinsToFolderTwins(HLIST hlistNewObjectTwins) { BOOL bResult = TRUE; BOOL bContinue; HNODE hnode;
ASSERT(IS_VALID_HANDLE(hlistNewObjectTwins, LIST));
/*
* Don't use WalkList() here because we want to insert new nodes in * hlistNewObjectTwins after the current node. */
for (bContinue = GetFirstNode(hlistNewObjectTwins, &hnode); bContinue && bResult; bContinue = GetNextNode(hnode, &hnode)) { POBJECTTWIN pot; HPATHLIST hpl; HPTRARRAY hpaFolderPairs; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
pot = GetNodeData(hnode);
ASSERT(! pot->ulcSrcFolderTwins);
TRACE_OUT((TEXT("ApplyNewObjectTwinsToFolderTwins(): Applying new object twin %s\\%s."), DebugGetPathString(pot->hpath), GetString(pot->ptfParent->hsName)));
/*
* Assume that hpl, hpaFolderPairs, and aicPtrs don't change during this * loop. Calculate them outside the loop. */
hpl = GetBriefcasePathList(pot->ptfParent->hbr); hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pot->ptfParent->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
for (ai = 0; ai < aicPtrs; ai++) { PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
if (FolderTwinGeneratesObjectTwin(pfp, pot->hpath, GetString(pot->ptfParent->hsName))) { HPATH hpathMatchingFolder; HNODE hnodeUnused;
ASSERT(pot->ulcSrcFolderTwins < ULONG_MAX); pot->ulcSrcFolderTwins++;
/*
* Append the generated object twin's subpath to the matching * folder twin's base path for subtree twins. */
if (BuildPathForMatchingObjectTwin(pfp, pot, hpl, &hpathMatchingFolder)) { /*
* We don't want to collapse any twin families if the matching * object twin is found in a different twin family. This will * be done by ApplyNewFolderTwinsToTwinFamilies() for spin-off * object twins generated by new folder twins. * * Spin-off object twins created by new object twins never * require collapsing twin families. For a spin-off object twin * generated by a new object twin to collapse twin families, * there would have to have been separate twin families * connected by a folder twin. But if those twin families were * already connected by a folder twin, they would not be * separate because they would already have been collapsed by * ApplyNewFolderTwinsToTwinFamilies() when the connecting * folder twin was added. */
if (! FindObjectTwin(pot->ptfParent->hbr, hpathMatchingFolder, GetString(pot->ptfParent->hsName), &hnodeUnused)) { POBJECTTWIN potNew;
/*
* CreateObjectTwin() ASSERT()s that an object twin for * hpathMatchingFolder is not found, so we don't need to do * that here. */
if (CreateObjectTwin(pot->ptfParent, hpathMatchingFolder, &potNew)) { /*
* Add the new object twin to hlistNewObjectTwins after * the new object twin currently being processed to make * certain that it gets processed in the outside loop * through hlistNewObjectTwins. */
if (! InsertNodeAfter(hnode, NULL, potNew, &hnodeUnused)) { DestroyStub(&(potNew->stub)); bResult = FALSE; break; } } }
DeletePath(hpathMatchingFolder); } else { bResult = FALSE; break; } } } }
return(bResult); }
/*
** BuildPathForMatchingObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: Path is added to object twin's briefcase's path list. */ PUBLIC_CODE BOOL BuildPathForMatchingObjectTwin(PCFOLDERPAIR pcfp, PCOBJECTTWIN pcot, HPATHLIST hpl, PHPATH phpath) { BOOL bResult;
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN)); ASSERT(IS_VALID_HANDLE(hpl, PATHLIST)); ASSERT(IS_VALID_WRITE_PTR(phpath, HPATH));
ASSERT(FolderTwinGeneratesObjectTwin(pcfp, pcot->hpath, GetString(pcot->ptfParent->hsName)));
/* Is the generating folder twin a subtree twin? */
if (IsStubFlagSet(&(pcfp->stub), STUB_FL_SUBTREE)) { TCHAR rgchPathSuffix[MAX_PATH_LEN]; LPCTSTR pcszSubPath;
/*
* Yes. Append the object twin's subpath to the subtree twin's base * path. */
pcszSubPath = FindChildPathSuffix(pcfp->hpath, pcot->hpath, rgchPathSuffix);
bResult = AddChildPath(hpl, pcfp->pfpOther->hpath, pcszSubPath, phpath); } else /* No. Just use the matching folder twin's folder. */ bResult = CopyPath(pcfp->pfpOther->hpath, hpl, phpath);
return(bResult); }
/*
** EnumGeneratedObjectTwins() ** ** ** ** Arguments: ** ** Returns: FALSE if callback aborted. TRUE if not. ** ** Side Effects: none */ PUBLIC_CODE BOOL EnumGeneratedObjectTwins(PCFOLDERPAIR pcfp, ENUMGENERATEDOBJECTTWINSPROC egotp, PVOID pvRefData) { BOOL bResult = TRUE; HPTRARRAY hpaTwinFamilies; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
/* pvRefData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_CODE_PTR(egotp, ENUMGENERATEDOBJECTTWINPROC));
/*
* Walk the array of twin families, looking for twin families whose names * intersect the given folder twin's name specification. */
hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(pcfp->pfpd->hbr);
aicPtrs = GetPtrCount(hpaTwinFamilies); ai = 0;
while (ai < aicPtrs) { PTWINFAMILY ptf; LPCTSTR pcszName;
ptf = GetPtr(hpaTwinFamilies, ai);
ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY)); ASSERT(IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED));
/*
* Does the twin family's name match the folder twin's name * specification? */
pcszName = GetString(ptf->hsName);
if (IsFolderObjectTwinName(pcszName) || NamesIntersect(pcszName, GetString(pcfp->pfpd->hsName))) { BOOL bContinue; HNODE hnodePrev;
/* Yes. Look for a matching folder. */
/* Lock the twin family so it isn't deleted out from under us. */
LockStub(&(ptf->stub));
/*
* Walk each twin family's list of object twins looking for object * twins in the given folder twin's subtree. */
bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnodePrev);
while (bContinue) { HNODE hnodeNext; POBJECTTWIN pot;
bContinue = GetNextNode(hnodePrev, &hnodeNext);
pot = (POBJECTTWIN)GetNodeData(hnodePrev);
ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
if (FolderTwinIntersectsFolder(pcfp, pot->hpath)) { /*
* A given object twin should only be generated by one of the * folder twins in a pair of folder twins. */
ASSERT(! FolderTwinGeneratesObjectTwin(pcfp->pfpOther, pot->hpath, GetString(pot->ptfParent->hsName)));
bResult = (*egotp)(pot, pvRefData);
if (! bResult) break; }
hnodePrev = hnodeNext; }
/* Was the twin family unlinked? */
if (IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED)) /* No. */ ai++; else { /* Yes. */
aicPtrs--; ASSERT(aicPtrs == GetPtrCount(hpaTwinFamilies));
TRACE_OUT((TEXT("EnumGeneratedObjectTwins(): Twin family for object %s unlinked by callback."), GetString(ptf->hsName))); }
UnlockStub(&(ptf->stub));
if (! bResult) break; } else /* No. Skip it. */ ai++; }
return(bResult); }
/*
** EnumGeneratingFolderTwins() ** ** ** ** Arguments: ** ** Returns: FALSE if callback aborted. TRUE if not. ** ** Side Effects: none ** ** N.b., if the egftp callback removes a pair of folder twins, it must remove ** the pair from the first folder twin encountered. If it removes the pair of ** folder twins from the second folder twin encountered, a folder twin will be ** skipped. */ PUBLIC_CODE BOOL EnumGeneratingFolderTwins(PCOBJECTTWIN pcot, ENUMGENERATINGFOLDERTWINSPROC egftp, PVOID pvRefData, PULONG pulcGeneratingFolderTwins) { BOOL bResult = TRUE; HPTRARRAY hpaFolderPairs; ARRAYINDEX aicPtrs; ARRAYINDEX ai;
/* pvRefData may be any value. */
ASSERT(IS_VALID_STRUCT_PTR(pcot, COBJECTTWIN)); ASSERT(IS_VALID_CODE_PTR(egftp, ENUMGENERATINGFOLDERTWINSPROC)); ASSERT(IS_VALID_WRITE_PTR(pulcGeneratingFolderTwins, ULONG));
*pulcGeneratingFolderTwins = 0;
hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcot->ptfParent->hbr);
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
ai = 0;
while (ai < aicPtrs) { PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
if (FolderTwinGeneratesObjectTwin(pfp, pcot->hpath, GetString(pcot->ptfParent->hsName))) { ASSERT(! FolderTwinGeneratesObjectTwin(pfp->pfpOther, pcot->hpath, GetString(pcot->ptfParent->hsName)));
ASSERT(*pulcGeneratingFolderTwins < ULONG_MAX); (*pulcGeneratingFolderTwins)++;
/*
* Lock the pair of folder twins so they don't get deleted out from * under us. */
LockStub(&(pfp->stub));
bResult = (*egftp)(pfp, pvRefData);
if (IsStubFlagSet(&(pfp->stub), STUB_FL_UNLINKED)) { WARNING_OUT((TEXT("EnumGeneratingFolderTwins(): Folder twin pair unlinked during callback.")));
aicPtrs -= 2; ASSERT(! (aicPtrs % 2)); ASSERT(aicPtrs == GetPtrCount(hpaFolderPairs)); } else ai++;
UnlockStub(&(pfp->stub));
if (! bResult) break; } else ai++; }
return(bResult); }
/*
** FolderTwinGeneratesObjectTwin() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none ** ** A folder twin or subtree twin is said to generate an object twin when the ** following conditions are met: ** ** 1) The folder twin or subtree twin is on the same volume as the object twin. ** ** 2) The name of the object twin (literal) intersects the objects matched by ** the folder twin or subtree twin (literal or wildcard). ** ** 3) The folder twin's folder exactly matches the object twin's folder, or the ** subtree twin's root folder is a path prefix of the object twin's folder. */ PUBLIC_CODE BOOL FolderTwinGeneratesObjectTwin(PCFOLDERPAIR pcfp, HPATH hpathFolder, LPCTSTR pcszName) { ASSERT(IS_VALID_STRUCT_PTR(pcfp, CFOLDERPAIR)); ASSERT(IS_VALID_HANDLE(hpathFolder, PATH)); ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
return(FolderTwinIntersectsFolder(pcfp, hpathFolder) && (IsFolderObjectTwinName(pcszName) || NamesIntersect(pcszName, GetString(pcfp->pfpd->hsName)))); }
/*
** IsValidHFOLDERTWIN() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidHFOLDERTWIN(HFOLDERTWIN hft) { return(IS_VALID_STRUCT_PTR((PFOLDERPAIR)hft, CFOLDERPAIR)); }
#ifdef VSTF
/*
** IsValidPCFOLDERPAIR() ** ** ** ** Arguments: ** ** Returns: ** ** Side Effects: none */ PUBLIC_CODE BOOL IsValidPCFOLDERPAIR(PCFOLDERPAIR pcfp) { BOOL bResult = FALSE;
/* All the fields of an unlinked folder pair should be valid. */
if (EVAL(IsValidFolderPairHalf(pcfp))) { if (IsStubFlagSet(&(pcfp->stub), STUB_FL_BEING_DELETED)) bResult = TRUE; else if (EVAL(IsValidFolderPairHalf(pcfp->pfpOther)) && EVAL(pcfp->pfpOther->pfpOther == pcfp) && EVAL(pcfp->pfpd == pcfp->pfpOther->pfpd) && EVAL(pcfp->stub.ulcLock == pcfp->pfpOther->stub.ulcLock)) { BOOL bUnlinked; BOOL bOtherUnlinked;
/*
* Neither or both folder pair halves may be unlinked, but not only * one. */
bUnlinked = IsStubFlagSet(&(pcfp->stub), STUB_FL_UNLINKED); bOtherUnlinked = IsStubFlagSet(&(pcfp->pfpOther->stub), STUB_FL_UNLINKED);
if (EVAL((bUnlinked && bOtherUnlinked) || (! bUnlinked && ! bOtherUnlinked))) bResult = TRUE; } }
return(bResult); }
#endif
/*
** WriteFolderPairList() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT WriteFolderPairList(HCACHEDFILE hcf, HPTRARRAY hpaFolderPairs) { TWINRESULT tr = TR_BRIEFCASE_WRITE_FAILED; DWORD dwcbDBFolderTwinListHeaderOffset;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
/* Save initial file position. */
dwcbDBFolderTwinListHeaderOffset = GetCachedFilePointerPosition(hcf);
if (dwcbDBFolderTwinListHeaderOffset != INVALID_SEEK_POSITION) { DBFOLDERTWINLISTHEADER dbftlh;
/* Leave space for folder twin data header. */
ZeroMemory(&dbftlh, sizeof(dbftlh));
if (WriteToCachedFile(hcf, (PCVOID)&dbftlh, sizeof(dbftlh), NULL)) { ARRAYINDEX aicPtrs; ARRAYINDEX ai;
tr = TR_SUCCESS;
/* Mark all folder pairs unused. */
ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
aicPtrs = GetPtrCount(hpaFolderPairs); ASSERT(! (aicPtrs % 2));
/* Write all folder pairs. */
for (ai = 0; ai < aicPtrs; ai++) { PFOLDERPAIR pfp;
pfp = GetPtr(hpaFolderPairs, ai);
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
if (IsStubFlagClear(&(pfp->stub), STUB_FL_USED)) { ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_USED));
tr = WriteFolderPair(hcf, pfp);
if (tr == TR_SUCCESS) { SetStubFlag(&(pfp->stub), STUB_FL_USED); SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_USED); } else break; } }
/* Save folder twin data header. */
if (tr == TR_SUCCESS) { ASSERT(! (aicPtrs % 2));
dbftlh.lcFolderPairs = aicPtrs / 2;
tr = WriteDBSegmentHeader(hcf, dwcbDBFolderTwinListHeaderOffset, &dbftlh, sizeof(dbftlh));
if (tr == TR_SUCCESS) TRACE_OUT((TEXT("WriteFolderPairList(): Wrote %ld folder pairs."), dbftlh.lcFolderPairs)); } } }
return(tr); }
/*
** ReadFolderPairList() ** ** ** ** Arguments: ** ** Returns: TWINRESULT ** ** Side Effects: none */ PUBLIC_CODE TWINRESULT ReadFolderPairList(HCACHEDFILE hcf, HBRFCASE hbr, HHANDLETRANS hhtFolderTrans, HHANDLETRANS hhtNameTrans) { TWINRESULT tr; DBFOLDERTWINLISTHEADER dbftlh; DWORD dwcbRead;
ASSERT(IS_VALID_HANDLE(hcf, CACHEDFILE)); ASSERT(IS_VALID_HANDLE(hbr, BRFCASE)); ASSERT(IS_VALID_HANDLE(hhtFolderTrans, HANDLETRANS)); ASSERT(IS_VALID_HANDLE(hhtNameTrans, HANDLETRANS));
if (ReadFromCachedFile(hcf, &dbftlh, sizeof(dbftlh), &dwcbRead) && dwcbRead == sizeof(dbftlh)) { LONG l;
tr = TR_SUCCESS;
TRACE_OUT((TEXT("ReadFolderPairList(): Reading %ld folder pairs."), dbftlh.lcFolderPairs));
for (l = 0; l < dbftlh.lcFolderPairs && tr == TR_SUCCESS; l++) tr = ReadFolderPair(hcf, hbr, hhtFolderTrans, hhtNameTrans);
ASSERT(tr != TR_SUCCESS || AreFolderPairsValid(GetBriefcaseFolderPairPtrArray(hbr))); } else tr = TR_CORRUPT_BRIEFCASE;
return(tr); }
/***************************** Exported Functions ****************************/
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | AddFolderTwin | Twins two folders.
@parm HBRFCASE | hbr | A handle to the open briefcase that the new folder twins are to be added to.
@parm PCNEWFOLDERTWIN | pcnft | A pointer to a CNEWFOLDERTWIN describing the two folders to be twinned.
@parm PHFOLDERTWIN | phft | A pointer to an HFOLDERTWIN to be filled in with a handle to the new folder twins. *phft is only valid if TR_SUCCESS or TR_DUPLICATE_TWIN is returned.
@rdesc If the folder twins were added successfully, TR_SUCCESS is returned, and *phFolderTwin contains a handle to the new folder twins. Otherwise, the folder twins were not added successfully, the return value indicates the error that occurred, and *phFolderTwin is undefined. If one or both of the volumes specified by the NEWFOLDERTWIN structure is not present, TR_UNAVAILABLE_VOLUME will be returned, and the folder twin will not be added.
@comm Once the caller is finshed with the twin handle returned by AddFolderTwin(), ReleaseTwinHandle() should be called to release the twin handle. N.b., DeleteTwin() does not release a twin handle returned by AddFolderTwin().
@xref ReleaseTwinHandle DeleteTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI AddFolderTwin(HBRFCASE hbr, PCNEWFOLDERTWIN pcnft, PHFOLDERTWIN phft) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(AddFolderTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_STRUCT_PTR(pcnft, CNEWFOLDERTWIN) && EVAL(pcnft->ulSize == sizeof(*pcnft)) && IS_VALID_WRITE_PTR(phft, HFOLDERTWIN)) #endif
{ INEWFOLDERTWIN inft;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = MakeINewFolderTwin(hbr, pcnft, &inft);
if (tr == TR_SUCCESS) { PFOLDERPAIR pfp;
ASSERT(! IS_ATTR_DIR(pcnft->dwAttributes));
tr = TwinFolders(&inft, &pfp);
if (tr == TR_SUCCESS || tr == TR_DUPLICATE_TWIN) { LockStub(&(pfp->stub));
*phft = (HFOLDERTWIN)pfp; }
ReleaseINewFolderTwin(&inft); } } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(AddFolderTwin, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | IsFolderTwin | Determines whether or not a folder is a folder twin.
@parm HBRFCASE | hbr | A handle to the open briefcase to check for the folder twin.
@parm PCSTR | pcszFolder | A pointer to a string indicating the folder in question.
@parm PBOOL | pbIsFolderTwin | A pointer to a BOOL to be filled in with TRUE if the folder is a folder twin, or FALSE if not. *pbIsFolderTwin is only valid if TR_SUCCESS is returned.
@rdesc If the lookup was successful, TR_SUCCESS is returned. Otherwise, the lookup was not successful, and the return value indicates the error that occurred.
@xref CreateFolderTwinList DestroyFolderTwinList
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI IsFolderTwin(HBRFCASE hbr, LPCTSTR pcszFolder, PBOOL pbIsFolderTwin) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(IsFolderTwin);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_STRING_PTR(pcszFolder, CSTR) && IS_VALID_WRITE_PTR(pbIsFolderTwin, BOOL)) #endif
{ HPATH hpath;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = TranslatePATHRESULTToTWINRESULT( AddPath(GetBriefcasePathList(hbr), pcszFolder, &hpath));
if (tr == TR_SUCCESS) { ARRAYINDEX aiFirst;
/* Search for folder pair referencing given folder. */
*pbIsFolderTwin = SearchSortedArray( GetBriefcaseFolderPairPtrArray(hbr), &FolderPairSearchCmp, hpath, &aiFirst);
if (*pbIsFolderTwin) TRACE_OUT((TEXT("IsFolderTwin(): %s is a folder twin."), DebugGetPathString(hpath))); else TRACE_OUT((TEXT("IsFolderTwin(): %s is not a folder twin."), DebugGetPathString(hpath)));
DeletePath(hpath); } } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(IsFolderTwin, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | CreateFolderTwinList | Creates a list of the folder twins of a given folder.
@parm HBRFCASE | hbr | A handle to the open briefcase that the folder twin list is to be created from.
@parm PCSTR | pcszFolder | A pointer to a string indicating the folder whose folder twins are to be listed.
@parm PFOLDERTWINLIST | ppftl | A pointer to an PFOLDERTWINLIST to be filled in with a pointer to the new list of folder twins. *ppFolderTwinList is only valid if TR_SUCCESS is returned.
@rdesc If the folder twin list was created successfully, TR_SUCCESS is returned. Otherwise, the folder twin list was not created successfully, and the return value indicates the error that occurred.
@xref DestroyFolderTwinList IsFolderTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI CreateFolderTwinList(HBRFCASE hbr, LPCTSTR pcszFolder, PFOLDERTWINLIST *ppftl) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(CreateFolderTwinList);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_HANDLE(hbr, BRFCASE) && IS_VALID_STRING_PTR(pcszFolder, CSTR) && IS_VALID_WRITE_PTR(ppftl, PFOLDERTWINLIST)) #endif
{ HPATH hpath;
InvalidatePathListInfo(GetBriefcasePathList(hbr));
tr = TranslatePATHRESULTToTWINRESULT( AddPath(GetBriefcasePathList(hbr), pcszFolder, &hpath));
if (tr == TR_SUCCESS) { PFOLDERTWINLIST pftlNew;
/* Try to create a new folder twin list. */
if (AllocateMemory(sizeof(*pftlNew), &pftlNew)) { ARRAYINDEX ai;
/* Initialize FOLDERTWINLIST structure fields. */
pftlNew->ulcItems = 0; pftlNew->pcftFirst = NULL; pftlNew->hbr = hbr;
/* Search for first folder pair referencing given folder. */
if (SearchSortedArray(GetBriefcaseFolderPairPtrArray(hbr), &FolderPairSearchCmp, hpath, &ai)) { PFOLDERTWIN pftHead; ARRAYINDEX aicFolderTwins;
tr = CreateListOfFolderTwins(hbr, ai, hpath, &pftHead, &aicFolderTwins);
if (tr == TR_SUCCESS) { /* Success! Update parent folder twin list fields. */
pftlNew->pcftFirst = pftHead; pftlNew->ulcItems = aicFolderTwins; } else /* Free data structure, ignoring return value. */ FreeMemory(pftlNew); } else tr = TR_SUCCESS;
/* Return pointer to new FOLDERTWINLIST. */
if (tr == TR_SUCCESS) { *ppftl = pftlNew;
ASSERT(IS_VALID_STRUCT_PTR(*ppftl, CFOLDERTWINLIST)); } } else tr = TR_OUT_OF_MEMORY;
DeletePath(hpath); } } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(CreateFolderTwinList, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
/******************************************************************************
@doc SYNCENGAPI
@api TWINRESULT | DestroyFolderTwinList | Destroys a folder twin list created by CreateFolderTwinList().
@parm PFOLDERTWINLIST | pftl | A pointer to the folder twin list to be destroyed. The FOLDERTWINLIST pointed to by pftl is not valid after DestroyFolderTwinList() is called.
@rdesc If the folder twin list was deleted successfully, TR_SUCCESS is returned. Otherwise, the folder twin list was not deleted successfully, and the return value indicates the error that occurred.
@xref CreateFolderTwinList IsFolderTwin
******************************************************************************/
SYNCENGAPI TWINRESULT WINAPI DestroyFolderTwinList(PFOLDERTWINLIST pftl) { TWINRESULT tr;
if (BeginExclusiveBriefcaseAccess()) { DebugEntry(DestroyFolderTwinList);
#ifdef EXPV
/* Verify parameters. */
if (IS_VALID_STRUCT_PTR(pftl, CFOLDERTWINLIST)) #endif
{ DestroyListOfFolderTwins((PFOLDERTWIN)(pftl->pcftFirst)); FreeMemory(pftl);
tr = TR_SUCCESS; } #ifdef EXPV
else tr = TR_INVALID_PARAMETER; #endif
DebugExitTWINRESULT(DestroyFolderTwinList, tr);
EndExclusiveBriefcaseAccess(); } else tr = TR_REENTERED;
return(tr); }
|