349 lines
10 KiB
C
349 lines
10 KiB
C
/*
|
|
* subcycle.c - Subtree cycle detection routines module.
|
|
*/
|
|
|
|
|
|
/* Headers
|
|
**********/
|
|
|
|
#include "project.h"
|
|
#pragma hdrstop
|
|
|
|
#include "stub.h"
|
|
#include "subcycle.h"
|
|
|
|
|
|
/* Constants
|
|
************/
|
|
|
|
/* pointer array allocation constants */
|
|
|
|
#define NUM_CYCLE_PTRS_TO_ADD (16)
|
|
|
|
|
|
/***************************** Private Functions *****************************/
|
|
|
|
/* Module Prototypes
|
|
********************/
|
|
|
|
PRIVATE_CODE TWINRESULT CheckHalfForSubtreeCycle(HPTRARRAY, HPATH, HPATH, LPCTSTR);
|
|
|
|
|
|
/*
|
|
** CheckHalfForSubtreeCycle()
|
|
**
|
|
** Checks to see if half of a proposed new folder subtree twin would create one
|
|
** or more cycles of folder subtree twins.
|
|
**
|
|
** Arguments: hpaFolderPairs - handle to PTRARRAY containing pointers to
|
|
** folder pairs
|
|
** hpathStartFolder - root folder of initial half of proposed
|
|
** new folder pair
|
|
** hpathEndFolder - root folder of other half of proposed new
|
|
** folder pair
|
|
** pcszName - name specification of matching objects to be
|
|
** included in proposed new folder subtree pair
|
|
**
|
|
** Returns: TWINRESULT
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** N.b., this function should be called twice for each proposed new folder
|
|
** subtree pair.
|
|
*/
|
|
PRIVATE_CODE TWINRESULT CheckHalfForSubtreeCycle(HPTRARRAY hpaFolderPairs,
|
|
HPATH hpathStartFolder,
|
|
HPATH hpathEndFolder,
|
|
LPCTSTR pcszName)
|
|
{
|
|
TWINRESULT tr;
|
|
ARRAYINDEX aicFolderPairs;
|
|
NEWPTRARRAY npa;
|
|
HPTRARRAY hpaFolders;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
|
|
ASSERT(IS_VALID_HANDLE(hpathStartFolder, PATH));
|
|
ASSERT(IS_VALID_HANDLE(hpathEndFolder, PATH));
|
|
ASSERT(IS_VALID_STRING_PTR(pcszName, CSTR));
|
|
|
|
aicFolderPairs = GetPtrCount(hpaFolderPairs);
|
|
|
|
/*
|
|
* Try to create an unsorted pointer array to be used in checking for
|
|
* cycles.
|
|
*/
|
|
|
|
npa.aicInitialPtrs = aicFolderPairs;
|
|
npa.aicAllocGranularity = NUM_CYCLE_PTRS_TO_ADD;
|
|
npa.dwFlags = 0;
|
|
|
|
if (CreatePtrArray(&npa, &hpaFolders))
|
|
{
|
|
ARRAYINDEX aicFolders;
|
|
ARRAYINDEX aiCurFolder;
|
|
HPATH hpathCurFolderRoot;
|
|
|
|
/* Search all folder pairs connected to the first new folder twin. */
|
|
|
|
/*
|
|
* Mark all folder twins unused. A "used" folder twin is one that has
|
|
* already been visited while searching for subtree cycles. I.e., a
|
|
* used folder subtree pair half intersected the first folder of the
|
|
* proposed new folder twin, and its other half was added to the list for
|
|
* later comparison.
|
|
*/
|
|
|
|
ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
|
|
|
|
/*
|
|
* Loop to process entire graph of folder subtree twins connected to the
|
|
* first new folder twin. Folder twins are only added to the hpaFolders
|
|
* array if they don't already intersect the second of the two proposed
|
|
* new folder subtree twins.
|
|
*/
|
|
|
|
tr = TR_SUCCESS;
|
|
aicFolders = 0;
|
|
aiCurFolder = 0;
|
|
|
|
/* Begin with start folder. */
|
|
|
|
hpathCurFolderRoot = hpathStartFolder;
|
|
|
|
FOREVER
|
|
{
|
|
ARRAYINDEX aiCheckFolderRoot;
|
|
|
|
/*
|
|
* Loop to find all subtree folder pairs that intersect
|
|
* hpaFolders[aiCurFolder]'s subtree.
|
|
*/
|
|
|
|
for (aiCheckFolderRoot = 0;
|
|
aiCheckFolderRoot < aicFolderPairs;
|
|
aiCheckFolderRoot++)
|
|
{
|
|
PFOLDERPAIR pfpCheck;
|
|
|
|
/* Get this subtree folder pair's root folder. */
|
|
|
|
pfpCheck = GetPtr(hpaFolderPairs, aiCheckFolderRoot);
|
|
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfpCheck, CFOLDERPAIR));
|
|
|
|
/* Have we already visited this folder pair? */
|
|
|
|
if (IsStubFlagSet(&(pfpCheck->stub), STUB_FL_SUBTREE) &&
|
|
IsStubFlagClear(&(pfpCheck->stub), STUB_FL_BEING_TRANSLATED) &&
|
|
IsStubFlagClear(&(pfpCheck->stub), STUB_FL_USED) &&
|
|
IsStubFlagClear(&(pfpCheck->pfpOther->stub), STUB_FL_USED))
|
|
{
|
|
/*
|
|
* No. Does this subtree folder pair intersect the current
|
|
* folder pair node's subtree, and the objects named in the
|
|
* proposed new folder subtree twin?
|
|
*/
|
|
|
|
ASSERT(IsStubFlagSet(&(pfpCheck->pfpOther->stub), STUB_FL_SUBTREE));
|
|
ASSERT(IsStubFlagClear(&(pfpCheck->pfpOther->stub), STUB_FL_BEING_TRANSLATED));
|
|
|
|
if (SubtreesIntersect(hpathCurFolderRoot, pfpCheck->hpath) &&
|
|
NamesIntersect(GetString(pfpCheck->pfpd->hsName), pcszName))
|
|
{
|
|
HPATH hpathOtherCheckFolderRoot;
|
|
|
|
/* Yes. Get the other side of the folder subtree pair. */
|
|
|
|
hpathOtherCheckFolderRoot = pfpCheck->pfpOther->hpath;
|
|
|
|
/*
|
|
* Does this pair connect back to the other side of the
|
|
* proposed new folder pair?
|
|
*/
|
|
|
|
if (SubtreesIntersect(hpathOtherCheckFolderRoot,
|
|
hpathEndFolder))
|
|
{
|
|
/*
|
|
* Yes. Are the roots different parts of the common
|
|
* subtree?
|
|
*/
|
|
|
|
if (ComparePaths(hpathEndFolder,
|
|
hpathOtherCheckFolderRoot)
|
|
!= CR_EQUAL)
|
|
{
|
|
/* Yes. Found a cycle. Bail out. */
|
|
|
|
WARNING_OUT((TEXT("CheckHalfForSubtreeCycle(): Subtree cycle found connecting folders %s and %s."),
|
|
DebugGetPathString(hpathStartFolder),
|
|
DebugGetPathString(hpathEndFolder)));
|
|
|
|
tr = TR_SUBTREE_CYCLE_FOUND;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We don't need to include this root in the search if it
|
|
* is the same as the other side of the proposed new
|
|
* folder pair since it will be covered during the other
|
|
* call to CheckHalfForSubtreeCycle().
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
/* Add this subtree as another node to be examined. */
|
|
|
|
if (! InsertPtr(hpaFolders, NULL, aicFolders++,
|
|
(PCVOID)(pfpCheck->pfpOther)))
|
|
tr = TR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Mark this folder twin as already visited. */
|
|
|
|
if (tr == TR_SUCCESS)
|
|
SetStubFlag(&(pfpCheck->stub), STUB_FL_USED);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Any folder subtree twins left to investigate? */
|
|
|
|
if (aiCurFolder < aicFolders)
|
|
{
|
|
PFOLDERPAIR pfpCur;
|
|
|
|
/* Yes. */
|
|
|
|
pfpCur = GetPtr(hpaFolders, aiCurFolder++);
|
|
|
|
hpathCurFolderRoot = pfpCur->hpath;
|
|
}
|
|
else
|
|
/* No. */
|
|
break;
|
|
}
|
|
|
|
DestroyPtrArray(hpaFolders);
|
|
}
|
|
else
|
|
tr = TR_OUT_OF_MEMORY;
|
|
|
|
return(tr);
|
|
}
|
|
|
|
|
|
/****************************** Public Functions *****************************/
|
|
|
|
|
|
/*
|
|
** BeginTranslateFolder()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void BeginTranslateFolder(PFOLDERPAIR pfp)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
|
|
|
|
ASSERT(IsStubFlagClear(&(pfp->stub), STUB_FL_BEING_TRANSLATED));
|
|
ASSERT(IsStubFlagClear(&(pfp->pfpOther->stub), STUB_FL_BEING_TRANSLATED));
|
|
|
|
SetStubFlag(&(pfp->stub), STUB_FL_BEING_TRANSLATED);
|
|
SetStubFlag(&(pfp->pfpOther->stub), STUB_FL_BEING_TRANSLATED);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** EndTranslateFolder()
|
|
**
|
|
**
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
PUBLIC_CODE void EndTranslateFolder(PFOLDERPAIR pfp)
|
|
{
|
|
ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
|
|
|
|
ASSERT(IsStubFlagSet(&(pfp->stub), STUB_FL_BEING_TRANSLATED));
|
|
ASSERT(IsStubFlagSet(&(pfp->pfpOther->stub), STUB_FL_BEING_TRANSLATED));
|
|
|
|
ClearStubFlag(&(pfp->stub), STUB_FL_BEING_TRANSLATED);
|
|
ClearStubFlag(&(pfp->pfpOther->stub), STUB_FL_BEING_TRANSLATED);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
** CheckForSubtreeCycles()
|
|
**
|
|
** Checks to see if a proposed new folder subtree twin would create one or more
|
|
** cycles of folder subtree twins.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Returns: TWINRESULT
|
|
**
|
|
** Side Effects: none
|
|
**
|
|
** N.b., TR_SUBTREE_CYCLE_FOUND is returned if the folder subtree roots of the
|
|
** proposed new folder subtree twin are the same.
|
|
*/
|
|
PUBLIC_CODE TWINRESULT CheckForSubtreeCycles(HPTRARRAY hpaFolderPairs,
|
|
HPATH hpathFirstFolder,
|
|
HPATH hpathSecondFolder,
|
|
HSTRING hsName)
|
|
{
|
|
TWINRESULT tr;
|
|
|
|
ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
|
|
ASSERT(IS_VALID_HANDLE(hpathFirstFolder, PATH));
|
|
ASSERT(IS_VALID_HANDLE(hpathSecondFolder, PATH));
|
|
ASSERT(IS_VALID_HANDLE(hsName, STRING));
|
|
|
|
/* Are the folder twins cyclical on their own? */
|
|
|
|
if (SubtreesIntersect(hpathFirstFolder, hpathSecondFolder))
|
|
{
|
|
/* Yes. */
|
|
|
|
tr = TR_SUBTREE_CYCLE_FOUND;
|
|
|
|
WARNING_OUT((TEXT("CheckForSubtreeCycles(): Subtree cycle found connecting folders %s and %s."),
|
|
DebugGetPathString(hpathFirstFolder),
|
|
DebugGetPathString(hpathSecondFolder)));
|
|
}
|
|
else
|
|
{
|
|
LPCTSTR pcszName;
|
|
|
|
/* No. Check for any indirect subtree cycle. */
|
|
|
|
pcszName = GetString(hsName);
|
|
|
|
tr = CheckHalfForSubtreeCycle(hpaFolderPairs, hpathFirstFolder,
|
|
hpathSecondFolder, pcszName);
|
|
|
|
if (tr == TR_SUCCESS)
|
|
tr = CheckHalfForSubtreeCycle(hpaFolderPairs, hpathSecondFolder,
|
|
hpathFirstFolder, pcszName);
|
|
}
|
|
|
|
return(tr);
|
|
}
|
|
|