reclist.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 81k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. /*
  2.  * reclist.c - Reconciliation list ADT module.
  3.  */
  4. /* Headers
  5.  **********/
  6. #include "project.h"
  7. #pragma hdrstop
  8. #include "stub.h"
  9. #include "oleutil.h"
  10. /* Constants
  11.  ************/
  12. /* RECITEMACTION weights for folders returned by WeighFolderAction(). */
  13. /* (RECITEMACTION weights for files are the RECITEMACTION values.) */
  14. #define RIA_WT_COPY        (-2)
  15. #define RIA_WT_NOTHING     (-1)
  16. #define RIA_WT_DELETE      (+5)
  17. /* Types
  18.  ********/
  19. /* used to count the number of RECNODEs of each RECNODESTATE in a RECITEM */
  20. typedef struct _recnodestatecounter
  21. {
  22.    ULONG ulcUnavailable;
  23.    ULONG ulcDoesNotExist;
  24.    ULONG ulcDeleted;
  25.    ULONG ulcNotReconciled;
  26.    ULONG ulcUpToDate;
  27.    ULONG ulcChanged;
  28.    ULONG ulcNeverReconciled;
  29. }
  30. RECNODESTATECOUNTER;
  31. DECLARE_STANDARD_TYPES(RECNODESTATECOUNTER);
  32. /* DoesTwinFamilyNeedRec() callback structure */
  33. typedef struct _twinfamilyrecinfo
  34. {
  35.    TWINRESULT tr;
  36.    BOOL bNeedsRec;
  37. }
  38. TWINFAMILYRECINFO;
  39. DECLARE_STANDARD_TYPES(TWINFAMILYRECINFO);
  40. /***************************** Private Functions *****************************/
  41. /* Module Prototypes
  42.  ********************/
  43. PRIVATE_CODE RECNODESTATE DetermineRecNodeState(PCRECNODE);
  44. PRIVATE_CODE void AddRecNodeState(RECNODESTATE, PRECNODESTATECOUNTER);
  45. PRIVATE_CODE void CountRecNodeStates(PCRECITEM, PRECNODESTATECOUNTER, PULONG);
  46. PRIVATE_CODE void DetermineRecActions(PRECITEM);
  47. PRIVATE_CODE void BreakMergeIfNecessary(PRECITEM);
  48. PRIVATE_CODE TWINRESULT AddRecItemsToRecList(HTWINLIST, CREATERECLISTPROC, LPARAM, PRECLIST);
  49. PRIVATE_CODE void LinkUpRecList(PRECLIST, HPTRARRAY);
  50. PRIVATE_CODE int WeighFileAction(RECITEMACTION);
  51. PRIVATE_CODE int WeighFolderAction(RECITEMACTION);
  52. PRIVATE_CODE COMPARISONRESULT RecItemSortCmp(PCVOID, PCVOID);
  53. PRIVATE_CODE void DestroyArrayOfRecItems(HPTRARRAY);
  54. PRIVATE_CODE BOOL MarkTwinFamilyUsed(POBJECTTWIN, PVOID);
  55. PRIVATE_CODE ULONG MarkIntersectingTwinFamiliesUsed(HTWIN);
  56. PRIVATE_CODE void DestroyRecItem(PRECITEM);
  57. PRIVATE_CODE void DestroyRecNode(PRECNODE);
  58. PRIVATE_CODE void DestroyListOfRecItems(PRECITEM);
  59. PRIVATE_CODE void DestroyListOfRecNodes(PRECNODE);
  60. PRIVATE_CODE void MyDestroyRecList(PRECLIST);
  61. PRIVATE_CODE BOOL DeleteDeletedObjectTwins(PCRECITEM, PBOOL);
  62. PRIVATE_CODE BOOL FindAGeneratedObjectTwinProc(POBJECTTWIN, PVOID);
  63. PRIVATE_CODE BOOL FolderTwinShouldBeImplicitlyDeleted(PFOLDERPAIR);
  64. PRIVATE_CODE BOOL DeleteDeletedFolderTwins(HPTRARRAY);
  65. PRIVATE_CODE TWINRESULT CreateRecItem(PTWINFAMILY, PRECITEM *);
  66. PRIVATE_CODE TWINRESULT AddObjectTwinRecNode(PRECITEM, POBJECTTWIN);
  67. PRIVATE_CODE BOOL DoesTwinFamilyNeedRec(POBJECTTWIN, PVOID);
  68. PRIVATE_CODE TWINRESULT GetFolderPairStatus(PFOLDERPAIR, CREATERECLISTPROC, LPARAM, PFOLDERTWINSTATUS);
  69. #ifdef DEBUG
  70. PRIVATE_CODE BOOL IsValidCreateRecListProcMsg(UINT);
  71. PRIVATE_CODE BOOL IsValidFOLDERTWINSTATUS(FOLDERTWINSTATUS);
  72. PRIVATE_CODE BOOL IsValidPCRECNODESTATECOUNTER(PCRECNODESTATECOUNTER);
  73. #endif
  74. #if defined(DEBUG) || defined(VSTF)
  75. PRIVATE_CODE BOOL IsValidRECNODESTATE(RECNODESTATE);
  76. PRIVATE_CODE BOOL IsValidRECNODEACTION(RECNODEACTION);
  77. PRIVATE_CODE BOOL IsValidRECITEMACTION(RECITEMACTION);
  78. PRIVATE_CODE BOOL IsValidPCRECLIST(PCRECLIST);
  79. #endif
  80. /*
  81. ** DetermineRecNodeState()
  82. **
  83. **
  84. **
  85. ** Arguments:
  86. **
  87. ** Returns:
  88. **
  89. ** Side Effects:  none
  90. */
  91. PRIVATE_CODE RECNODESTATE DetermineRecNodeState(PCRECNODE pcrn)
  92. {
  93.    RECNODESTATE rnstate;
  94.    ASSERT(IS_VALID_WRITE_PTR(pcrn, RECNODE));
  95.    if (pcrn->fsCurrent.fscond != FS_COND_UNAVAILABLE)
  96.    {
  97.       if (IsReconciledFileStamp(&(pcrn->fsLast)))
  98.       {
  99.          if (pcrn->fsCurrent.fscond == FS_COND_EXISTS)
  100.          {
  101.             BOOL bReconciledLastTime;
  102.             bReconciledLastTime = (IsStubFlagClear(&(((PCOBJECTTWIN)(pcrn->hObjectTwin))->stub),
  103.                                                    STUB_FL_NOT_RECONCILED));
  104.             if (MyCompareFileStamps(&(pcrn->fsLast), &(pcrn->fsCurrent))
  105.                 == CR_EQUAL)
  106.             {
  107.                if (bReconciledLastTime)
  108.                   rnstate = RNS_UP_TO_DATE;
  109.                else
  110.                   rnstate = RNS_NOT_RECONCILED;
  111.             }
  112.             else
  113.             {
  114.                if (bReconciledLastTime)
  115.                   rnstate = RNS_CHANGED;
  116.                else
  117.                   /* Divergent version. */
  118.                   rnstate = RNS_NEVER_RECONCILED;
  119.             }
  120.          }
  121.          else
  122.          {
  123.             ASSERT(pcrn->fsCurrent.fscond == FS_COND_DOES_NOT_EXIST);
  124.             rnstate = RNS_DELETED;
  125.          }
  126.       }
  127.       else
  128.       {
  129.          if (pcrn->fsCurrent.fscond == FS_COND_EXISTS)
  130.             rnstate = RNS_NEVER_RECONCILED;
  131.          else
  132.          {
  133.             ASSERT(pcrn->fsCurrent.fscond == FS_COND_DOES_NOT_EXIST);
  134.             rnstate = RNS_DOES_NOT_EXIST;
  135.          }
  136.       }
  137.    }
  138.    else
  139.    {
  140.       /* Deleted wins over unavailable. */
  141.       if (pcrn->fsLast.fscond == FS_COND_DOES_NOT_EXIST)
  142.          rnstate = RNS_DELETED;
  143.       else
  144.          rnstate = RNS_UNAVAILABLE;
  145.    }
  146.    /* Collapse folder RECNODE states. */
  147.    if (IsFolderObjectTwinName(pcrn->priParent->pcszName))
  148.    {
  149.       switch (rnstate)
  150.       {
  151.          case RNS_NEVER_RECONCILED:
  152.          case RNS_NOT_RECONCILED:
  153.          case RNS_CHANGED:
  154.             rnstate = RNS_UP_TO_DATE;
  155.             break;
  156.       }
  157.    }
  158.    ASSERT(IsValidRECNODESTATE(rnstate));
  159.    return(rnstate);
  160. }
  161. /*
  162. ** AddRecNodeState()
  163. **
  164. **
  165. **
  166. ** Arguments:
  167. **
  168. ** Returns:
  169. **
  170. ** Side Effects:  none
  171. */
  172. PRIVATE_CODE void AddRecNodeState(RECNODESTATE rnstate,
  173.                                   PRECNODESTATECOUNTER prnscntr)
  174. {
  175.    ASSERT(IsValidRECNODESTATE(rnstate));
  176.    ASSERT(IS_VALID_STRUCT_PTR(prnscntr, CRECNODESTATECOUNTER));
  177.    switch (rnstate)
  178.    {
  179.       case RNS_UNAVAILABLE:
  180.          ASSERT(prnscntr->ulcUnavailable < ULONG_MAX);
  181.          prnscntr->ulcUnavailable++;
  182.          break;
  183.       case RNS_DOES_NOT_EXIST:
  184.          ASSERT(prnscntr->ulcDoesNotExist < ULONG_MAX);
  185.          prnscntr->ulcDoesNotExist++;
  186.          break;
  187.       case RNS_DELETED:
  188.          ASSERT(prnscntr->ulcDeleted < ULONG_MAX);
  189.          prnscntr->ulcDeleted++;
  190.          break;
  191.       case RNS_NOT_RECONCILED:
  192.          ASSERT(prnscntr->ulcNotReconciled < ULONG_MAX);
  193.          prnscntr->ulcNotReconciled++;
  194.          break;
  195.       case RNS_UP_TO_DATE:
  196.          ASSERT(prnscntr->ulcUpToDate < ULONG_MAX);
  197.          prnscntr->ulcUpToDate++;
  198.          break;
  199.       case RNS_CHANGED:
  200.          ASSERT(prnscntr->ulcChanged < ULONG_MAX);
  201.          prnscntr->ulcChanged++;
  202.          break;
  203.       default:
  204.          ASSERT(rnstate == RNS_NEVER_RECONCILED);
  205.          ASSERT(prnscntr->ulcNeverReconciled < ULONG_MAX);
  206.          prnscntr->ulcNeverReconciled++;
  207.          break;
  208.    }
  209.    return;
  210. }
  211. /*
  212. ** CountRecNodeStates()
  213. **
  214. **
  215. **
  216. ** Arguments:
  217. **
  218. ** Returns:
  219. **
  220. ** Side Effects:  none
  221. */
  222. PRIVATE_CODE void CountRecNodeStates(PCRECITEM pcri,
  223.                                      PRECNODESTATECOUNTER prnscntr,
  224.                                      PULONG pulcToDelete)
  225. {
  226.    PCRECNODE pcrn;
  227.    ASSERT(IS_VALID_READ_PTR(pcri, CRECITEM));
  228.    ASSERT(IS_VALID_STRUCT_PTR(prnscntr, CRECNODESTATECOUNTER));
  229.    ASSERT(IS_VALID_WRITE_PTR(pulcToDelete, ULONG));
  230.    ZeroMemory(prnscntr, sizeof(*prnscntr));
  231.    *pulcToDelete = 0;
  232.    for (pcrn = pcri->prnFirst; pcrn; pcrn = pcrn->prnNext)
  233.    {
  234.       AddRecNodeState(pcrn->rnstate, prnscntr);
  235.       if (pcrn->rnstate == RNS_UP_TO_DATE &&
  236.           IsStubFlagClear(&(((PCOBJECTTWIN)(pcrn->hObjectTwin))->stub),
  237.                           STUB_FL_KEEP))
  238.       {
  239.          ASSERT(*pulcToDelete < ULONG_MAX);
  240.          (*pulcToDelete)++;
  241.       }
  242.    }
  243.    ASSERT(IS_VALID_STRUCT_PTR(prnscntr, CRECNODESTATECOUNTER));
  244.    ASSERT(prnscntr->ulcUnavailable +
  245.           prnscntr->ulcDoesNotExist +
  246.           prnscntr->ulcDeleted +
  247.           prnscntr->ulcNotReconciled +
  248.           prnscntr->ulcUpToDate +
  249.           prnscntr->ulcChanged +
  250.           prnscntr->ulcNeverReconciled == pcri->ulcNodes);
  251.    ASSERT(*pulcToDelete <= prnscntr->ulcUpToDate);
  252.    return;
  253. }
  254. /*
  255. ** DetermineRecActions()
  256. **
  257. **
  258. **
  259. ** Arguments:
  260. **
  261. ** Returns:       TWINRESULT
  262. **
  263. ** Side Effects:  none
  264. */
  265. PRIVATE_CODE void DetermineRecActions(PRECITEM pri)
  266. {
  267.    RECNODESTATECOUNTER rnscntr;
  268.    ULONG ulcToDelete;
  269.    RECITEMACTION riaSummary = RIA_NOTHING;
  270.    RECNODEACTION rnaDoesNotExist = RNA_NOTHING;
  271.    RECNODEACTION rnaNotReconciled = RNA_NOTHING;
  272.    RECNODEACTION rnaUpToDateSrc = RNA_NOTHING;
  273.    RECNODEACTION rnaUpToDate = RNA_NOTHING;
  274.    RECNODEACTION rnaChanged = RNA_NOTHING;
  275.    RECNODEACTION rnaNeverReconciled = RNA_NOTHING;
  276.    BOOL bNeedUpToDateCopySrc = FALSE;
  277.    BOOL bNeedNotReconciledCopySrc = FALSE;
  278.    PRECNODE prn;
  279.    ASSERT(IS_VALID_WRITE_PTR(pri, RECITEM));
  280.    ZeroMemory(&rnscntr, sizeof(rnscntr));
  281.    CountRecNodeStates(pri, &rnscntr, &ulcToDelete);
  282.    if (rnscntr.ulcNeverReconciled > 0)
  283.    {
  284.       if (rnscntr.ulcChanged > 0)
  285.       {
  286.          riaSummary = RIA_MERGE;
  287.          rnaNeverReconciled = RNA_MERGE_ME;
  288.          rnaChanged = RNA_MERGE_ME;
  289.          rnaUpToDate = RNA_COPY_TO_ME;
  290.          rnaNotReconciled = RNA_COPY_TO_ME;
  291.          rnaDoesNotExist = RNA_COPY_TO_ME;
  292.       }
  293.       else if (rnscntr.ulcUpToDate > 0)
  294.       {
  295.          riaSummary = RIA_MERGE;
  296.          rnaNeverReconciled = RNA_MERGE_ME;
  297.          rnaUpToDate = RNA_MERGE_ME;
  298.          rnaNotReconciled = RNA_COPY_TO_ME;
  299.          rnaDoesNotExist = RNA_COPY_TO_ME;
  300.       }
  301.       else if (rnscntr.ulcNotReconciled > 0)
  302.       {
  303.          riaSummary = RIA_MERGE;
  304.          rnaNeverReconciled = RNA_MERGE_ME;
  305.          rnaNotReconciled = RNA_MERGE_ME;
  306.          rnaDoesNotExist = RNA_COPY_TO_ME;
  307.       }
  308.       else if (rnscntr.ulcNeverReconciled >= 2)
  309.       {
  310.          riaSummary = RIA_MERGE;
  311.          rnaNeverReconciled = RNA_MERGE_ME;
  312.          rnaDoesNotExist = RNA_COPY_TO_ME;
  313.       }
  314.       else if (rnscntr.ulcDoesNotExist > 0)
  315.       {
  316.          ASSERT(rnscntr.ulcNeverReconciled == 1);
  317.          riaSummary = RIA_COPY;
  318.          rnaNeverReconciled = RNA_COPY_FROM_ME;
  319.          rnaDoesNotExist = RNA_COPY_TO_ME;
  320.       }
  321.    }
  322.    else if (rnscntr.ulcChanged >= 2)
  323.    {
  324.       riaSummary = RIA_MERGE;
  325.       rnaChanged = RNA_MERGE_ME;
  326.       rnaUpToDate = RNA_COPY_TO_ME;
  327.       rnaNotReconciled = RNA_COPY_TO_ME;
  328.       rnaDoesNotExist = RNA_COPY_TO_ME;
  329.    }
  330.    else if (rnscntr.ulcChanged == 1)
  331.    {
  332.       if (rnscntr.ulcUpToDate > 0 ||
  333.           rnscntr.ulcNotReconciled > 0 ||
  334.           rnscntr.ulcDoesNotExist > 0)
  335.       {
  336.          riaSummary = RIA_COPY;
  337.          rnaChanged = RNA_COPY_FROM_ME;
  338.          rnaUpToDate = RNA_COPY_TO_ME;
  339.          rnaNotReconciled = RNA_COPY_TO_ME;
  340.          rnaDoesNotExist = RNA_COPY_TO_ME;
  341.       }
  342.    }
  343.    else if (IsTwinFamilyDeletionPending((PCTWINFAMILY)(pri->hTwinFamily)))
  344.    {
  345.       if (ulcToDelete > 0)
  346.       {
  347.          riaSummary = RIA_DELETE;
  348.          rnaNotReconciled = RNA_DELETE_ME;
  349.          rnaUpToDate = RNA_DELETE_ME;
  350.       }
  351.    }
  352.    else if (rnscntr.ulcUpToDate > 0)
  353.    {
  354.       if (rnscntr.ulcNotReconciled > 0 ||
  355.           rnscntr.ulcDoesNotExist > 0)
  356.       {
  357.          riaSummary = RIA_COPY;
  358.          bNeedUpToDateCopySrc = TRUE;
  359.          rnaNotReconciled = RNA_COPY_TO_ME;
  360.          rnaDoesNotExist = RNA_COPY_TO_ME;
  361.       }
  362.    }
  363.    else if (rnscntr.ulcNotReconciled > 0)
  364.    {
  365.       if (rnscntr.ulcDoesNotExist > 0)
  366.       {
  367.          riaSummary = RIA_COPY;
  368.          bNeedNotReconciledCopySrc = TRUE;
  369.          rnaDoesNotExist = RNA_COPY_TO_ME;
  370.       }
  371.    }
  372.    /* Apply determined actions. */
  373.    ASSERT(! (bNeedUpToDateCopySrc && bNeedNotReconciledCopySrc));
  374.    for (prn = pri->prnFirst; prn; prn = prn->prnNext)
  375.    {
  376.       switch (prn->rnstate)
  377.       {
  378.          case RNS_NEVER_RECONCILED:
  379.             prn->rnaction = rnaNeverReconciled;
  380.             break;
  381.          case RNS_DOES_NOT_EXIST:
  382.             prn->rnaction = rnaDoesNotExist;
  383.             break;
  384.          case RNS_NOT_RECONCILED:
  385.             if (bNeedNotReconciledCopySrc)
  386.             {
  387.                prn->rnaction = RNA_COPY_FROM_ME;
  388.                bNeedNotReconciledCopySrc = FALSE;
  389.             }
  390.             else
  391.                prn->rnaction = rnaNotReconciled;
  392.             break;
  393.          case RNS_UP_TO_DATE:
  394.             if (bNeedUpToDateCopySrc)
  395.             {
  396.                prn->rnaction = RNA_COPY_FROM_ME;
  397.                bNeedUpToDateCopySrc = FALSE;
  398.             }
  399.             else
  400.                prn->rnaction = rnaUpToDate;
  401.             break;
  402.          case RNS_CHANGED:
  403.             prn->rnaction = rnaChanged;
  404.             break;
  405.          default:
  406.             ASSERT(prn->rnstate == RNS_UNAVAILABLE ||
  407.                    prn->rnstate == RNS_DELETED);
  408.             prn->rnaction = RNA_NOTHING;
  409.             break;
  410.       }
  411.       if (prn->rnaction == RNA_DELETE_ME)
  412.       {
  413.          if (IsStubFlagClear(&(((PCOBJECTTWIN)(prn->hObjectTwin))->stub),
  414.              STUB_FL_KEEP))
  415.             SET_FLAG(prn->dwFlags, RN_FL_DELETION_SUGGESTED);
  416.          else
  417.             prn->rnaction = RNA_NOTHING;
  418.       }
  419.    }
  420.    pri->riaction = riaSummary;
  421.    /* Break a merge if no reconciliation handler is registered. */
  422.    if (pri->riaction == RIA_MERGE)
  423.       BreakMergeIfNecessary(pri);
  424.    ASSERT(IS_VALID_STRUCT_PTR(pri, CRECITEM));
  425.    return;
  426. }
  427. /*
  428. ** BreakMergeIfNecessary()
  429. **
  430. **
  431. **
  432. ** Arguments:
  433. **
  434. ** Returns:
  435. **
  436. ** Side Effects:  none
  437. */
  438. PRIVATE_CODE void BreakMergeIfNecessary(PRECITEM pri)
  439. {
  440.    PRECNODE prnMergeDest;
  441.    TCHAR rgchPath[MAX_PATH_LEN];
  442.    CLSID clsidReconciler;
  443.    ASSERT(IS_VALID_STRUCT_PTR(pri, CRECITEM));
  444.    ASSERT(pri->riaction == RIA_MERGE);
  445.    /* Is a class reconciler registered for this RECITEM? */
  446.    ChooseMergeDestination(pri, &prnMergeDest);
  447.    ASSERT(prnMergeDest->priParent == pri);
  448.    ComposePath(rgchPath, prnMergeDest->pcszFolder, prnMergeDest->priParent->pcszName);
  449.    ASSERT(lstrlen(rgchPath) < ARRAYSIZE(rgchPath));
  450.    if (FAILED(GetReconcilerClassID(rgchPath, &clsidReconciler)))
  451.    {
  452.       pri->riaction = RIA_BROKEN_MERGE;
  453.       TRACE_OUT((TEXT("MassageMergeCase(): Breaking merge RECITEM for %s.  No registered reconciliation handler."),
  454.                  pri->pcszName));
  455.    }
  456.    return;
  457. }
  458. /*
  459. ** AddRecItemsToRecList()
  460. **
  461. **
  462. **
  463. ** Arguments:
  464. **
  465. ** Returns:       TWINRESULT
  466. **
  467. ** Side Effects:  none
  468. */
  469. PRIVATE_CODE TWINRESULT AddRecItemsToRecList(HTWINLIST htl,
  470.                                              CREATERECLISTPROC crlp,
  471.                                              LPARAM lpCallbackData,
  472.                                              PRECLIST prl)
  473. {
  474.    TWINRESULT tr = TR_SUCCESS;
  475.    HBRFCASE hbr;
  476.    HPTRARRAY hpaTwinFamilies;
  477.    ARRAYINDEX aicTwins;
  478.    ARRAYINDEX ai;
  479.    ULONG ulcMarkedTwinFamilies = 0;
  480.    NEWPTRARRAY npa;
  481.    HPTRARRAY hpaRecItems;
  482.    /* lpCallbackData may be any value. */
  483.    ASSERT(IS_VALID_HANDLE(htl, TWINLIST));
  484.    ASSERT(! crlp ||
  485.           IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
  486.    ASSERT(IS_VALID_STRUCT_PTR(prl, CRECLIST));
  487.    /*
  488.     * "Used" twin families are twin families that are to be added to the
  489.     * reconciliation list as RECITEMs.
  490.     */
  491.    /* Mark all twin families unused. */
  492.    hbr = GetTwinListBriefcase(htl);
  493.    hpaTwinFamilies = GetBriefcaseTwinFamilyPtrArray(hbr);
  494.    ClearFlagInArrayOfStubs(hpaTwinFamilies, STUB_FL_USED);
  495.    /* Mark twin families that intersect twins in twin list as used. */
  496.    aicTwins = GetTwinListCount(htl);
  497.    for (ai = 0; ai < aicTwins; ai++)
  498.    {
  499.       ULONG ulcNewlyMarked;
  500.       ulcNewlyMarked = MarkIntersectingTwinFamiliesUsed(GetTwinFromTwinList(htl, ai));
  501.       ASSERT(ulcMarkedTwinFamilies <= ULONG_MAX - ulcNewlyMarked);
  502.       ulcMarkedTwinFamilies += ulcNewlyMarked;
  503.    }
  504.    /* Create a PTRARRAY to keep track of the RECITEMs created. */
  505.    npa.aicInitialPtrs = ulcMarkedTwinFamilies;
  506.    npa.aicAllocGranularity = 1;
  507.    npa.dwFlags = 0;
  508.    if (CreatePtrArray(&npa, &hpaRecItems))
  509.    {
  510.       ARRAYINDEX aicPtrs;
  511.       /* Add the marked twin families to the RECLIST as RECITEMS. */
  512.       aicPtrs = GetPtrCount(hpaTwinFamilies);
  513.       ai = 0;
  514.       while (ai < aicPtrs && ulcMarkedTwinFamilies > 0)
  515.       {
  516.          PTWINFAMILY ptf;
  517.          ptf = GetPtr(hpaTwinFamilies, ai);
  518.          ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  519.          if (IsStubFlagSet(&(ptf->stub), STUB_FL_USED))
  520.          {
  521.             PRECITEM priNew;
  522.             ulcMarkedTwinFamilies--;
  523.             tr = CreateRecItem(ptf, &priNew);
  524.             if (tr == TR_SUCCESS)
  525.             {
  526.                ARRAYINDEX ai;
  527.                if (AddPtr(hpaRecItems, NULL, priNew, &ai))
  528.                {
  529.                   if (! NotifyCreateRecListStatus(crlp, CRLS_DELTA_CREATE_REC_LIST,
  530.                                                   0, lpCallbackData))
  531.                      tr = TR_ABORT;
  532.                }
  533.                else
  534.                   tr = TR_OUT_OF_MEMORY;
  535.             }
  536.             if (tr != TR_SUCCESS)
  537.                break;
  538.          }
  539.          ai++;
  540.       }
  541.       if (tr == TR_SUCCESS)
  542.       {
  543.          ASSERT(! ulcMarkedTwinFamilies);
  544.          LinkUpRecList(prl, hpaRecItems);
  545.       }
  546.       else
  547.          DestroyArrayOfRecItems(hpaRecItems);
  548.       DestroyPtrArray(hpaRecItems);
  549.    }
  550.    else
  551.       tr = TR_OUT_OF_MEMORY;
  552.    return(tr);
  553. }
  554. /*
  555. ** WeighFileAction()
  556. **
  557. **
  558. **
  559. ** Arguments:
  560. **
  561. ** Returns:
  562. **
  563. ** Side Effects:  none
  564. */
  565. PRIVATE_CODE int WeighFileAction(RECITEMACTION riaction)
  566. {
  567.    ASSERT(IsValidRECITEMACTION(riaction));
  568.    return(riaction);
  569. }
  570. /*
  571. ** WeighFolderAction()
  572. **
  573. **
  574. **
  575. ** Arguments:
  576. **
  577. ** Returns:
  578. **
  579. ** Side Effects:  none
  580. */
  581. PRIVATE_CODE int WeighFolderAction(RECITEMACTION riaction)
  582. {
  583.    int nWeight;
  584.    ASSERT(IsValidRECITEMACTION(riaction));
  585.    switch (riaction)
  586.    {
  587.       case RIA_COPY:
  588.          nWeight = RIA_WT_COPY;
  589.          break;
  590.       case RIA_NOTHING:
  591.          nWeight = RIA_WT_NOTHING;
  592.          break;
  593.       default:
  594.          ASSERT(riaction == RIA_DELETE);
  595.          nWeight = RIA_WT_DELETE;
  596.          break;
  597.    }
  598.    return(nWeight);
  599. }
  600. /*
  601. ** RecItemSortCmp()
  602. **
  603. **
  604. **
  605. ** Arguments:
  606. **
  607. ** Returns:
  608. **
  609. ** Side Effects:  none
  610. **
  611. ** RECITEMs are sorted in the following order:
  612. **    1) create folder
  613. **    2) do nothing to folder
  614. **    3) do nothing to file
  615. **    4) delete file
  616. **    5) copy file
  617. **    6) merge file
  618. **    7) broken merge file
  619. **    8) delete folder
  620. ** and then by:
  621. **    1) name
  622. */
  623. PRIVATE_CODE COMPARISONRESULT RecItemSortCmp(PCVOID pcriFirst,
  624.                                              PCVOID pcriSecond)
  625. {
  626.    COMPARISONRESULT cr;
  627.    BOOL bFirstFile;
  628.    BOOL bSecondFile;
  629.    int nFirstWeight;
  630.    int nSecondWeight;
  631.    ASSERT(IS_VALID_STRUCT_PTR(pcriFirst, CRECITEM));
  632.    ASSERT(IS_VALID_STRUCT_PTR(pcriSecond, CRECITEM));
  633.    bFirstFile = (*(((PCRECITEM)pcriFirst)->pcszName) != TEXT(''));
  634.    bSecondFile = (*(((PCRECITEM)pcriSecond)->pcszName) != TEXT(''));
  635.    if (bFirstFile)
  636.       nFirstWeight = WeighFileAction(((PCRECITEM)pcriFirst)->riaction);
  637.    else
  638.       nFirstWeight = WeighFolderAction(((PCRECITEM)pcriFirst)->riaction);
  639.    if (bSecondFile)
  640.       nSecondWeight = WeighFileAction(((PCRECITEM)pcriSecond)->riaction);
  641.    else
  642.       nSecondWeight = WeighFolderAction(((PCRECITEM)pcriSecond)->riaction);
  643.    cr = CompareInts(nFirstWeight, nSecondWeight);
  644.    if (cr == CR_EQUAL)
  645.       cr = CompareNameStrings(((PCRECITEM)pcriFirst)->pcszName,
  646.                               ((PCRECITEM)pcriSecond)->pcszName);
  647.    return(cr);
  648. }
  649. /*
  650. ** LinkUpRecList()
  651. **
  652. **
  653. **
  654. ** Arguments:
  655. **
  656. ** Returns:
  657. **
  658. ** Side Effects:  none
  659. */
  660. PRIVATE_CODE void LinkUpRecList(PRECLIST prl, HPTRARRAY hpaRecItems)
  661. {
  662.    ARRAYINDEX ai;
  663.    ARRAYINDEX aicPtrs;
  664.    ASSERT(IS_VALID_STRUCT_PTR(prl, CRECLIST));
  665.    ASSERT(IS_VALID_HANDLE(hpaRecItems, PTRARRAY));
  666.    SortPtrArray(hpaRecItems, &RecItemSortCmp);
  667.    aicPtrs = GetPtrCount(hpaRecItems);
  668.    for (ai = aicPtrs; ai > 0; ai--)
  669.    {
  670.       PRECITEM pri;
  671.       pri = GetPtr(hpaRecItems, ai - 1);
  672.       pri->priNext = prl->priFirst;
  673.       prl->priFirst = pri;
  674.    }
  675.    prl->ulcItems = aicPtrs;
  676.    ASSERT(IS_VALID_STRUCT_PTR(prl, CRECLIST));
  677.    return;
  678. }
  679. /*
  680. ** DestroyArrayOfRecItems()
  681. **
  682. **
  683. **
  684. ** Arguments:
  685. **
  686. ** Returns:
  687. **
  688. ** Side Effects:  none
  689. */
  690. PRIVATE_CODE void DestroyArrayOfRecItems(HPTRARRAY hpaRecItems)
  691. {
  692.    ARRAYINDEX ai;
  693.    ARRAYINDEX aicPtrs;
  694.    ASSERT(IS_VALID_HANDLE(hpaRecItems, PTRARRAY));
  695.    aicPtrs = GetPtrCount(hpaRecItems);
  696.    for (ai = 0; ai < aicPtrs; ai++)
  697.       DestroyRecItem(GetPtr(hpaRecItems, ai));
  698.    return;
  699. }
  700. /*
  701. ** MarkTwinFamilyUsed()
  702. **
  703. **
  704. **
  705. ** Arguments:
  706. **
  707. ** Returns:
  708. **
  709. ** Side Effects:  none
  710. */
  711. #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
  712. PRIVATE_CODE BOOL MarkTwinFamilyUsed(POBJECTTWIN pot, PVOID pulcNewlyMarked)
  713. {
  714.    ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  715.    ASSERT(IS_VALID_WRITE_PTR(pulcNewlyMarked, ULONG));
  716.    if (IsStubFlagClear(&(pot->ptfParent->stub), STUB_FL_USED))
  717.    {
  718.       /* Mark the twin family used. */
  719.       SetStubFlag(&(pot->ptfParent->stub), STUB_FL_USED);
  720.       ASSERT(*(PULONG)pulcNewlyMarked < ULONG_MAX);
  721.       (*(PULONG)pulcNewlyMarked)++;
  722.    }
  723.    return(TRUE);
  724. }
  725. #pragma warning(default:4100) /* "unreferenced formal parameter" warning */
  726. /*
  727. ** MarkIntersectingTwinFamiliesUsed()
  728. **
  729. ** Marks the twin families that intersect a twin as used.
  730. **
  731. ** Arguments:     htwin - handle to intersecting twin
  732. **
  733. ** Returns:       Number of twin families newly marked used.
  734. **
  735. ** Side Effects:  none
  736. */
  737. PRIVATE_CODE ULONG MarkIntersectingTwinFamiliesUsed(HTWIN htwin)
  738. {
  739.    ULONG ulcNewlyMarked = 0;
  740.    ASSERT(IS_VALID_HANDLE(htwin, TWIN));
  741.    /* Skip deleted twins. */
  742.    if (IsStubFlagClear((PCSTUB)htwin, STUB_FL_UNLINKED))
  743.    {
  744.       /* Determine intersecting twin families based upon type of twin. */
  745.       switch (((PSTUB)htwin)->st)
  746.       {
  747.          case ST_OBJECTTWIN:
  748.             if (IsStubFlagClear(&(((POBJECTTWIN)htwin)->ptfParent->stub),
  749.                                 STUB_FL_USED))
  750.             {
  751.                /* Mark the twin family of the object twin as used. */
  752.                SetStubFlag(&(((POBJECTTWIN)htwin)->ptfParent->stub),
  753.                            STUB_FL_USED);
  754.                ulcNewlyMarked++;
  755.             }
  756.             break;
  757.          case ST_TWINFAMILY:
  758.             if (IsStubFlagClear(&(((PTWINFAMILY)htwin)->stub), STUB_FL_USED))
  759.             {
  760.                /* Mark the twin family used. */
  761.                SetStubFlag(&(((PTWINFAMILY)htwin)->stub), STUB_FL_USED);
  762.                ulcNewlyMarked++;
  763.             }
  764.             break;
  765.          default:
  766.             /*
  767.              * Mark the twin families of the generated object twins from one of
  768.              * the folder twins as used.  (Only one of the two lists of object
  769.              * twins needs to be added since the other list should contain
  770.              * object twins in exactly the same twin families as the first.)
  771.              */
  772.             ASSERT(((PSTUB)htwin)->st == ST_FOLDERPAIR);
  773.             EVAL(EnumGeneratedObjectTwins((PCFOLDERPAIR)htwin,
  774.                                           &MarkTwinFamilyUsed,
  775.                                           &ulcNewlyMarked));
  776.             break;
  777.       }
  778.    }
  779.    return(ulcNewlyMarked);
  780. }
  781. /*
  782. ** DestroyRecItem()
  783. **
  784. **
  785. **
  786. ** Arguments:
  787. **
  788. ** Returns:       TWINRESULT
  789. **
  790. ** Side Effects:  none
  791. */
  792. PRIVATE_CODE void DestroyRecItem(PRECITEM pri)
  793. {
  794.    ASSERT(IS_VALID_STRUCT_PTR(pri, CRECITEM));
  795.    /* Destroy the RECITEM's list of RECNODES. */
  796.    DestroyListOfRecNodes(pri->prnFirst);
  797.    /* Now unlock the twin family stub associated with the RECITEM. */
  798.    UnlockStub(&(((PTWINFAMILY)(pri->hTwinFamily))->stub));
  799.    FreeMemory(pri);
  800.    return;
  801. }
  802. /*
  803. ** DestroyRecNode()
  804. **
  805. **
  806. **
  807. ** Arguments:
  808. **
  809. ** Returns:
  810. **
  811. ** Side Effects:  none
  812. */
  813. PRIVATE_CODE void DestroyRecNode(PRECNODE prn)
  814. {
  815.    ASSERT(IS_VALID_STRUCT_PTR(prn, CRECNODE));
  816.    /* Unlock the object twin stub associated with the RECNODE. */
  817.    UnlockStub(&(((POBJECTTWIN)(prn->hObjectTwin))->stub));
  818.    FreeMemory((LPTSTR)(prn->pcszFolder));
  819.    FreeMemory(prn);
  820.    return;
  821. }
  822. /*
  823. ** DestroyListOfRecItems()
  824. **
  825. ** Destroys a list of reconciliation items.
  826. **
  827. ** Arguments:     priHead - pointer to first reconciliation item in list
  828. **
  829. ** Returns:       TWINRESULT
  830. **
  831. ** Side Effects:  none
  832. */
  833. PRIVATE_CODE void DestroyListOfRecItems(PRECITEM priHead)
  834. {
  835.    while (priHead)
  836.    {
  837.       PRECITEM priPrev;
  838.       ASSERT(IS_VALID_STRUCT_PTR(priHead, CRECITEM));
  839.       priPrev = priHead;
  840.       priHead = priHead->priNext;
  841.       DestroyRecItem(priPrev);
  842.    }
  843.    return;
  844. }
  845. /*
  846. ** DestroyListOfRecNodes()
  847. **
  848. **
  849. **
  850. ** Arguments:
  851. **
  852. ** Returns:       TWINRESULT
  853. **
  854. ** Side Effects:  none
  855. */
  856. PRIVATE_CODE void DestroyListOfRecNodes(PRECNODE prnHead)
  857. {
  858.    while (prnHead)
  859.    {
  860.       PRECNODE prnPrev;
  861.       ASSERT(IS_VALID_STRUCT_PTR(prnHead, CRECNODE));
  862.       prnPrev = prnHead;
  863.       prnHead = prnHead->prnNext;
  864.       DestroyRecNode(prnPrev);
  865.    }
  866.    return;
  867. }
  868. /*
  869. ** MyDestroyRecList()
  870. **
  871. **
  872. **
  873. ** Arguments:
  874. **
  875. ** Returns:
  876. **
  877. ** Side Effects:  none
  878. */
  879. PRIVATE_CODE void MyDestroyRecList(PRECLIST prl)
  880. {
  881.    ASSERT(IS_VALID_STRUCT_PTR(prl, CRECLIST));
  882.    DestroyListOfRecItems(prl->priFirst);
  883.    FreeMemory(prl);
  884.    return;
  885. }
  886. /*
  887. ** DeleteDeletedObjectTwins()
  888. **
  889. ** Performs garbage collection of obsolete object twins.
  890. **
  891. ** Arguments:
  892. **
  893. ** Returns:
  894. **
  895. ** Side Effects:  May implicitly delete twin family.  Marks generating folder
  896. **                twins used.
  897. **
  898. ** Deletes the following:
  899. **    1) Any reconciled object twin last known non-existent whose twin family
  900. **       is not in the deletion pending state.  This may cause the parent twin
  901. **       family to be implicitly deleted as a result.
  902. **    2) Any twin family all of whose object twins are last known non-existent.
  903. */
  904. PRIVATE_CODE BOOL DeleteDeletedObjectTwins(PCRECITEM pcri,
  905.                                            PBOOL pbAnyFolderTwinsMarked)
  906. {
  907.    BOOL bAnyDeleted = FALSE;
  908.    ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
  909.    ASSERT(IS_VALID_WRITE_PTR(pbAnyFolderTwinsMarked, BOOL));
  910.    *pbAnyFolderTwinsMarked = FALSE;
  911.    if (! IsTwinFamilyDeletionPending((PCTWINFAMILY)(pcri->hTwinFamily)))
  912.    {
  913.       ULONG ulcNonExistent = 0;
  914.       PRECNODE prn;
  915.       PTWINFAMILY ptf;
  916.       for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  917.       {
  918.          if (LastKnownNonExistent(&(prn->fsLast), &(prn->fsCurrent)))
  919.          {
  920.             ASSERT(ulcNonExistent < ULONG_MAX);
  921.             ulcNonExistent++;
  922.             if (IsReconciledFileStamp(&(prn->fsLast)))
  923.             {
  924.                POBJECTTWIN pot;
  925.                pot = (POBJECTTWIN)(prn->hObjectTwin);
  926.                if (! pot->ulcSrcFolderTwins)
  927.                {
  928.                   DestroyStub(&(pot->stub));
  929.                   TRACE_OUT((TEXT("DeleteDeletedObjectTwins(): Implicitly removed object twin for deleted file %s\%s."),
  930.                              prn->pcszFolder,
  931.                              prn->priParent->pcszName));
  932.                }
  933.                else
  934.                {
  935.                   ULONG ulcFolderTwins;
  936.                   ClearStubFlag(&(pot->stub), STUB_FL_FROM_OBJECT_TWIN);
  937.                   EVAL(EnumGeneratingFolderTwins(
  938.                            pot,
  939.                            (ENUMGENERATINGFOLDERTWINSPROC)&SetStubFlagWrapper,
  940.                            (PVOID)STUB_FL_USED, &ulcFolderTwins));
  941.                   *pbAnyFolderTwinsMarked = (ulcFolderTwins > 0);
  942.                }
  943.                bAnyDeleted = TRUE;
  944.             }
  945.          }
  946.       }
  947.       ptf = (PTWINFAMILY)(pcri->hTwinFamily);
  948.       if (ulcNonExistent == pcri->ulcNodes &&
  949.           IsStubFlagClear(&(ptf->stub), STUB_FL_UNLINKED))
  950.       {
  951.          ClearTwinFamilySrcFolderTwinCount(ptf);
  952.          EVAL(DestroyStub(&(ptf->stub)) == TR_SUCCESS);
  953.          TRACE_OUT((TEXT("DeleteDeletedObjectTwins(): Implicitly removed twin family for %s since all members are last known non-existent."),
  954.                     pcri->pcszName));
  955.       }
  956.    }
  957.    return(bAnyDeleted);
  958. }
  959. #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */
  960. /*
  961. ** FindAGeneratedObjectTwinProc()
  962. **
  963. **
  964. **
  965. ** Arguments:
  966. **
  967. ** Returns:
  968. **
  969. ** Side Effects:  none
  970. **
  971. ** An obsolete generated object twin is an object twin that is last known
  972. ** non-existent, and whose twin family is not in the deletion pending state.
  973. */
  974. PRIVATE_CODE BOOL FindAGeneratedObjectTwinProc(POBJECTTWIN pot, PVOID pvUnused)
  975. {
  976.    ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  977.    ASSERT(! pvUnused);
  978.    return(pot->fsLastRec.fscond == FS_COND_DOES_NOT_EXIST &&
  979.           ! IsTwinFamilyDeletionPending(pot->ptfParent));
  980. }
  981. #pragma warning(default:4100) /* "unreferenced formal parameter" warning */
  982. /*
  983. ** FolderTwinShouldBeImplicitlyDeleted()
  984. **
  985. **
  986. **
  987. ** Arguments:
  988. **
  989. ** Returns:
  990. **
  991. ** Side Effects:  none
  992. **
  993. ** A folder twin should be implicitly deleted when it meets all the following
  994. ** conditions:
  995. **    1) The folder root is last known non-existent.
  996. **    2) One or more of its generated object twins has just been implicitly
  997. **       deleted.
  998. **    3) It no longer generates any non-obsolete object twins.
  999. */
  1000. PRIVATE_CODE BOOL FolderTwinShouldBeImplicitlyDeleted(PFOLDERPAIR pfp)
  1001. {
  1002.    BOOL bDelete;
  1003.    ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  1004.    if (IsStubFlagSet(&(pfp->stub), STUB_FL_DELETION_PENDING) &&
  1005.        IsStubFlagSet(&(pfp->stub), STUB_FL_USED) &&
  1006.        EnumGeneratedObjectTwins(pfp, &FindAGeneratedObjectTwinProc, NULL))
  1007.    {
  1008.       TRACE_OUT((TEXT("FolderTwinShouldBeImplicitlyDeleted(): Folder twin %s should be implicitly deleted."),
  1009.                  DebugGetPathString(pfp->hpath)));
  1010.       bDelete = TRUE;
  1011.    }
  1012.    else
  1013.       bDelete = FALSE;
  1014.    return(bDelete);
  1015. }
  1016. /*
  1017. ** DeleteDeletedFolderTwins()
  1018. **
  1019. ** Performs garbage collection of obsolete folder twins.
  1020. **
  1021. ** Arguments:
  1022. **
  1023. ** Returns:
  1024. **
  1025. ** Side Effects:  none
  1026. */
  1027. PRIVATE_CODE BOOL DeleteDeletedFolderTwins(HPTRARRAY hpaFolderPairs)
  1028. {
  1029.    BOOL bAnyDeleted = FALSE;
  1030.    ARRAYINDEX aicPtrs;
  1031.    ARRAYINDEX ai;
  1032.    ASSERT(IS_VALID_HANDLE(hpaFolderPairs, PTRARRAY));
  1033.    aicPtrs = GetPtrCount(hpaFolderPairs);
  1034.    ASSERT(! (aicPtrs % 2));
  1035.    ai = 0;
  1036.    while (ai < aicPtrs)
  1037.    {
  1038.       PFOLDERPAIR pfp;
  1039.       pfp = GetPtr(hpaFolderPairs, ai);
  1040.       if (FolderTwinShouldBeImplicitlyDeleted(pfp) ||
  1041.           FolderTwinShouldBeImplicitlyDeleted(pfp->pfpOther))
  1042.       {
  1043.          TRACE_OUT((TEXT("DeleteDeletedFolderTwins(): Implicitly deleting %s twin pair %s and %s, files %s."),
  1044.                     IsStubFlagSet(&(pfp->stub), STUB_FL_SUBTREE) ? TEXT("subtree") : TEXT("folder"),
  1045.                     DebugGetPathString(pfp->hpath),
  1046.                     DebugGetPathString(pfp->pfpOther->hpath),
  1047.                     GetString(pfp->pfpd->hsName)));
  1048.          DestroyStub(&(pfp->stub));
  1049.          aicPtrs -= 2;
  1050.          ASSERT(! (aicPtrs % 2));
  1051.          ASSERT(aicPtrs == GetPtrCount(hpaFolderPairs));
  1052.          bAnyDeleted = TRUE;
  1053.       }
  1054.       else
  1055.       {
  1056.          /* Don't check this pair of folder twins again. */
  1057.          ClearStubFlag(&(pfp->stub), STUB_FL_USED);
  1058.          ClearStubFlag(&(pfp->pfpOther->stub), STUB_FL_USED);
  1059.          ai++;
  1060.       }
  1061.    }
  1062.    return(bAnyDeleted);
  1063. }
  1064. /*
  1065. ** CreateRecItem()
  1066. **
  1067. **
  1068. **
  1069. ** Arguments:
  1070. **
  1071. ** Returns:
  1072. **
  1073. ** Side Effects:  none
  1074. */
  1075. PRIVATE_CODE TWINRESULT CreateRecItem(PTWINFAMILY ptf, PRECITEM *ppri)
  1076. {
  1077.    TWINRESULT tr;
  1078.    ASSERT(IS_VALID_STRUCT_PTR(ptf, CTWINFAMILY));
  1079.    ASSERT(IS_VALID_WRITE_PTR(ppri, PRECITEM));
  1080.    /* Create a new RECITEM for the twin family. */
  1081.    if (AllocateMemory(sizeof(**ppri), ppri))
  1082.    {
  1083.       LPCTSTR pcszName;
  1084.       BOOL bContinue;
  1085.       HNODE hnode;
  1086.       /* Get twin family's object name. */
  1087.       tr = TR_SUCCESS;
  1088.       pcszName = GetString(ptf->hsName);
  1089.       /* Fill in the fields required for adding new RECNODEs. */
  1090.       /* N.b., SYNCUI depends on dwUser to be initialized to 0. */
  1091.       (*ppri)->pcszName = pcszName;
  1092.       (*ppri)->ulcNodes = 0;
  1093.       (*ppri)->prnFirst = NULL;
  1094.       (*ppri)->hTwinFamily = (HTWINFAMILY)ptf;
  1095.       (*ppri)->dwUser = 0;
  1096.       TRACE_OUT((TEXT("CreateRecItem(): Creating a RECITEM for %s."),
  1097.                  pcszName));
  1098.       /* Add object twins to the RECITEM one at a time as RECNODEs. */
  1099.       for (bContinue = GetFirstNode(ptf->hlistObjectTwins, &hnode);
  1100.            bContinue;
  1101.            bContinue = GetNextNode(hnode, &hnode))
  1102.       {
  1103.          POBJECTTWIN pot;
  1104.          pot = (POBJECTTWIN)GetNodeData(hnode);
  1105.          tr = AddObjectTwinRecNode(*ppri, pot);
  1106.          if (tr != TR_SUCCESS)
  1107.          {
  1108.             DestroyRecItem(*ppri);
  1109.             break;
  1110.          }
  1111.       }
  1112.       if (tr == TR_SUCCESS)
  1113.       {
  1114.          DetermineDeletionPendingState(*ppri);
  1115.          DetermineRecActions(*ppri);
  1116.          LockStub(&(ptf->stub));
  1117. #ifdef DEBUG
  1118.          {
  1119.             LPCTSTR pcszAction;
  1120.             switch ((*ppri)->riaction)
  1121.             {
  1122.                case RIA_COPY:
  1123.                   pcszAction = TEXT("Copy");
  1124.                   break;
  1125.                case RIA_MERGE:
  1126.                   pcszAction = TEXT("Merge");
  1127.                   break;
  1128.                case RIA_BROKEN_MERGE:
  1129.                   pcszAction = TEXT("Broken merge for");
  1130.                   break;
  1131.                case RIA_DELETE:
  1132.                   pcszAction = TEXT("Delete");
  1133.                   break;
  1134.                default:
  1135.                   ASSERT((*ppri)->riaction == RIA_NOTHING);
  1136.                   pcszAction = TEXT("Do nothing to");
  1137.                   break;
  1138.             }
  1139.             TRACE_OUT((TEXT("CreateRecItem(): %s %s."),
  1140.                        pcszAction,
  1141.                        (*ppri)->pcszName));
  1142.          }
  1143. #endif
  1144.       }
  1145.    }
  1146.    else
  1147.       tr = TR_OUT_OF_MEMORY;
  1148.    ASSERT(tr != TR_SUCCESS ||
  1149.           IS_VALID_READ_PTR(*ppri, CRECITEM));
  1150.    return(tr);
  1151. }
  1152. /*
  1153. ** AddObjectTwinRecNode()
  1154. **
  1155. **
  1156. **
  1157. ** Arguments:
  1158. **
  1159. ** Returns:       TWINRESULT
  1160. **
  1161. ** Side Effects:  none
  1162. */
  1163. PRIVATE_CODE TWINRESULT AddObjectTwinRecNode(PRECITEM pri, POBJECTTWIN pot)
  1164. {
  1165.    TWINRESULT tr = TR_OUT_OF_MEMORY;
  1166.    PRECNODE prnNew;
  1167.    ASSERT(IS_VALID_READ_PTR(pri, CRECITEM));
  1168.    ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  1169.    ASSERT(pri->ulcNodes < ULONG_MAX);
  1170.    /* Try to allocate a new reconciliation node. */
  1171.    if (AllocateMemory(sizeof(*prnNew), &prnNew))
  1172.    {
  1173.       LPTSTR pszFolder;
  1174.       if (AllocatePathString(pot->hpath, &pszFolder))
  1175.       {
  1176.          /* Fill in RECNODE fields. */
  1177.          /* N.b., we don't touch the dwUser field. */
  1178.          /*
  1179.           * The rnaction field may be changed later during the call to
  1180.           * DetermineRecActions().
  1181.           */
  1182.          prnNew->hvid = (HVOLUMEID)(pot->hpath);
  1183.          prnNew->pcszFolder = pszFolder;
  1184.          prnNew->hObjectTwin = (HOBJECTTWIN)pot;
  1185.          prnNew->priParent = pri;
  1186.          prnNew->fsLast = pot->fsLastRec;
  1187.          prnNew->rnaction = RNA_NOTHING;
  1188.          prnNew->dwFlags = 0;
  1189.          /* Set flags. */
  1190.          if (IsStubFlagSet(&(pot->stub), STUB_FL_FROM_OBJECT_TWIN))
  1191.             SET_FLAG(prnNew->dwFlags, RN_FL_FROM_OBJECT_TWIN);
  1192.          if (pot->ulcSrcFolderTwins > 0)
  1193.             SET_FLAG(prnNew->dwFlags, RN_FL_FROM_FOLDER_TWIN);
  1194.          /* Determine RECNODE file stamp. */
  1195.          if (IsStubFlagSet(&(pot->stub), STUB_FL_FILE_STAMP_VALID))
  1196.          {
  1197.             prnNew->fsCurrent = pot->fsCurrent;
  1198.             TRACE_OUT((TEXT("AddObjectTwinRecNode(): Used cached file stamp for object twin %s\%s."),
  1199.                        prnNew->pcszFolder,
  1200.                        prnNew->priParent->pcszName));
  1201.          }
  1202.          else
  1203.          {
  1204.             MyGetFileStampByHPATH(pot->hpath, prnNew->priParent->pcszName,
  1205.                                   &(prnNew->fsCurrent));
  1206.             TRACE_OUT((TEXT("AddObjectTwinRecNode(): Determined uncached file stamp for object twin %s\%s."),
  1207.                        prnNew->pcszFolder,
  1208.                        prnNew->priParent->pcszName));
  1209.          }
  1210.          prnNew->rnstate = DetermineRecNodeState(prnNew);
  1211.          /* Tie the new RECNODE in to the parent RECITEM's list of RECNODEs. */
  1212.          prnNew->prnNext = pri->prnFirst;
  1213.          pri->prnFirst = prnNew;
  1214.          ASSERT(pri->ulcNodes < ULONG_MAX);
  1215.          pri->ulcNodes++;
  1216.          LockStub(&(pot->stub));
  1217.          tr = TR_SUCCESS;
  1218.          ASSERT(IS_VALID_STRUCT_PTR(prnNew, CRECNODE));
  1219.          TRACE_OUT((TEXT("AddObjectTwinRecNode(): Adding a RECNODE for object %s\%s.  RECNODE state is %s."),
  1220.                     pszFolder,
  1221.                     pri->pcszName,
  1222.                     GetRECNODESTATEString(prnNew->rnstate)));
  1223.       }
  1224.       else
  1225.          FreeMemory(prnNew);
  1226.    }
  1227.    return(tr);
  1228. }
  1229. /*
  1230. ** DoesTwinFamilyNeedRec()
  1231. **
  1232. **
  1233. **
  1234. ** Arguments:
  1235. **
  1236. ** Returns:       TWINRESULT
  1237. **
  1238. ** Side Effects:  none
  1239. */
  1240. PRIVATE_CODE BOOL DoesTwinFamilyNeedRec(POBJECTTWIN pot, PVOID ptfri)
  1241. {
  1242.    BOOL bContinue = FALSE;
  1243.    TWINRESULT tr;
  1244.    PRECITEM priTemp;
  1245.    ASSERT(IS_VALID_STRUCT_PTR(pot, COBJECTTWIN));
  1246.    ASSERT(IS_VALID_WRITE_PTR((PTWINFAMILYRECINFO)ptfri, TWINFAMILYRECINFO));
  1247.    /*
  1248.     * Create a temporary RECITEM for this object twin's twin family to
  1249.     * determine whether or not the twin family requires reconciliation.
  1250.     */
  1251.    tr = CreateRecItem(pot->ptfParent, &priTemp);
  1252.    if (tr == TR_SUCCESS)
  1253.    {
  1254.       if (priTemp->riaction == RIA_NOTHING)
  1255.       {
  1256.          ((PTWINFAMILYRECINFO)ptfri)->bNeedsRec = FALSE;
  1257.          bContinue = TRUE;
  1258.          TRACE_OUT((TEXT("DoesTwinFamilyNeedRec(): Twin family for object %s is up-to-date."),
  1259.                     priTemp->pcszName));
  1260.       }
  1261.       else
  1262.       {
  1263.          ((PTWINFAMILYRECINFO)ptfri)->bNeedsRec = TRUE;
  1264.          TRACE_OUT((TEXT("DoesTwinFamilyNeedRec(): Twin family for object %s needs to be reconciled."),
  1265.                     priTemp->pcszName));
  1266.       }
  1267.       DestroyRecItem(priTemp);
  1268.    }
  1269.    ((PTWINFAMILYRECINFO)ptfri)->tr = tr;
  1270.    return(bContinue);
  1271. }
  1272. /*
  1273. ** GetFolderPairStatus()
  1274. **
  1275. ** Determines the status of a folder pair.
  1276. **
  1277. ** Arguments:     pfp - pointer to folder pair whose status is to be
  1278. **                      determined
  1279. **                pfts - pointer to FOLDERTWINSTATUS to be filled in with
  1280. **                       reconciliation action that should be taken on the
  1281. **                       folder pair, *pfts is filled in with one of the
  1282. **                       following values:
  1283. **
  1284. **                         FTS_DO_NOTHING - no reconciliation required
  1285. **                         FTS_DO_SOMETHING - reconciliation required
  1286. **                         FTS_UNAVAILABLE - one or both of the folders is
  1287. **                                           unavailable
  1288. **
  1289. ** Returns:       TWINRESULT
  1290. **
  1291. ** Side Effects:  Expands intersecting folder twins to object twins.  This may
  1292. **                be S-L-O-W.
  1293. */
  1294. PRIVATE_CODE TWINRESULT GetFolderPairStatus(PFOLDERPAIR pfp,
  1295.                                             CREATERECLISTPROC crlp,
  1296.                                             LPARAM lpCallbackData,
  1297.                                             PFOLDERTWINSTATUS pfts)
  1298. {
  1299.    TWINRESULT tr;
  1300.    /* lpCallbackData may be any value. */
  1301.    ASSERT(IS_VALID_STRUCT_PTR(pfp, CFOLDERPAIR));
  1302.    ASSERT(! crlp ||
  1303.           IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
  1304.    ASSERT(IS_VALID_WRITE_PTR(pfts, UINT));
  1305.    if (IsPathVolumeAvailable(pfp->hpath))
  1306.    {
  1307.       tr = ExpandIntersectingFolderTwins(pfp, crlp, lpCallbackData);
  1308.       if (tr == TR_SUCCESS)
  1309.       {
  1310.          TWINFAMILYRECINFO tfri;
  1311.          /*
  1312.           * Walk the list of generated object twins for one half of this folder
  1313.           * pair.  Prepare a RECITEM for each object twin's twin family.
  1314.           * Continue until one of the RECITEMs requires reconciliation, or we
  1315.           * run out of object twins.
  1316.           *
  1317.           * Both of the lists of object twins in this folder pair should hit
  1318.           * exactly the same twin families.
  1319.           */
  1320.          /* Set defaults in case there are no generated object twins. */
  1321.          tfri.tr = TR_SUCCESS;
  1322.          tfri.bNeedsRec = FALSE;
  1323.          /*
  1324.           * EnumGeneratedObjectTwins() returns TRUE if enumeration finished
  1325.           * without being stopped by the callback function.
  1326.           */
  1327.          if (EnumGeneratedObjectTwins(pfp, &DoesTwinFamilyNeedRec, &tfri))
  1328.             ASSERT(tfri.tr == TR_SUCCESS && ! tfri.bNeedsRec);
  1329.          else
  1330.             ASSERT((tfri.tr != TR_SUCCESS) ||
  1331.                    (tfri.tr == TR_SUCCESS && tfri.bNeedsRec));
  1332.          tr = tfri.tr;
  1333.          if (tr == TR_SUCCESS)
  1334.          {
  1335.             if (tfri.bNeedsRec)
  1336.                *pfts = FTS_DO_SOMETHING;
  1337.             else
  1338.                *pfts = FTS_DO_NOTHING;
  1339.          }
  1340.       }
  1341.    }
  1342.    else
  1343.    {
  1344.       *pfts = FTS_UNAVAILABLE;
  1345.       tr = TR_SUCCESS;
  1346.    }
  1347.    ASSERT(tr != TR_SUCCESS ||
  1348.           IsValidFOLDERTWINSTATUS(*pfts));
  1349.    return(tr);
  1350. }
  1351. #ifdef DEBUG
  1352. /*
  1353. ** IsValidCreateRecListProcMsg()
  1354. **
  1355. **
  1356. **
  1357. ** Arguments:
  1358. **
  1359. ** Returns:
  1360. **
  1361. ** Side Effects:  none
  1362. */
  1363. PRIVATE_CODE BOOL IsValidCreateRecListProcMsg(UINT uMsg)
  1364. {
  1365.    BOOL bResult;
  1366.    switch (uMsg)
  1367.    {
  1368.       case CRLS_BEGIN_CREATE_REC_LIST:
  1369.       case CRLS_DELTA_CREATE_REC_LIST:
  1370.       case CRLS_END_CREATE_REC_LIST:
  1371.          bResult = TRUE;
  1372.          break;
  1373.       default:
  1374.          bResult = FALSE;
  1375.          ERROR_OUT((TEXT("IsValidCreateRecListProcMsg(): Invalid CreateRecListProc() message %u."),
  1376.                     uMsg));
  1377.          break;
  1378.    }
  1379.    return(bResult);
  1380. }
  1381. /*
  1382. ** IsValidFOLDERTWINSTATUS()
  1383. **
  1384. **
  1385. **
  1386. ** Arguments:
  1387. **
  1388. ** Returns:
  1389. **
  1390. ** Side Effects:  none
  1391. */
  1392. PRIVATE_CODE BOOL IsValidFOLDERTWINSTATUS(FOLDERTWINSTATUS fts)
  1393. {
  1394.    BOOL bResult;
  1395.    switch (fts)
  1396.    {
  1397.       case FTS_DO_NOTHING:
  1398.       case FTS_DO_SOMETHING:
  1399.       case FTS_UNAVAILABLE:
  1400.          bResult = TRUE;
  1401.          break;
  1402.       default:
  1403.          bResult = FALSE;
  1404.          ERROR_OUT((TEXT("IsValidFOLDERTWINSTATUS(): Invalid FOLDERTWINSTATUS %d."),
  1405.                     fts));
  1406.          break;
  1407.    }
  1408.    return(bResult);
  1409. }
  1410. /*
  1411. ** IsValidPCRECNODESTATECOUNTER()
  1412. **
  1413. **
  1414. **
  1415. ** Arguments:
  1416. **
  1417. ** Returns:
  1418. **
  1419. ** Side Effects:  none
  1420. */
  1421. PRIVATE_CODE BOOL IsValidPCRECNODESTATECOUNTER(PCRECNODESTATECOUNTER pcrnscntr)
  1422. {
  1423.    /* The fields may be any values. */
  1424.    return(IS_VALID_READ_PTR(pcrnscntr, CRECNODESTATECOUNTER));
  1425. }
  1426. #endif
  1427. #if defined(DEBUG) || defined(VSTF)
  1428. /*
  1429. ** IsValidRECNODESTATE()
  1430. **
  1431. **
  1432. **
  1433. ** Arguments:
  1434. **
  1435. ** Returns:
  1436. **
  1437. ** Side Effects:  none
  1438. */
  1439. PRIVATE_CODE BOOL IsValidRECNODESTATE(RECNODESTATE rnstate)
  1440. {
  1441.    BOOL bResult;
  1442.    switch (rnstate)
  1443.    {
  1444.       case RNS_NEVER_RECONCILED:
  1445.       case RNS_UNAVAILABLE:
  1446.       case RNS_DOES_NOT_EXIST:
  1447.       case RNS_DELETED:
  1448.       case RNS_NOT_RECONCILED:
  1449.       case RNS_UP_TO_DATE:
  1450.       case RNS_CHANGED:
  1451.          bResult = TRUE;
  1452.          break;
  1453.       default:
  1454.          bResult = FALSE;
  1455.          ERROR_OUT((TEXT("IsValidRECNODESTATE(): Invalid RECNODESTATE %d."),
  1456.                     rnstate));
  1457.          break;
  1458.    }
  1459.    return(bResult);
  1460. }
  1461. /*
  1462. ** IsValidRECNODEACTION()
  1463. **
  1464. **
  1465. **
  1466. ** Arguments:
  1467. **
  1468. ** Returns:
  1469. **
  1470. ** Side Effects:  none
  1471. */
  1472. PRIVATE_CODE BOOL IsValidRECNODEACTION(RECNODEACTION rnaction)
  1473. {
  1474.    BOOL bResult;
  1475.    switch (rnaction)
  1476.    {
  1477.       case RNA_NOTHING:
  1478.       case RNA_COPY_FROM_ME:
  1479.       case RNA_COPY_TO_ME:
  1480.       case RNA_MERGE_ME:
  1481.       case RNA_DELETE_ME:
  1482.          bResult = TRUE;
  1483.          break;
  1484.       default:
  1485.          bResult = FALSE;
  1486.          ERROR_OUT((TEXT("IsValidRECNODEACTION(): Invalid RECNODEACTION %d."),
  1487.                     rnaction));
  1488.          break;
  1489.    }
  1490.    return(bResult);
  1491. }
  1492. /*
  1493. ** IsValidRECITEMACTION()
  1494. **
  1495. **
  1496. **
  1497. ** Arguments:
  1498. **
  1499. ** Returns:
  1500. **
  1501. ** Side Effects:  none
  1502. */
  1503. PRIVATE_CODE BOOL IsValidRECITEMACTION(RECITEMACTION riaction)
  1504. {
  1505.    BOOL bResult;
  1506.    switch (riaction)
  1507.    {
  1508.       case RIA_NOTHING:
  1509.       case RIA_DELETE:
  1510.       case RIA_COPY:
  1511.       case RIA_MERGE:
  1512.       case RIA_BROKEN_MERGE:
  1513.          bResult = TRUE;
  1514.          break;
  1515.       default:
  1516.          bResult = FALSE;
  1517.          ERROR_OUT((TEXT("IsValidRECITEMACTION(): Invalid RECITEMACTION %d."),
  1518.                     riaction));
  1519.          break;
  1520.    }
  1521.    return(bResult);
  1522. }
  1523. /*
  1524. ** IsValidPCRECLIST()
  1525. **
  1526. **
  1527. **
  1528. ** Arguments:
  1529. **
  1530. ** Returns:
  1531. **
  1532. ** Side Effects:  none
  1533. */
  1534. PRIVATE_CODE BOOL IsValidPCRECLIST(PCRECLIST pcrl)
  1535. {
  1536.    BOOL bResult = FALSE;
  1537.    if (IS_VALID_READ_PTR(pcrl, CRECLIST) &&
  1538.        IS_VALID_HANDLE(pcrl->hbr, BRFCASE))
  1539.    {
  1540.       PRECITEM pri;
  1541.       ULONG ulcRecItems = 0;
  1542.       for (pri = pcrl->priFirst;
  1543.            pri && IS_VALID_STRUCT_PTR(pri, CRECITEM);
  1544.            pri = pri->priNext)
  1545.       {
  1546.          ASSERT(ulcRecItems < ULONG_MAX);
  1547.          ulcRecItems++;
  1548.       }
  1549.       if (! pri && EVAL(ulcRecItems == pcrl->ulcItems))
  1550.          bResult = TRUE;
  1551.    }
  1552.    return(bResult);
  1553. }
  1554. #endif
  1555. /****************************** Public Functions *****************************/
  1556. /*
  1557. ** IsReconciledFileStamp()
  1558. **
  1559. **
  1560. **
  1561. ** Arguments:
  1562. **
  1563. ** Returns:
  1564. **
  1565. ** Side Effects:  none
  1566. */
  1567. PUBLIC_CODE BOOL IsReconciledFileStamp(PCFILESTAMP pcfs)
  1568. {
  1569.    ASSERT(IS_VALID_STRUCT_PTR(pcfs, CFILESTAMP));
  1570.    return(pcfs->fscond != FS_COND_UNAVAILABLE);
  1571. }
  1572. /*
  1573. ** LastKnownNonExistent()
  1574. **
  1575. ** 
  1576. **
  1577. ** Arguments:
  1578. **
  1579. ** Returns:
  1580. **
  1581. ** Side Effects:  none
  1582. */
  1583. PUBLIC_CODE BOOL LastKnownNonExistent(PCFILESTAMP pcfsLast,
  1584.                                       PCFILESTAMP pcfsCurrent)
  1585. {
  1586.    ASSERT(IS_VALID_STRUCT_PTR(pcfsLast, CFILESTAMP));
  1587.    ASSERT(IS_VALID_STRUCT_PTR(pcfsCurrent, CFILESTAMP));
  1588.    return(pcfsCurrent->fscond == FS_COND_DOES_NOT_EXIST ||
  1589.           (pcfsCurrent->fscond == FS_COND_UNAVAILABLE &&
  1590.            pcfsLast->fscond == FS_COND_DOES_NOT_EXIST));
  1591. }
  1592. /*
  1593. ** DetermineDeletionPendingState()
  1594. **
  1595. **
  1596. **
  1597. ** Arguments:
  1598. **
  1599. ** Returns:       void
  1600. **
  1601. ** Side Effects:  none
  1602. */
  1603. PUBLIC_CODE void DetermineDeletionPendingState(PCRECITEM pcri)
  1604. {
  1605.    PCRECNODE pcrn;
  1606.    ULONG ulcDeleted = 0;
  1607.    ULONG ulcToDelete = 0;
  1608.    ULONG ulcChanged = 0;
  1609.    ULONG ulcJustDeleted = 0;
  1610.    ULONG ulcNeverReconciledTotal = 0;
  1611.    /*
  1612.     * Don't fully validate *pcri here since we may be called from
  1613.     * CreateRecItem() with an incomplete RECITEM.
  1614.     */
  1615.    ASSERT(IS_VALID_READ_PTR(pcri, CRECITEM));
  1616.    /* Count RECNODE states. */
  1617.    for (pcrn = pcri->prnFirst; pcrn; pcrn= pcrn->prnNext)
  1618.    {
  1619.       if (LastKnownNonExistent(&(pcrn->fsLast), &(pcrn->fsCurrent)))
  1620.       {
  1621.          ASSERT(ulcDeleted < ULONG_MAX);
  1622.          ulcDeleted++;
  1623.          TRACE_OUT((TEXT("DetermineDeletionPendingState(): %s\%s last known non-existent."),
  1624.                     pcrn->pcszFolder,
  1625.                     pcri->pcszName));
  1626.       }
  1627.       else if (IsStubFlagClear(&(((PCOBJECTTWIN)(pcrn->hObjectTwin))->stub),
  1628.                                STUB_FL_KEEP))
  1629.       {
  1630.          ASSERT(ulcToDelete < ULONG_MAX);
  1631.          ulcToDelete++;
  1632.          TRACE_OUT((TEXT("DetermineDeletionPendingState(): %s\%s not explicitly kept."),
  1633.                     pcrn->pcszFolder,
  1634.                     pcri->pcszName));
  1635.       }
  1636.       if (IsReconciledFileStamp(&(pcrn->fsLast)))
  1637.       {
  1638.          if (pcrn->fsCurrent.fscond == FS_COND_EXISTS &&
  1639.              MyCompareFileStamps(&(pcrn->fsLast), &(pcrn->fsCurrent)) != CR_EQUAL)
  1640.          {
  1641.             ASSERT(ulcChanged < ULONG_MAX);
  1642.             ulcChanged++;
  1643.             TRACE_OUT((TEXT("DetermineDeletionPendingState(): %s\%s changed."),
  1644.                        pcrn->pcszFolder,
  1645.                        pcri->pcszName));
  1646.          }
  1647.          if (pcrn->fsLast.fscond == FS_COND_EXISTS &&
  1648.              pcrn->fsCurrent.fscond == FS_COND_DOES_NOT_EXIST)
  1649.          {
  1650.             ASSERT(ulcJustDeleted < ULONG_MAX);
  1651.             ulcJustDeleted++;
  1652.             TRACE_OUT((TEXT("DetermineDeletionPendingState(): %s\%s just deleted."),
  1653.                        pcrn->pcszFolder,
  1654.                        pcri->pcszName));
  1655.          }
  1656.       }
  1657.       else
  1658.       {
  1659.          ASSERT(ulcNeverReconciledTotal < ULONG_MAX);
  1660.          ulcNeverReconciledTotal++;
  1661.          TRACE_OUT((TEXT("DetermineDeletionPendingState(): %s\%s never reconciled."),
  1662.                     pcrn->pcszFolder,
  1663.                     pcri->pcszName));
  1664.       }
  1665.    }
  1666.    /*
  1667.     * Take twin family out of the deletion pending state if any object twin
  1668.     * has changed, or if no object twins are awaiting deletion (i.e., all
  1669.     * object twins are deleted or kept).
  1670.     *
  1671.     * Take twin family in to the deletion pending state if any object twin has
  1672.     * just been deleted.
  1673.     */
  1674.    if (ulcNeverReconciledTotal > 0 ||
  1675.        ulcChanged > 0 ||
  1676.        ! ulcDeleted ||
  1677.        ! ulcToDelete)
  1678.    {
  1679.       UnmarkTwinFamilyDeletionPending((PTWINFAMILY)(pcri->hTwinFamily));
  1680.       if (ulcJustDeleted > 0)
  1681.          TRACE_OUT((TEXT("DetermineDeletionPendingState(): One or more object twins of %s deleted, but deletion not pending (%lu never reconciled, %lu changed, %lu deleted, %lu to delete, %lu just deleted)."),
  1682.                     pcri->pcszName,
  1683.                     ulcNeverReconciledTotal,
  1684.                     ulcChanged,
  1685.                     ulcDeleted,
  1686.                     ulcToDelete,
  1687.                     ulcJustDeleted));
  1688.    }
  1689.    else if (ulcJustDeleted > 0)
  1690.       MarkTwinFamilyDeletionPending((PTWINFAMILY)(pcri->hTwinFamily));
  1691.    return;
  1692. }
  1693. /*
  1694. ** DeleteTwinsFromRecItem()
  1695. **
  1696. **
  1697. **
  1698. ** Arguments:
  1699. **
  1700. ** Returns:
  1701. **
  1702. ** Side Effects:  May implicitly delete object twins, twin families, and pairs
  1703. **                of folder twins.
  1704. */
  1705. PUBLIC_CODE BOOL DeleteTwinsFromRecItem(PCRECITEM pcri)
  1706. {
  1707.    BOOL bObjectTwinsDeleted;
  1708.    BOOL bCheckFolderTwins;
  1709.    BOOL bFolderTwinsDeleted;
  1710.    HPTRARRAY hpaFolderPairs;
  1711.    ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
  1712.    /*
  1713.     * DetermineDeletionPendingState() has already been performed by
  1714.     * MyReconcileItem() for the twin family of this RECITEM.
  1715.     */
  1716.    hpaFolderPairs = GetBriefcaseFolderPairPtrArray(((PCTWINFAMILY)(pcri->hTwinFamily))->hbr);
  1717.    ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
  1718.    bObjectTwinsDeleted = DeleteDeletedObjectTwins(pcri, &bCheckFolderTwins);
  1719.    if (bObjectTwinsDeleted)
  1720.       TRACE_OUT((TEXT("DeleteTwinsFromRecItem(): One or more object twins implicitly deleted from twin family for %s."),
  1721.                  pcri->pcszName));
  1722.    if (bCheckFolderTwins)
  1723.    {
  1724.       TRACE_OUT((TEXT("DeleteTwinsFromRecItem(): Checking for folder twins to implicitly delete.")));
  1725.       bFolderTwinsDeleted = DeleteDeletedFolderTwins(hpaFolderPairs);
  1726.       if (bFolderTwinsDeleted)
  1727.          TRACE_OUT((TEXT("DeleteTwinsFromRecItem(): One or more pairs of folder twins implicitly deleted.")));
  1728.    }
  1729.    else
  1730.       bFolderTwinsDeleted = FALSE;
  1731.    return(bObjectTwinsDeleted || bFolderTwinsDeleted);
  1732. }
  1733. /*
  1734. ** DeleteTwinsFromRecList()
  1735. **
  1736. **
  1737. **
  1738. ** Arguments:
  1739. **
  1740. ** Returns:
  1741. **
  1742. ** Side Effects:  May implicitly delete object twins, twin families, and pairs
  1743. **                of folder twins.
  1744. */
  1745. PUBLIC_CODE BOOL DeleteTwinsFromRecList(PCRECLIST pcrl)
  1746. {
  1747.    PCRECITEM pcri;
  1748.    BOOL bObjectTwinsDeleted = FALSE;
  1749.    BOOL bCheckFolderTwins = FALSE;
  1750.    BOOL bFolderTwinsDeleted;
  1751.    HPTRARRAY hpaFolderPairs;
  1752.    ASSERT(IS_VALID_STRUCT_PTR(pcrl, CRECLIST));
  1753.    /*
  1754.     * DetermineDeletionPendingState() has already been performed by
  1755.     * CreateRecItem() for the twin family of each RECITEM in the RECLIST.
  1756.     */
  1757.    hpaFolderPairs = GetBriefcaseFolderPairPtrArray(pcrl->hbr);
  1758.    ClearFlagInArrayOfStubs(hpaFolderPairs, STUB_FL_USED);
  1759.    for (pcri = pcrl->priFirst; pcri; pcri = pcri->priNext)
  1760.    {
  1761.       BOOL bLocalCheckFolderTwins;
  1762.       if (DeleteDeletedObjectTwins(pcri, &bLocalCheckFolderTwins))
  1763.       {
  1764.          TRACE_OUT((TEXT("DeleteTwinsFromRecList(): One or more object twins implicitly deleted from twin family for %s."),
  1765.                     pcri->pcszName));
  1766.          bObjectTwinsDeleted = TRUE;
  1767.       }
  1768.       if (bLocalCheckFolderTwins)
  1769.          bCheckFolderTwins = TRUE;
  1770.    }
  1771.    if (bCheckFolderTwins)
  1772.    {
  1773.       TRACE_OUT((TEXT("DeleteTwinsFromRecList(): Checking for folder twins to implicitly delete.")));
  1774.       bFolderTwinsDeleted = DeleteDeletedFolderTwins(hpaFolderPairs);
  1775.       if (bFolderTwinsDeleted)
  1776.          TRACE_OUT((TEXT("DeleteTwinsFromRecItem(): One or more pairs of folder twins implicitly deleted.")));
  1777.    }
  1778.    else
  1779.       bFolderTwinsDeleted = FALSE;
  1780.    return(bObjectTwinsDeleted || bFolderTwinsDeleted);
  1781. }
  1782. /*
  1783. ** FindCopySource()
  1784. **
  1785. **
  1786. **
  1787. ** Arguments:
  1788. **
  1789. ** Returns:
  1790. **
  1791. ** Side Effects:  none
  1792. */
  1793. PUBLIC_CODE TWINRESULT FindCopySource(PCRECITEM pcri, PRECNODE *pprnCopySrc)
  1794. {
  1795.    TWINRESULT tr = TR_INVALID_PARAMETER;
  1796.    PRECNODE prn;
  1797.    ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
  1798.    ASSERT(IS_VALID_WRITE_PTR(pprnCopySrc, PRECNODE));
  1799.    ASSERT(pcri->riaction == RIA_COPY);
  1800.    for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  1801.    {
  1802.       if (prn->rnaction == RNA_COPY_FROM_ME)
  1803.       {
  1804.          *pprnCopySrc = prn;
  1805.          tr = TR_SUCCESS;
  1806.          break;
  1807.       }
  1808.    }
  1809.    ASSERT(tr != TR_SUCCESS ||
  1810.           (*pprnCopySrc)->rnaction == RNA_COPY_FROM_ME);
  1811.    return(tr);
  1812. }
  1813. /*
  1814. ** ChooseMergeDestination()
  1815. **
  1816. **
  1817. **
  1818. ** Arguments:
  1819. **
  1820. ** Returns:
  1821. **
  1822. ** Side Effects:  none
  1823. */
  1824. PUBLIC_CODE void ChooseMergeDestination(PCRECITEM pcri,
  1825.                                         PRECNODE *pprnMergeDest)
  1826. {
  1827.    PRECNODE prn;
  1828.    ASSERT(IS_VALID_STRUCT_PTR(pcri, CRECITEM));
  1829.    ASSERT(IS_VALID_WRITE_PTR(pprnMergeDest, PRECNODE));
  1830.    ASSERT(pcri->riaction == RIA_MERGE);
  1831.    for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  1832.    {
  1833.       if (prn->rnaction == RNA_MERGE_ME)
  1834.       {
  1835.          *pprnMergeDest = prn;
  1836.          break;
  1837.       }
  1838.    }
  1839.    ASSERT(IS_VALID_STRUCT_PTR(*pprnMergeDest, CRECNODE));
  1840.    ASSERT((*pprnMergeDest)->rnaction == RNA_MERGE_ME);
  1841.    return;
  1842. }
  1843. /*
  1844. ** ClearFlagInArrayOfStubs()
  1845. **
  1846. ** Clears flags in all the stubs pointed to by an array of pointers to stubs.
  1847. **
  1848. ** Arguments:     hpa - handle to array of pointers to stubs
  1849. **                dwClearFlags - flags to be cleared
  1850. **
  1851. ** Returns:       void
  1852. **
  1853. ** Side Effects:  none
  1854. */
  1855. PUBLIC_CODE void ClearFlagInArrayOfStubs(HPTRARRAY hpa, DWORD dwClearFlags)
  1856. {
  1857.    ARRAYINDEX aicPtrs;
  1858.    ARRAYINDEX ai;
  1859.    ASSERT(IS_VALID_HANDLE(hpa, PTRARRAY));
  1860.    ASSERT(FLAGS_ARE_VALID(dwClearFlags, ALL_STUB_FLAGS));
  1861.    aicPtrs = GetPtrCount(hpa);
  1862.    for (ai = 0; ai < aicPtrs; ai++)
  1863.       ClearStubFlag(GetPtr(hpa, ai), dwClearFlags);
  1864.    return;
  1865. }
  1866. /*
  1867. ** NotifyCreateRecListStatus()
  1868. **
  1869. **
  1870. **
  1871. ** Arguments:
  1872. **
  1873. ** Returns:
  1874. **
  1875. ** Side Effects:  none
  1876. */
  1877. PUBLIC_CODE BOOL NotifyCreateRecListStatus(CREATERECLISTPROC crlp, UINT uMsg,
  1878.                                            LPARAM lp, LPARAM lpCallbackData)
  1879. {
  1880.    BOOL bContinue;
  1881.    /* lpCallbackData may be any value. */
  1882.    ASSERT(! crlp ||
  1883.           IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC));
  1884.    ASSERT(IsValidCreateRecListProcMsg(uMsg));
  1885.    ASSERT(! lp);
  1886.    if (crlp)
  1887.    {
  1888.       TRACE_OUT((TEXT("NotifyReconciliationStatus(): Calling CREATERECLISTPROC with message %s, LPARAM %#lx, callback data %#lx."),
  1889.                  GetCREATERECLISTPROCMSGString(uMsg),
  1890.                  lp,
  1891.                  lpCallbackData));
  1892.       bContinue = (*crlp)(uMsg, lp, lpCallbackData);
  1893.    }
  1894.    else
  1895.    {
  1896.       TRACE_OUT((TEXT("NotifyReconciliationStatus(): Not calling NULL CREATERECLISTPROC with message %s, LPARAM %#lx, callback data %#lx."),
  1897.                  GetCREATERECLISTPROCMSGString(uMsg),
  1898.                  lp,
  1899.                  lpCallbackData));
  1900.       bContinue = TRUE;
  1901.    }
  1902.    if (! bContinue)
  1903.       WARNING_OUT((TEXT("NotifyCreateRecListStatus(): Client callback aborted RecList creation.")));
  1904.    return(bContinue);
  1905. }
  1906. /*
  1907. ** CompareInts()
  1908. **
  1909. **
  1910. **
  1911. ** Arguments:
  1912. **
  1913. ** Returns:
  1914. **
  1915. ** Side Effects:  none
  1916. */
  1917. PUBLIC_CODE COMPARISONRESULT CompareInts(int nFirst, int nSecond)
  1918. {
  1919.    COMPARISONRESULT cr;
  1920.    /* nFirst and nSecond may be any value. */
  1921.    if (nFirst < nSecond)
  1922.       cr = CR_FIRST_SMALLER;
  1923.    else if (nFirst > nSecond)
  1924.       cr = CR_FIRST_LARGER;
  1925.    else
  1926.       cr = CR_EQUAL;
  1927.    return(cr);
  1928. }
  1929. #if defined(DEBUG) || defined(VSTF)
  1930. /*
  1931. ** IsValidFILESTAMPCONDITION()
  1932. **
  1933. **
  1934. **
  1935. ** Arguments:
  1936. **
  1937. ** Returns:
  1938. **
  1939. ** Side Effects:  none
  1940. */
  1941. PUBLIC_CODE BOOL IsValidFILESTAMPCONDITION(FILESTAMPCONDITION fsc)
  1942. {
  1943.    BOOL bResult;
  1944.    switch (fsc)
  1945.    {
  1946.       case FS_COND_EXISTS:
  1947.       case FS_COND_DOES_NOT_EXIST:
  1948.       case FS_COND_UNAVAILABLE:
  1949.          bResult = TRUE;
  1950.          break;
  1951.       default:
  1952.          bResult = FALSE;
  1953.          ERROR_OUT((TEXT("IsValidFILESTAMPCONDITION(): Unknown FILESTAMPCONDITION %d."),
  1954.                     fsc));
  1955.          break;
  1956.    }
  1957.    return(bResult);
  1958. }
  1959. /*
  1960. ** IsValidPCFILESTAMP()
  1961. **
  1962. **
  1963. **
  1964. ** Arguments:
  1965. **
  1966. ** Returns:
  1967. **
  1968. ** Side Effects:  none
  1969. */
  1970. PUBLIC_CODE BOOL IsValidPCFILESTAMP(PCFILESTAMP pcfs)
  1971. {
  1972.    /* dwcbLowLength may be any value. */
  1973.    return(IS_VALID_READ_PTR(pcfs, CFILESTAMP) &&
  1974.           EVAL(IsValidFILESTAMPCONDITION(pcfs->fscond)) &&
  1975.           IS_VALID_STRUCT_PTR(&(pcfs->ftMod), CFILETIME) &&
  1976.           IS_VALID_STRUCT_PTR(&(pcfs->ftModLocal), CFILETIME) &&
  1977.           ! pcfs->dwcbHighLength);
  1978. }
  1979. /*
  1980. ** IsFolderObjectTwinFileStamp()
  1981. **
  1982. **
  1983. **
  1984. ** Arguments:
  1985. **
  1986. ** Returns:
  1987. **
  1988. ** Side Effects:  none
  1989. */
  1990. PUBLIC_CODE BOOL IsFolderObjectTwinFileStamp(PCFILESTAMP pcfs)
  1991. {
  1992.    return(EVAL(! pcfs->ftMod.dwLowDateTime) &&
  1993.           EVAL(! pcfs->ftMod.dwHighDateTime) &&
  1994.           EVAL(! pcfs->dwcbLowLength) &&
  1995.           EVAL(! pcfs->dwcbHighLength));
  1996. }
  1997. /*
  1998. ** IsValidPCRECNODE()
  1999. **
  2000. **
  2001. **
  2002. ** Arguments:
  2003. **
  2004. ** Returns:
  2005. **
  2006. ** Side Effects:  none
  2007. */
  2008. PUBLIC_CODE BOOL IsValidPCRECNODE(PCRECNODE pcrn)
  2009. {
  2010.    return(IS_VALID_READ_PTR(pcrn, CRECNODE) &&
  2011.           IS_VALID_HANDLE(pcrn->hvid, VOLUMEID) &&
  2012.           IS_VALID_STRING_PTR(pcrn->pcszFolder, CSTR) &&
  2013.           IS_VALID_HANDLE(pcrn->hObjectTwin, OBJECTTWIN) &&
  2014.           IS_VALID_STRUCT_PTR(&(pcrn->fsLast), CFILESTAMP) &&
  2015.           IS_VALID_STRUCT_PTR(&(pcrn->fsCurrent), CFILESTAMP) &&
  2016.           FLAGS_ARE_VALID(pcrn->dwFlags, ALL_RECNODE_FLAGS) &&
  2017.           EVAL(IsValidRECNODESTATE(pcrn->rnstate)) &&
  2018.           EVAL(IsValidRECNODEACTION(pcrn->rnaction)) &&
  2019.           EVAL(*(pcrn->priParent->pcszName) ||
  2020.                (IsFolderObjectTwinFileStamp(&(pcrn->fsLast)) &&
  2021.                 IsFolderObjectTwinFileStamp(&(pcrn->fsCurrent)))) &&
  2022.           EVAL(IsReconciledFileStamp(&(pcrn->fsCurrent)) ||
  2023.                MyCompareFileStamps(&(pcrn->fsLast), &(((PCOBJECTTWIN)(pcrn->hObjectTwin))->fsLastRec)) == CR_EQUAL));
  2024. }
  2025. /*
  2026. ** IsValidPCRECITEM()
  2027. **
  2028. **
  2029. **
  2030. ** Arguments:
  2031. **
  2032. ** Returns:
  2033. **
  2034. ** Side Effects:  none
  2035. */
  2036. PUBLIC_CODE BOOL IsValidPCRECITEM(PCRECITEM pcri)
  2037. {
  2038.    BOOL bResult = FALSE;
  2039.    /* Does the twin family associated with this RECITEM still exist? */
  2040.    if (IS_VALID_READ_PTR(pcri, CRECITEM) &&
  2041.        IS_VALID_STRING_PTR(pcri->pcszName, CSTR) &&
  2042.        IS_VALID_HANDLE(pcri->hTwinFamily, TWINFAMILY))
  2043.    {
  2044.       if (IsStubFlagSet(&(((PTWINFAMILY)(pcri->hTwinFamily))->stub),
  2045.                         STUB_FL_UNLINKED))
  2046.          bResult = TRUE;
  2047.       else
  2048.       {
  2049.          ULONG ulcRecNodes = 0;
  2050.          PRECNODE prn;
  2051.          /*
  2052.           * Yes.  Verify the parent pointers, node count, and flags in this
  2053.           * RECITEM.
  2054.           */
  2055.          /* All unavailable RECNODEs should contain action RNA_NOTHING. */
  2056.          for (prn = pcri->prnFirst;
  2057.               prn && IS_VALID_STRUCT_PTR(prn, CRECNODE);
  2058.               prn = prn->prnNext)
  2059.          {
  2060.             /* Does the object twin associated with this RECNODE still exist? */
  2061.             if (IsStubFlagClear(&(((PTWINFAMILY)(pcri->hTwinFamily))->stub),
  2062.                                 STUB_FL_UNLINKED))
  2063.             {
  2064.                /* Yes.  Verify its parent RECITEM pointer. */
  2065.                if (prn->priParent != pcri)
  2066.                {
  2067.                   ERROR_OUT((TEXT("IsValidPCRECITEM(): Bad parent pointer found in RECNODE - parent pointer (%#lx), actual parent (%#lx)."),
  2068.                              prn->priParent,
  2069.                              pcri));
  2070.                   break;
  2071.                }
  2072.             }
  2073.             ASSERT(ulcRecNodes < ULONG_MAX);
  2074.             ulcRecNodes++;
  2075.          }
  2076.          if (! prn)
  2077.          {
  2078.             /* Check RECNODE count. */
  2079.             if (ulcRecNodes == pcri->ulcNodes)
  2080.             {
  2081.                if (ulcRecNodes >= 2)
  2082.                {
  2083.                   /* Now verify the RECITEM's actions. */
  2084.                   switch (pcri->riaction)
  2085.                   {
  2086.                      case RIA_NOTHING:
  2087.                         /* All RECNODEs should contain action RNA_NOTHING. */
  2088.                         for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  2089.                         {
  2090.                            if (prn->rnaction != RNA_NOTHING)
  2091.                            {
  2092.                               ERROR_OUT((TEXT("IsValidPCRECITEM(): Nop RECITEM with non-nop RECNODE action %d."),
  2093.                                          prn->rnaction));
  2094.                               break;
  2095.                            }
  2096.                         }
  2097.                         if (! prn)
  2098.                            bResult = TRUE;
  2099.                         break;
  2100.                      case RIA_COPY:
  2101.                      {
  2102.                         PRECNODE prnSrc = NULL;
  2103.                         ULONG ulcCopyDests = 0;
  2104.                         /*
  2105.                          * There should only be one available RECNODE
  2106.                          * containing action RNA_COPY_FROM_ME.
  2107.                          *
  2108.                          * The other available RECNODEs should contain action
  2109.                          * RNA_COPY_TO_ME or RNA_NOTHING.
  2110.                          */
  2111.                         for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  2112.                         {
  2113.                            if (RECNODE_IS_AVAILABLE(prn))
  2114.                            {
  2115.                               switch (prn->rnaction)
  2116.                               {
  2117.                                  case RNA_COPY_TO_ME:
  2118.                                     ASSERT(ulcCopyDests < ULONG_MAX);
  2119.                                     ulcCopyDests++;
  2120.                                     break;
  2121.                                  case RNA_NOTHING:
  2122.                                     break;
  2123.                                  case RNA_COPY_FROM_ME:
  2124.                                     if (! prnSrc)
  2125.                                        prnSrc = prn;
  2126.                                     else
  2127.                                        ERROR_OUT((TEXT("IsValidPCRECITEM(): Copy RECITEM with multiple source file RECNODEs.")));
  2128.                                     break;
  2129.                                  case RNA_MERGE_ME:
  2130.                                     ERROR_OUT((TEXT("IsValidPCRECITEM(): Copy RECITEM with merge RECNODE.")));
  2131.                                     break;
  2132.                                  default:
  2133.                                     ERROR_OUT((TEXT("IsValidPCRECITEM(): Copy RECITEM with unknown RECNODE action %d."),
  2134.                                                prn->rnaction));
  2135.                                     break;
  2136.                               }
  2137.                            }
  2138.                         }
  2139.                         if (! prn)
  2140.                         {
  2141.                            /* Did we find a copy source? */
  2142.                            if (prnSrc)
  2143.                            {
  2144.                               /* Yes. */
  2145.                               /* Did we find one or more copy destinations? */
  2146.                               if (ulcCopyDests > 0)
  2147.                                  /* Yes. */
  2148.                                  bResult = TRUE;
  2149.                               else
  2150.                                  /* No. */
  2151.                                  ERROR_OUT((TEXT("IsValidPCRECITEM(): Copy RECITEM with no copy destination RECNODEs.")));
  2152.                            }
  2153.                            else
  2154.                               /* No. */
  2155.                               ERROR_OUT((TEXT("IsValidPCRECITEM(): Copy RECITEM with no copy source RECNODE.")));
  2156.                         }
  2157.                         break;
  2158.                      }
  2159.                      case RIA_MERGE:
  2160.                      case RIA_BROKEN_MERGE:
  2161.                      {
  2162.                         PRECNODE prn;
  2163.                         ULONG ulcMergeBuddies = 0;
  2164. #ifdef DEBUG
  2165.                         LPCTSTR pcszAction = (pcri->riaction == RIA_MERGE) ?
  2166.                                            TEXT("merge") :
  2167.                                            TEXT("broken merge");
  2168. #endif
  2169.                         /*
  2170.                          * There should be multiple available RECNODEs
  2171.                          * containing action RNA_MERGE_ME.
  2172.                          *
  2173.                          * The other available RECNODEs should contain action
  2174.                          * RNA_COPY_TO_ME.
  2175.                          */
  2176.                         for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  2177.                         {
  2178.                            if (RECNODE_IS_AVAILABLE(prn))
  2179.                            {
  2180.                               switch (prn->rnaction)
  2181.                               {
  2182.                                  case RNA_COPY_TO_ME:
  2183.                                     break;
  2184.                                  case RNA_NOTHING:
  2185.                                     ERROR_OUT((TEXT("IsValidPCRECITEM(): %s RECITEM with RNA_NOTHING RECNODE."),
  2186.                                                pcszAction));
  2187.                                     break;
  2188.                                  case RNA_COPY_FROM_ME:
  2189.                                     ERROR_OUT((TEXT("IsValidPCRECITEM(): %s RECITEM with RNA_COPY_FROM_ME RECNODE."),
  2190.                                                pcszAction));
  2191.                                     break;
  2192.                                  case RNA_MERGE_ME:
  2193.                                     ASSERT(ulcMergeBuddies < ULONG_MAX);
  2194.                                     ulcMergeBuddies++;
  2195.                                     break;
  2196.                                  default:
  2197.                                     ERROR_OUT((TEXT("IsValidPCRECITEM(): %s RECITEM with unknown RECNODE action %d."),
  2198.                                                pcszAction,
  2199.                                                prn->rnaction));
  2200.                                     break;
  2201.                               }
  2202.                            }
  2203.                         }
  2204.                         if (! prn)
  2205.                         {
  2206.                            /* Are there multiple merge source RECNODEs? */
  2207.                            if (ulcMergeBuddies > 1)
  2208.                               bResult = TRUE;
  2209.                            else
  2210.                               ERROR_OUT((TEXT("IsValidPCRECITEM(): %s RECITEM with too few (%lu) merge source RECNODEs."),
  2211.                                          pcszAction,
  2212.                                          ulcMergeBuddies));
  2213.                         }
  2214.                         break;
  2215.                      }
  2216.                      case RIA_DELETE:
  2217.                      {
  2218.                         BOOL bDelete = FALSE;
  2219.                         /*
  2220.                          * There should be at least one available RECNODE
  2221.                          * marked RNA_DELETE_ME.  All other RECNODEs should be
  2222.                          * marked RNA_NOTHING.
  2223.                          */
  2224.                         for (prn = pcri->prnFirst; prn; prn = prn->prnNext)
  2225.                         {
  2226.                            if (RECNODE_IS_AVAILABLE(prn) &&
  2227.                                prn->rnaction == RNA_DELETE_ME)
  2228.                               bDelete = TRUE;
  2229.                            else if (prn->rnaction != RNA_NOTHING)
  2230.                               ERROR_OUT((TEXT("IsValidPCRECITEM(): Delete RECITEM with RECNODE marked %s."),
  2231.                                          GetRECNODEACTIONString(prn->rnaction)));
  2232.                         }
  2233.                         if (bDelete)
  2234.                            bResult = TRUE;
  2235.                         else
  2236.                            ERROR_OUT((TEXT("IsValidPCRECITEM(): Delete RECITEM with no RECNODEs marked RNA_DELETE_ME.")));
  2237.                         break;
  2238.                      }
  2239.                      default:
  2240.                         ERROR_OUT((TEXT("IsValidPCRECITEM(): Unrecognized RECITEMACTION %d."),
  2241.                                    pcri->riaction));
  2242.                         break;
  2243.                   }
  2244.                }
  2245.                else
  2246.                   ERROR_OUT((TEXT("IsValidPCRECITEM(): RECITEM only has %lu RECNODEs."),
  2247.                              ulcRecNodes));
  2248.             }
  2249.             else
  2250.                ERROR_OUT((TEXT("IsValidPCRECITEM(): RECITEM has bad RECNODE count.  (%lu actual RECNODEs for RECITEM claiming %lu RECNODEs.)"),
  2251.                           ulcRecNodes,
  2252.                           pcri->ulcNodes));
  2253.          }
  2254.       }
  2255.    }
  2256.    return(bResult);
  2257. }
  2258. #endif
  2259. /***************************** Exported Functions ****************************/
  2260. /* RAIDRAID: (16203) AutoDoc CREATERECLISTPROC messages below. */
  2261. /******************************************************************************
  2262. @doc SYNCENGAPI
  2263. @api TWINRESULT | CreateRecList | Creates a reconciliation list for all twins
  2264. or for marked twins.
  2265. @parm HTWINLIST | htl | A handle to the twin list that the reconciliation list
  2266. is to be created from.  All twins in the twin list generate RECITEMs in the
  2267. reconciliation list.
  2268. @parm CREATERECLISTPROC | crlp | A procedure instance address of a callback
  2269. function to be called with status information during the creation of the
  2270. RECLIST.  crlp may be NULL to indicate that no RECLIST creation status callback
  2271. function is to be called.
  2272. @parm LPARAM | lpCallbackData | Callback data to be supplied to the RECLIST
  2273. creation status callback function.  If crlp is NULL, lpCallbackData is ignored.
  2274. @parm PRECLIST * | pprl | A pointer to a RECLIST to be filled in with a
  2275. pointer to the new reconciliation list.  *pprl is only valid if TR_SUCCESS is
  2276. returned.
  2277. @rdesc If the reconciliation list was created successfully, TR_SUCCESS is
  2278. returned.  Otherwise, the reconciliation list was not created successfully, and
  2279. the return value indicates the error that occurred.
  2280. @xref DestroyRecList MarkTwin
  2281. ******************************************************************************/
  2282. SYNCENGAPI TWINRESULT WINAPI CreateRecList(HTWINLIST htl,
  2283.                                            CREATERECLISTPROC crlp,
  2284.                                            LPARAM lpCallbackData,
  2285.                                            PRECLIST *pprl)
  2286. {
  2287.    TWINRESULT tr;
  2288.    if (BeginExclusiveBriefcaseAccess())
  2289.    {
  2290.       DebugEntry(CreateRecList);
  2291. #ifdef EXPV
  2292.       /* Verify parameters. */
  2293.       /* lpCallbackData may be any value. */
  2294.       if (IS_VALID_HANDLE(htl, TWINLIST) &&
  2295.           (! crlp ||
  2296.            IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC)) &&
  2297.           IS_VALID_WRITE_PTR(pprl, PRECLIST))
  2298. #endif
  2299.       {
  2300.          if (NotifyCreateRecListStatus(crlp, CRLS_BEGIN_CREATE_REC_LIST, 0,
  2301.                                        lpCallbackData))
  2302.          {
  2303.             HBRFCASE hbr;
  2304.             /* Expand the required folder twins to object twins. */
  2305.             hbr = GetTwinListBriefcase(htl);
  2306.             InvalidatePathListInfo(GetBriefcasePathList(hbr));
  2307.             tr = ExpandFolderTwinsIntersectingTwinList(htl, crlp,
  2308.                                                        lpCallbackData);
  2309.             if (tr == TR_SUCCESS)
  2310.             {
  2311.                PRECLIST prlNew;
  2312.                /* Try to create a new reconciliation list. */
  2313.                if (AllocateMemory(sizeof(*prlNew), &prlNew))
  2314.                {
  2315.                   /* Initialize RECLIST structure fields. */
  2316.                   prlNew->ulcItems = 0;
  2317.                   prlNew->priFirst = NULL;
  2318.                   prlNew->hbr = hbr;
  2319.                   tr = AddRecItemsToRecList(htl, crlp, lpCallbackData, prlNew);
  2320.                   if (tr == TR_SUCCESS)
  2321.                   {
  2322.                      if (DeleteTwinsFromRecList(prlNew))
  2323.                      {
  2324.                         TRACE_OUT((TEXT("CreateRecList(): Twins implicitly deleted.  Recalculating RECLIST.")));
  2325.                         DestroyListOfRecItems(prlNew->priFirst);
  2326.                         prlNew->ulcItems = 0;
  2327.                         prlNew->priFirst = NULL;
  2328.                         ASSERT(prlNew->hbr == hbr);
  2329.                         tr = AddRecItemsToRecList(htl, crlp, lpCallbackData,
  2330.                                                   prlNew);
  2331.                      }
  2332.                   }
  2333.                   if (tr == TR_SUCCESS)
  2334.                   {
  2335.                      *pprl = prlNew;
  2336.                      /* Don't allow abort. */
  2337.                      NotifyCreateRecListStatus(crlp, CRLS_END_CREATE_REC_LIST,
  2338.                                                0, lpCallbackData);
  2339.                      ASSERT(IS_VALID_STRUCT_PTR(*pprl, CRECLIST));
  2340.                   }
  2341.                   else
  2342.                      /*
  2343.                       * Destroy RECLIST and any RECITEMs that have been created
  2344.                       * so far.
  2345.                       */
  2346.                      MyDestroyRecList(prlNew);
  2347.                }
  2348.             }
  2349.          }
  2350.          else
  2351.             tr = TR_ABORT;
  2352.       }
  2353. #ifdef EXPV
  2354.       else
  2355.          tr = TR_INVALID_PARAMETER;
  2356. #endif
  2357.       DebugExitTWINRESULT(CreateRecList, tr);
  2358.       EndExclusiveBriefcaseAccess();
  2359.    }
  2360.    else
  2361.       tr = TR_REENTERED;
  2362.    return(tr);
  2363. }
  2364. /******************************************************************************
  2365. @doc SYNCENGAPI
  2366. @api TWINRESULT | DestroyRecList | Destroys a reconciliation list created by
  2367. CreateRecList().
  2368. @parm PRECLIST | prl | A pointer to the reconciliation list to be destroyed.
  2369. The RECLIST pointed by pRecList is not valid after DestroyRecList() is called.
  2370. @rdesc If the specified reconciliation list was freed successfully, TR_SUCCESS
  2371. is returned.  Otherwise, the specified reconciliation list was not freed
  2372. successfully, and the return value indicates the error that occurred.
  2373. @xref CreateRecList
  2374. ******************************************************************************/
  2375. SYNCENGAPI TWINRESULT WINAPI DestroyRecList(PRECLIST prl)
  2376. {
  2377.    TWINRESULT tr;
  2378.    if (BeginExclusiveBriefcaseAccess())
  2379.    {
  2380.       DebugEntry(DestroyRecList);
  2381. #ifdef EXPV
  2382.       /* Verify parameters. */
  2383.       if (IS_VALID_STRUCT_PTR(prl, CRECLIST))
  2384. #endif
  2385.       {
  2386.          MyDestroyRecList(prl);
  2387.          tr = TR_SUCCESS;
  2388.       }
  2389. #ifdef EXPV
  2390.       else
  2391.          tr = TR_INVALID_PARAMETER;
  2392. #endif
  2393.       DebugExitTWINRESULT(DestroyRecList, tr);
  2394.       EndExclusiveBriefcaseAccess();
  2395.    }
  2396.    else
  2397.       tr = TR_REENTERED;
  2398.    return(tr);
  2399. }
  2400. /******************************************************************************
  2401. @doc SYNCENGAPI
  2402. @api TWINRESULT | GetFolderTwinStatus | Determines the reconciliation status of
  2403. a folder twin.
  2404. @parm HFOLDERTWIN | hFolderTwin | A handle to the folder twin whose
  2405. reconciliation status is to be determined.
  2406. @parm CREATERECLISTPROC | crlp | A procedure instance address of a callback
  2407. function to be called with status information during the creation of the
  2408. RECLIST.  crlp may be NULL to indicate that no RECLIST creation status callback
  2409. function is to be called.
  2410. @parm LPARAM | lpCallbackData | Callback data to be supplied to the RECLIST
  2411. creation status callback function.  If crlp is NULL, lpCallbackData is ignored.
  2412. @parm PFOLDERTWINSTATUS | pfts | A pointer to a UINT to be filled in with the
  2413. reconciliation status of the folder twin.  *pfts is only valid if TR_SUCCESS is
  2414. returned.  *pfts may be one of the following values:
  2415. @flag FTS_DO_NOTHING | This folder twin is up-to-date.  No reconciliation
  2416. action needs to be taken on it.  N.b., the folder may still contain object
  2417. twins that were not generated by the folder twin that are out-of-date.
  2418. @flag FTS_DO_SOMETHING | This folder twin is out-of-date.  Some reconciliation
  2419. action needs to be taken on it.
  2420. @flag FTS_UNAVAILABLE | This folder twin is unavailable for reconciliation.
  2421. @rdesc If the reconcilation status of the folder twin was determined
  2422. successfully, *pfts is filled in with the status of the folder twin, and
  2423. TR_SUCCESS is returned.  Otherwise, the reconciliation status of the folder
  2424. twin was not determined successfully, *pfts is undefined, and the return
  2425. value indicates the error that occurred.
  2426. @comm If GetFolderTwinStatus() is called with a valid handle to a folder twin
  2427. that has been deleted, TR_DELETED_TWIN will be returned.  N.b., in general,
  2428. calling GetFolderTwinStatus() for a folder twin will not be any slower than
  2429. calling CreateRecList() for that folder twin, and may be significantly faster
  2430. if the folder twin requires reconciliation.
  2431. @xref AddFolderTwin CreateFolderTwinList
  2432. ******************************************************************************/
  2433. SYNCENGAPI TWINRESULT WINAPI GetFolderTwinStatus(HFOLDERTWIN hFolderTwin,
  2434.                                                  CREATERECLISTPROC crlp,
  2435.                                                  LPARAM lpCallbackData,
  2436.                                                  PFOLDERTWINSTATUS pfts)
  2437. {
  2438.    TWINRESULT tr;
  2439.    if (BeginExclusiveBriefcaseAccess())
  2440.    {
  2441.       DebugEntry(GetFolderTwinStatus);
  2442. #ifdef EXPV
  2443.       /* Verify parameters. */
  2444.       /* lpCallbackData may be any value. */
  2445.       if (IS_VALID_HANDLE(hFolderTwin, FOLDERTWIN) &&
  2446.           EVAL(! crlp ||
  2447.                IS_VALID_CODE_PTR(crlp, CREATERECLISTPROC)) &&
  2448.           IS_VALID_WRITE_PTR(pfts, FOLDERTWINSTATUS))
  2449. #endif
  2450.       {
  2451.          /* Has this folder twin already been deleted? */
  2452.          if (IsStubFlagClear(&(((PFOLDERPAIR)hFolderTwin)->stub),
  2453.                              STUB_FL_UNLINKED))
  2454.          {
  2455.             if (NotifyCreateRecListStatus(crlp, CRLS_BEGIN_CREATE_REC_LIST, 0,
  2456.                                           lpCallbackData))
  2457.             {
  2458.                /* No. Determine its status. */
  2459.                InvalidatePathListInfo(
  2460.                   GetBriefcasePathList(((PCFOLDERPAIR)hFolderTwin)->pfpd->hbr));
  2461.                tr = GetFolderPairStatus((PFOLDERPAIR)hFolderTwin, crlp,
  2462.                                         lpCallbackData, pfts);
  2463.                if (tr == TR_SUCCESS)
  2464.                {
  2465.                   /* Don't allow abort. */
  2466.                   NotifyCreateRecListStatus(crlp, CRLS_END_CREATE_REC_LIST, 0,
  2467.                                             lpCallbackData);
  2468.                   if (IsStubFlagSet(&(((PFOLDERPAIR)hFolderTwin)->stub),
  2469.                                     STUB_FL_UNLINKED))
  2470.                   {
  2471.                      WARNING_OUT((TEXT("GetFolderTwinStatus(): Folder twin deleted during status determination.")));
  2472.                      tr = TR_DELETED_TWIN;
  2473.                   }
  2474. #ifdef DEBUG
  2475.                   {
  2476.                      LPCTSTR pcszStatus;
  2477.                      switch (*pfts)
  2478.                      {
  2479.                         case FTS_DO_NOTHING:
  2480.                            pcszStatus = TEXT("FTS_DO_NOTHING");
  2481.                            break;
  2482.                         case FTS_DO_SOMETHING:
  2483.                            pcszStatus = TEXT("FTS_DO_SOMETHING");
  2484.                            break;
  2485.                         default:
  2486.                            ASSERT(*pfts == FTS_UNAVAILABLE);
  2487.                            pcszStatus = TEXT("FTS_UNAVAILABLE");
  2488.                            break;
  2489.                      }
  2490.                      TRACE_OUT((TEXT("GetFolderTwinStatus(): Status of folder %s is %s."),
  2491.                                 DebugGetPathString(((PFOLDERPAIR)hFolderTwin)->hpath),
  2492.                                 pcszStatus));
  2493.                   }
  2494. #endif
  2495.                }
  2496.             }
  2497.             else
  2498.                tr = TR_ABORT;
  2499.          }
  2500.          else
  2501.          {
  2502.             /* Yes.  Bail. */
  2503.             WARNING_OUT((TEXT("GetFolderTwinStatus(): Called on deleted folder twin.")));
  2504.             tr = TR_DELETED_TWIN;
  2505.          }
  2506.       }
  2507. #ifdef EXPV
  2508.       else
  2509.          tr = TR_INVALID_PARAMETER;
  2510. #endif
  2511.       ASSERT(tr != TR_SUCCESS ||
  2512.              IsValidFOLDERTWINSTATUS(*pfts));
  2513.       DebugExitTWINRESULT(GetFolderTwinStatus, tr);
  2514.       EndExclusiveBriefcaseAccess();
  2515.    }
  2516.    else
  2517.       tr = TR_REENTERED;
  2518.    return(tr);
  2519. }