/* * 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); }