Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2411 lines
67 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: twin.c
  6. //
  7. // This file contains special twin handling functions.
  8. //
  9. // (Even though we've moved to a briefcase metaphor,
  10. // we still refer to twins internally...)
  11. //
  12. // History:
  13. // 08-06-93 ScottH Transferred from twin code
  14. //
  15. //---------------------------------------------------------------------------
  16. #include "brfprv.h" // common headers
  17. #include "res.h"
  18. #include "recact.h"
  19. // APPCOMPAT: due to a compiler bug, we need to declare this structure
  20. // as a 1-element array because it has pointers to functions in it
  21. // and it is in another datasegment.
  22. VTBLENGINE g_vtblEngine[1] = { { 0 } }; // per-instance v-table
  23. #define GetFunction(rgtable, name, type) \
  24. ((rgtable).##name = (type)GetProcAddress((rgtable).hinst, #name)); \
  25. ASSERT((rgtable).##name)
  26. #ifdef DEBUG
  27. #define SzTR(tr) #tr,
  28. #endif
  29. #define MAX_RANGE 0x7fff
  30. // Recitem dwUser values
  31. #define RIU_CHANGED 1
  32. #define RIU_SKIP 2
  33. #define RIU_SHOWSTATUS 3
  34. /*----------------------------------------------------------
  35. Purpose: Compare two structures by folder name
  36. Returns: -1 if <, 0 if ==, 1 if >
  37. Cond: --
  38. */
  39. int CALLBACK _export NCompareFolders(
  40. LPVOID lpv1,
  41. LPVOID lpv2,
  42. LPARAM lParam) // One of: CMP_RECNODES, CMP_FOLDERTWINS
  43. {
  44. switch (lParam)
  45. {
  46. case CMP_RECNODES:
  47. return lstrcmpi(((PRECNODE)lpv1)->pcszFolder, ((PRECNODE)lpv2)->pcszFolder);
  48. case CMP_FOLDERTWINS:
  49. return lstrcmpi(((PCFOLDERTWIN)lpv1)->pcszOtherFolder, ((PCFOLDERTWIN)lpv2)->pcszOtherFolder);
  50. default:
  51. ASSERT(0); // should never get here
  52. }
  53. return 0;
  54. }
  55. //---------------------------------------------------------------------------
  56. // Choose side functions
  57. //---------------------------------------------------------------------------
  58. #ifdef DEBUG
  59. /*----------------------------------------------------------
  60. Purpose: Dump a CHOOSESIDE structure
  61. Returns: --
  62. Cond: --
  63. */
  64. void PRIVATE ChooseSide_Dump(
  65. PCHOOSESIDE pchside)
  66. {
  67. BOOL bDump;
  68. TCHAR szBuf[MAXMSGLEN];
  69. ASSERT(pchside);
  70. #define szDumpLabel TEXT(" *** ")
  71. #define szDumpMargin TEXT(" ")
  72. ENTEREXCLUSIVE();
  73. {
  74. bDump = IsFlagSet(g_uDumpFlags, DF_CHOOSESIDE);
  75. }
  76. LEAVEEXCLUSIVE();
  77. if (bDump)
  78. {
  79. wsprintf(szBuf, TEXT("%s.pszFolder = {%s}\r\n"), (LPTSTR)szDumpLabel, pchside->pszFolder);
  80. OutputDebugString(szBuf);
  81. wsprintf(szBuf, TEXT("%s.dwFlags = 0x%lx\r\n"), (LPTSTR)szDumpMargin, pchside->dwFlags);
  82. OutputDebugString(szBuf);
  83. wsprintf(szBuf, TEXT("%s.nRank = %ld\r\n"), (LPTSTR)szDumpMargin, pchside->nRank);
  84. OutputDebugString(szBuf);
  85. }
  86. #undef szDumpLabel
  87. #undef szDumpMargin
  88. }
  89. /*----------------------------------------------------------
  90. Purpose: Dump a CHOOSESIDE list
  91. Returns: --
  92. Cond: --
  93. */
  94. void PUBLIC ChooseSide_DumpList(
  95. HDSA hdsa)
  96. {
  97. BOOL bDump;
  98. TCHAR szBuf[MAXMSGLEN];
  99. ASSERT(hdsa);
  100. #define szDumpLabel TEXT("Dump CHOOSESIDE list: ")
  101. ENTEREXCLUSIVE();
  102. {
  103. bDump = IsFlagSet(g_uDumpFlags, DF_CHOOSESIDE);
  104. }
  105. LEAVEEXCLUSIVE();
  106. if (bDump)
  107. {
  108. int i;
  109. int cel = DSA_GetItemCount(hdsa);
  110. PCHOOSESIDE pchside;
  111. wsprintf(szBuf, TEXT("%s.count = %lu\r\n"), (LPTSTR)szDumpLabel, cel);
  112. OutputDebugString(szBuf);
  113. if (NULL != (pchside = DSA_GetItemPtr(hdsa, 0)))
  114. {
  115. if (IsFlagSet(pchside->dwFlags, CSF_INSIDE))
  116. OutputDebugString(TEXT("Rank for inside\r\n"));
  117. else
  118. OutputDebugString(TEXT("Rank for outside\r\n"));
  119. }
  120. for (i = 0; i < cel; i++)
  121. {
  122. pchside = DSA_GetItemPtr(hdsa, i);
  123. ChooseSide_Dump(pchside);
  124. }
  125. }
  126. #undef szDumpLabel
  127. }
  128. #endif
  129. /*----------------------------------------------------------
  130. Purpose: Initialize an array of CHOOSESIDE elements from a
  131. recitem list. Array is unsorted.
  132. Returns: --
  133. Cond: The contents of the array are safe as long as the
  134. recitem list lives.
  135. */
  136. void PUBLIC ChooseSide_InitAsFile(
  137. HDSA hdsa,
  138. PRECITEM pri)
  139. {
  140. CHOOSESIDE chside;
  141. PRECNODE prn;
  142. ASSERT(hdsa);
  143. ASSERT(pri);
  144. DSA_DeleteAllItems(hdsa);
  145. // All entries start with these values
  146. chside.dwFlags = 0;
  147. chside.nRank = 0;
  148. // Add each recnode
  149. for (prn = pri->prnFirst; prn; prn = prn->prnNext)
  150. {
  151. chside.htwin = (HTWIN)prn->hObjectTwin;
  152. chside.hvid = prn->hvid;
  153. chside.pszFolder = prn->pcszFolder;
  154. chside.prn = prn;
  155. DSA_InsertItem(hdsa, 0x7fff, &chside);
  156. }
  157. }
  158. /*----------------------------------------------------------
  159. Purpose: Create an array of CHOOSESIDE elements from a recitem
  160. list. Array is unsorted.
  161. Returns: standard result
  162. Cond: The contents of the array are safe as long as the
  163. recitem list lives.
  164. */
  165. HRESULT PUBLIC ChooseSide_CreateAsFile(
  166. HDSA * phdsa,
  167. PRECITEM pri)
  168. {
  169. HRESULT hres;
  170. HDSA hdsa;
  171. ASSERT(phdsa);
  172. ASSERT(pri);
  173. hdsa = DSA_Create(sizeof(CHOOSESIDE), (int)pri->ulcNodes);
  174. if (hdsa)
  175. {
  176. ChooseSide_InitAsFile(hdsa, pri);
  177. hres = NOERROR;
  178. }
  179. else
  180. hres = E_OUTOFMEMORY;
  181. *phdsa = hdsa;
  182. return hres;
  183. }
  184. /*----------------------------------------------------------
  185. Purpose: Create an empty array of CHOOSESIDE elements.
  186. Returns: standard result
  187. Cond: --
  188. */
  189. HRESULT PUBLIC ChooseSide_CreateEmpty(
  190. HDSA * phdsa)
  191. {
  192. HRESULT hres;
  193. HDSA hdsa;
  194. ASSERT(phdsa);
  195. hdsa = DSA_Create(sizeof(CHOOSESIDE), 4);
  196. if (hdsa)
  197. {
  198. hres = NOERROR;
  199. }
  200. else
  201. hres = E_OUTOFMEMORY;
  202. *phdsa = hdsa;
  203. return hres;
  204. }
  205. /*----------------------------------------------------------
  206. Purpose: Create an array of CHOOSESIDE elements from a foldertwin
  207. list. Array is unsorted.
  208. Returns: standard result
  209. Cond: The contents of the array are safe as long as the
  210. foldertwin list lives.
  211. */
  212. HRESULT PUBLIC ChooseSide_CreateAsFolder(
  213. HDSA * phdsa,
  214. PFOLDERTWINLIST pftl)
  215. {
  216. HRESULT hres;
  217. HDSA hdsa;
  218. CHOOSESIDE chside;
  219. ASSERT(pftl);
  220. hdsa = DSA_Create(sizeof(chside), (int)pftl->ulcItems);
  221. if (hdsa)
  222. {
  223. PCFOLDERTWIN pft;
  224. LPCTSTR pszFolderLast = NULL;
  225. // All entries start with these values
  226. chside.dwFlags = CSF_FOLDER;
  227. chside.nRank = 0;
  228. chside.prn = NULL;
  229. // Special case the source folder
  230. chside.htwin = (HTWIN)pftl->pcftFirst->hftSrc;
  231. chside.hvid = pftl->pcftFirst->hvidSrc;
  232. chside.pszFolder = pftl->pcftFirst->pcszSrcFolder;
  233. // (Don't care if this fails)
  234. DSA_InsertItem(hdsa, 0x7fff, &chside);
  235. // Add the other folders (duplicates skipped)
  236. for (pft = pftl->pcftFirst; pft; pft = pft->pcftNext)
  237. {
  238. // Duplicate?
  239. if (pszFolderLast && IsSzEqual(pszFolderLast, pft->pcszOtherFolder))
  240. continue; // Yes (hack: the engine gives us a sorted list)
  241. chside.htwin = (HTWIN)pft->hftOther;
  242. chside.hvid = pft->hvidOther;
  243. chside.pszFolder = pft->pcszOtherFolder;
  244. DSA_InsertItem(hdsa, 0x7fff, &chside);
  245. pszFolderLast = pft->pcszOtherFolder;
  246. }
  247. *phdsa = hdsa;
  248. hres = NOERROR;
  249. }
  250. else
  251. hres = E_OUTOFMEMORY;
  252. return hres;
  253. }
  254. /*----------------------------------------------------------
  255. Purpose: Reset the ranks
  256. Returns: --
  257. Cond: --
  258. */
  259. void PRIVATE ChooseSide_ResetRanks(
  260. HDSA hdsa)
  261. {
  262. int i;
  263. int cel;
  264. ASSERT(hdsa);
  265. cel = DSA_GetItemCount(hdsa);
  266. for (i = 0; i < cel; i++)
  267. {
  268. PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i);
  269. pchside->nRank = 0;
  270. }
  271. }
  272. /*----------------------------------------------------------
  273. Purpose: Determine ranks based on whether each element in the
  274. array is inside the briefcase.
  275. Returns: --
  276. Cond: --
  277. */
  278. void PRIVATE ChooseSide_RankForInside(
  279. HDSA hdsa,
  280. LPCTSTR pszBrfPath, // Root path of briefcase
  281. LPCTSTR pszFolder) // If NULL, choose best outside element
  282. {
  283. int i;
  284. int cel;
  285. int cchLast = 0;
  286. PCHOOSESIDE pchsideLast;
  287. ASSERT(hdsa);
  288. ASSERT(pszBrfPath);
  289. ASSERT(pszFolder);
  290. ASSERT(PathIsPrefix(pszBrfPath, pszFolder));
  291. cel = DSA_GetItemCount(hdsa);
  292. for (i = 0; i < cel; i++)
  293. {
  294. PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i);
  295. DEBUG_CODE( SetFlag(pchside->dwFlags, CSF_INSIDE); )
  296. // Is this item inside this briefcase?
  297. if (PathIsPrefix(pszBrfPath, pchside->pszFolder))
  298. pchside->nRank++; // Yes
  299. // Is this item inside this folder?
  300. if (PathIsPrefix(pszFolder, pchside->pszFolder))
  301. {
  302. int cch = lstrlen(pchside->pszFolder);
  303. pchside->nRank++; // Yes; even better
  304. if (0 == cchLast)
  305. {
  306. cchLast = cch;
  307. pchsideLast = pchside;
  308. }
  309. else
  310. {
  311. // Is this path deeper than the last prefix-matching path?
  312. // (the path closer to the top is better)
  313. if (cch > cchLast)
  314. {
  315. // Yes; demote this one
  316. pchside->nRank--;
  317. }
  318. else
  319. {
  320. // No; demote previous one
  321. ASSERT(pchsideLast);
  322. pchsideLast->nRank--;
  323. cchLast = cch;
  324. pchsideLast = pchside;
  325. }
  326. }
  327. }
  328. }
  329. }
  330. /*----------------------------------------------------------
  331. Purpose: Determine ranks based on whether each element in the
  332. array is outside the briefcase.
  333. Returns: --
  334. Cond: --
  335. */
  336. void PRIVATE ChooseSide_RankForOutside(
  337. HDSA hdsa,
  338. LPCTSTR pszBrfPath) // Root path of briefcase
  339. {
  340. int i;
  341. int cel;
  342. ASSERT(hdsa);
  343. ASSERT(pszBrfPath);
  344. cel = DSA_GetItemCount(hdsa);
  345. for (i = 0; i < cel; i++)
  346. {
  347. PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i);
  348. DEBUG_CODE( ClearFlag(pchside->dwFlags, CSF_INSIDE); )
  349. // Is this item NOT in this briefcase?
  350. if ( !PathIsPrefix(pszBrfPath, pchside->pszFolder) )
  351. {
  352. // Yes
  353. int nDriveType = DRIVE_UNKNOWN;
  354. int ndrive = PathGetDriveNumber(pchside->pszFolder);
  355. if (-1 != ndrive)
  356. {
  357. nDriveType = DriveType(ndrive);
  358. }
  359. pchside->nRank += 2;
  360. if (IsFlagClear(pchside->dwFlags, CSF_FOLDER))
  361. {
  362. // Is the file unavailable?
  363. if (RNS_UNAVAILABLE == pchside->prn->rnstate ||
  364. FS_COND_UNAVAILABLE == pchside->prn->fsCurrent.fscond)
  365. {
  366. // Yes; demote
  367. pchside->nRank--;
  368. }
  369. }
  370. else
  371. {
  372. // Is the folder unavailable?
  373. FOLDERTWINSTATUS uStatus;
  374. Sync_GetFolderTwinStatus((HFOLDERTWIN)pchside->htwin, NULL, 0,
  375. &uStatus);
  376. if (FTS_UNAVAILABLE == uStatus)
  377. {
  378. // Yes; demote
  379. pchside->nRank--;
  380. }
  381. }
  382. // Rank on locality of disk (the closer the better)
  383. if (DRIVE_REMOVABLE == nDriveType || DRIVE_CDROM == nDriveType)
  384. ; // Floppy/removable (do nothing)
  385. else if (PathIsUNC(pchside->pszFolder) || IsNetDrive(ndrive))
  386. pchside->nRank++; // Net
  387. else
  388. pchside->nRank += 2; // Fixed disk
  389. }
  390. }
  391. }
  392. /*----------------------------------------------------------
  393. Purpose: Choose the element with the highest rank.
  394. Returns: TRUE if any element distinguished itself
  395. Cond: --
  396. */
  397. BOOL PRIVATE ChooseSide_GetBestRank(
  398. HDSA hdsa,
  399. PCHOOSESIDE * ppchside)
  400. {
  401. BOOL bRet;
  402. int i;
  403. int cel;
  404. int nRankCur = 0; // (start at 0 since 0 is not good enough to pass muster)
  405. DEBUG_CODE( BOOL bDbgDup = FALSE; )
  406. ASSERT(hdsa);
  407. ASSERT(ppchside);
  408. *ppchside = NULL;
  409. cel = DSA_GetItemCount(hdsa);
  410. for (i = 0; i < cel; i++)
  411. {
  412. PCHOOSESIDE pchside = DSA_GetItemPtr(hdsa, i);
  413. #ifdef DEBUG
  414. if (0 < nRankCur && nRankCur == pchside->nRank)
  415. bDbgDup = TRUE;
  416. #endif
  417. if (nRankCur < pchside->nRank)
  418. {
  419. *ppchside = pchside;
  420. nRankCur = pchside->nRank;
  421. DEBUG_CODE( bDbgDup = FALSE; ) // Reset
  422. }
  423. }
  424. #ifdef DEBUG
  425. // We shouldn't get duplicate highest ranks
  426. if (bDbgDup)
  427. {
  428. // Dump the chooseside list if there are duplicate highest ranks
  429. ChooseSide_DumpList(hdsa);
  430. }
  431. ASSERT(FALSE == bDbgDup);
  432. #endif
  433. bRet = 0 < nRankCur;
  434. ASSERT(bRet && *ppchside || !bRet && !*ppchside);
  435. return bRet;
  436. }
  437. /*----------------------------------------------------------
  438. Purpose: Get the best candidate element (inside or outside).
  439. If the pszFolder is NULL, this function gets the best
  440. outside path.
  441. Returns: TRUE if an element was found
  442. Cond: --
  443. */
  444. BOOL PUBLIC ChooseSide_GetBest(
  445. HDSA hdsa,
  446. LPCTSTR pszBrfPath, // Root path of briefcase
  447. LPCTSTR pszFolder, // If NULL, choose best outside element
  448. PCHOOSESIDE * ppchside)
  449. {
  450. ASSERT(hdsa);
  451. ASSERT(0 < DSA_GetItemCount(hdsa));
  452. ASSERT(pszBrfPath);
  453. ASSERT(ppchside);
  454. ChooseSide_ResetRanks(hdsa);
  455. // Are we ranking for inside paths?
  456. if (pszFolder)
  457. {
  458. // Yes; inside wins
  459. ChooseSide_RankForInside(hdsa, pszBrfPath, pszFolder);
  460. }
  461. else
  462. {
  463. // No; outside wins
  464. ChooseSide_RankForOutside(hdsa, pszBrfPath);
  465. }
  466. return ChooseSide_GetBestRank(hdsa, ppchside);
  467. }
  468. /*----------------------------------------------------------
  469. Purpose: Get the next best candidate element (inside or outside).
  470. ChooseSide_GetBest must have been previously called.
  471. Returns: TRUE if an element was found
  472. Cond: --
  473. */
  474. BOOL PUBLIC ChooseSide_GetNextBest(
  475. HDSA hdsa,
  476. PCHOOSESIDE * ppchside)
  477. {
  478. PCHOOSESIDE pchside;
  479. ASSERT(hdsa);
  480. ASSERT(0 < DSA_GetItemCount(hdsa));
  481. ASSERT(ppchside);
  482. // Get the best rank and reset it
  483. ChooseSide_GetBestRank(hdsa, &pchside);
  484. pchside->nRank = 0;
  485. // Now get the next best rank
  486. return ChooseSide_GetBestRank(hdsa, ppchside);
  487. }
  488. /*----------------------------------------------------------
  489. Purpose: Frees an array of CHOOSESIDE elements.
  490. Returns: --
  491. Cond: --
  492. */
  493. void PUBLIC ChooseSide_Free(
  494. HDSA hdsa)
  495. {
  496. if (hdsa)
  497. {
  498. DSA_Destroy(hdsa);
  499. }
  500. }
  501. //---------------------------------------------------------------------------
  502. //
  503. //---------------------------------------------------------------------------
  504. /*----------------------------------------------------------
  505. Purpose: Determine which node is inside and outside a briefcase.
  506. This function takes a list of recnodes and determines
  507. which node is "inside" a briefcase and which one is
  508. "outside" a briefcase. "Inside" means the file exists
  509. somewhere underneath the briefcase path indicated by
  510. atomBrf. "Outside" is anywhere else (but may be in
  511. a different briefcase as well).
  512. Returns: --
  513. Cond: --
  514. */
  515. HRESULT PUBLIC Sync_GetNodePair(
  516. PRECITEM pri,
  517. LPCTSTR pszBrfPath,
  518. LPCTSTR pszInsideDir, // Which folder inside the briefcase to consider
  519. PRECNODE * pprnInside,
  520. PRECNODE * pprnOutside)
  521. {
  522. HRESULT hres;
  523. HDSA hdsa;
  524. ASSERT(pri);
  525. ASSERT(pszBrfPath);
  526. ASSERT(pszInsideDir);
  527. ASSERT(pprnInside);
  528. ASSERT(pprnOutside);
  529. ASSERT(PathIsPrefix(pszBrfPath, pszInsideDir));
  530. hres = ChooseSide_CreateAsFile(&hdsa, pri);
  531. if (SUCCEEDED(hres))
  532. {
  533. PCHOOSESIDE pchside;
  534. // Get inside folder
  535. if (ChooseSide_GetBest(hdsa, pszBrfPath, pszInsideDir, &pchside))
  536. {
  537. *pprnInside = pchside->prn;
  538. }
  539. else
  540. {
  541. ASSERT(0);
  542. *pprnInside = NULL;
  543. hres = E_FAIL;
  544. }
  545. // Get outside folder
  546. if (ChooseSide_GetBest(hdsa, pszBrfPath, NULL, &pchside))
  547. {
  548. *pprnOutside = pchside->prn;
  549. }
  550. else
  551. {
  552. ASSERT(0);
  553. *pprnOutside = NULL;
  554. hres = E_FAIL;
  555. }
  556. #ifdef DEBUG
  557. if (SUCCEEDED(hres) && IsFlagSet(g_uDumpFlags, DF_PATHS))
  558. {
  559. TRACE_MSG(TF_ALWAYS, TEXT("Choosing pairs: %s and %s"), (*pprnInside)->pcszFolder,
  560. (*pprnOutside)->pcszFolder);
  561. }
  562. #endif
  563. ChooseSide_Free(hdsa);
  564. }
  565. else
  566. {
  567. *pprnInside = NULL;
  568. *pprnOutside = NULL;
  569. }
  570. return hres;
  571. }
  572. /*----------------------------------------------------------
  573. Purpose: Checks if we've loaded the sync engine
  574. Returns: TRUE if loaded
  575. Cond: --
  576. */
  577. BOOL PUBLIC Sync_IsEngineLoaded()
  578. {
  579. BOOL bRet;
  580. ENTEREXCLUSIVE();
  581. {
  582. bRet = g_vtblEngine[0].hinst != NULL;
  583. }
  584. LEAVEEXCLUSIVE();
  585. return bRet;
  586. }
  587. /*----------------------------------------------------------
  588. Purpose: Load the SYNCENG.DLL and initialize the v-table.
  589. Returns: TRUE on success
  590. Cond: --
  591. */
  592. BOOL PUBLIC Sync_QueryVTable(void)
  593. {
  594. BOOL bRet = TRUE;
  595. HINSTANCE hinst;
  596. ENTEREXCLUSIVE();
  597. {
  598. hinst = g_vtblEngine[0].hinst;
  599. }
  600. LEAVEEXCLUSIVE();
  601. // We want to assure that the engine is loaded the same
  602. // number of times that SYNCUI is (by a process). This prevents
  603. // Kernel from nuking the engine prematurely (if a
  604. // process is terminated).
  605. //
  606. // We go thru these hoops simply because SYNCUI does not
  607. // load SYNCENG immediately upon PROCESS_ATTACH. We wait
  608. // until we *really* need to load it the first time.
  609. // Once we finally do load it, we need to keep the load
  610. // count current.
  611. //
  612. // Kernel frees SYNCUI and SYNCENG for us.
  613. //
  614. if (NULL == hinst)
  615. {
  616. VTBLENGINE vtbl;
  617. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Loading %s (cProcess = %d)"),
  618. (LPCTSTR)c_szEngineDLL, g_cProcesses); )
  619. ZeroInit(&vtbl, sizeof(VTBLENGINE));
  620. // Don't be in the critical section when we load the DLL
  621. // or call GetProcAddress, since our LibMain can block on
  622. // this critical section.
  623. ASSERT_NOT_EXCLUSIVE();
  624. hinst = LoadLibrary(c_szEngineDLL);
  625. if ( ISVALIDHINSTANCE(hinst) )
  626. {
  627. // We are loading for the first time. Fill the vtable.
  628. //
  629. vtbl.hinst = hinst;
  630. // Get all the function addresses
  631. //
  632. GetFunction(vtbl, OpenBriefcase, OPENBRIEFCASEINDIRECT);
  633. GetFunction(vtbl, SaveBriefcase, SAVEBRIEFCASEINDIRECT);
  634. GetFunction(vtbl, CloseBriefcase, CLOSEBRIEFCASEINDIRECT);
  635. GetFunction(vtbl, ClearBriefcaseCache, CLEARBRIEFCASECACHEINDIRECT);
  636. GetFunction(vtbl, DeleteBriefcase, DELETEBRIEFCASEINDIRECT);
  637. GetFunction(vtbl, GetOpenBriefcaseInfo, GETOPENBRIEFCASEINFOINDIRECT);
  638. GetFunction(vtbl, FindFirstBriefcase, FINDFIRSTBRIEFCASEINDIRECT);
  639. GetFunction(vtbl, FindNextBriefcase, FINDNEXTBRIEFCASEINDIRECT);
  640. GetFunction(vtbl, FindBriefcaseClose, FINDBRIEFCASECLOSEINDIRECT);
  641. GetFunction(vtbl, AddObjectTwin, ADDOBJECTTWININDIRECT);
  642. GetFunction(vtbl, AddFolderTwin, ADDFOLDERTWININDIRECT);
  643. GetFunction(vtbl, ReleaseTwinHandle, RELEASETWINHANDLEINDIRECT);
  644. GetFunction(vtbl, DeleteTwin, DELETETWININDIRECT);
  645. GetFunction(vtbl, GetObjectTwinHandle, GETOBJECTTWINHANDLEINDIRECT);
  646. GetFunction(vtbl, IsFolderTwin, ISFOLDERTWININDIRECT);
  647. GetFunction(vtbl, CreateFolderTwinList, CREATEFOLDERTWINLISTINDIRECT);
  648. GetFunction(vtbl, DestroyFolderTwinList, DESTROYFOLDERTWINLISTINDIRECT);
  649. GetFunction(vtbl, GetFolderTwinStatus, GETFOLDERTWINSTATUSINDIRECT);
  650. GetFunction(vtbl, IsOrphanObjectTwin, ISORPHANOBJECTTWININDIRECT);
  651. GetFunction(vtbl, CountSourceFolderTwins, COUNTSOURCEFOLDERTWINSINDIRECT);
  652. GetFunction(vtbl, AnyTwins, ANYTWINSINDIRECT);
  653. GetFunction(vtbl, CreateTwinList, CREATETWINLISTINDIRECT);
  654. GetFunction(vtbl, DestroyTwinList, DESTROYTWINLISTINDIRECT);
  655. GetFunction(vtbl, AddTwinToTwinList, ADDTWINTOTWINLISTINDIRECT);
  656. GetFunction(vtbl, AddAllTwinsToTwinList, ADDALLTWINSTOTWINLISTINDIRECT);
  657. GetFunction(vtbl, RemoveTwinFromTwinList, REMOVETWINFROMTWINLISTINDIRECT);
  658. GetFunction(vtbl, RemoveAllTwinsFromTwinList, REMOVEALLTWINSFROMTWINLISTINDIRECT);
  659. GetFunction(vtbl, CreateRecList, CREATERECLISTINDIRECT);
  660. GetFunction(vtbl, DestroyRecList, DESTROYRECLISTINDIRECT);
  661. GetFunction(vtbl, ReconcileItem, RECONCILEITEMINDIRECT);
  662. GetFunction(vtbl, BeginReconciliation, BEGINRECONCILIATIONINDIRECT);
  663. GetFunction(vtbl, EndReconciliation, ENDRECONCILIATIONINDIRECT);
  664. GetFunction(vtbl, IsPathOnVolume, ISPATHONVOLUMEINDIRECT);
  665. GetFunction(vtbl, GetVolumeDescription, GETVOLUMEDESCRIPTIONINDIRECT);
  666. }
  667. else
  668. {
  669. bRet = FALSE;
  670. }
  671. ENTEREXCLUSIVE();
  672. {
  673. g_vtblEngine[0] = vtbl;
  674. }
  675. LEAVEEXCLUSIVE();
  676. }
  677. return bRet;
  678. }
  679. /*----------------------------------------------------------
  680. Purpose: Free the engine DLL if it is loaded
  681. Returns: --
  682. Cond: --
  683. */
  684. void PUBLIC Sync_ReleaseVTable()
  685. {
  686. HINSTANCE hinst;
  687. ENTEREXCLUSIVE();
  688. {
  689. hinst = g_vtblEngine[0].hinst;
  690. }
  691. LEAVEEXCLUSIVE();
  692. if (NULL != hinst)
  693. {
  694. DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("Freeing %s (cProcess = %d)"),
  695. (LPCTSTR)c_szEngineDLL, g_cProcesses); )
  696. // We must call FreeLibrary() on the sync engine even during a
  697. // PROCESS_DETACH. We may be getting detached from a process even
  698. // though the process isn't being terminated. If we don't unload
  699. // the sync engine now, it won't be unloaded until the process is
  700. // terminated.
  701. //
  702. FreeLibrary(hinst);
  703. ENTEREXCLUSIVE();
  704. {
  705. ZeroInit(&g_vtblEngine[0], sizeof(VTBLENGINE));
  706. }
  707. LEAVEEXCLUSIVE();
  708. }
  709. #ifdef DEBUG
  710. ENTEREXCLUSIVE();
  711. {
  712. ASSERT(g_vtblEngine[0].hinst == NULL);
  713. }
  714. LEAVEEXCLUSIVE();
  715. #endif
  716. }
  717. /*----------------------------------------------------------
  718. Purpose: Set the last sync error.
  719. Returns: same twinresult
  720. Cond: --
  721. */
  722. TWINRESULT PUBLIC Sync_SetLastError(
  723. TWINRESULT tr)
  724. {
  725. ENTEREXCLUSIVE();
  726. {
  727. ASSERTEXCLUSIVE();
  728. MySetTwinResult(tr);
  729. }
  730. LEAVEEXCLUSIVE();
  731. return tr;
  732. }
  733. /*----------------------------------------------------------
  734. Purpose: Get the last sync error.
  735. Returns: twinresult
  736. Cond: --
  737. */
  738. TWINRESULT PUBLIC Sync_GetLastError()
  739. {
  740. TWINRESULT tr;
  741. ENTEREXCLUSIVE();
  742. {
  743. ASSERTEXCLUSIVE();
  744. tr = MyGetTwinResult();
  745. }
  746. LEAVEEXCLUSIVE();
  747. return tr;
  748. }
  749. /*----------------------------------------------------------
  750. Purpose: Returns the number of recitems that would require
  751. some reconciliation.
  752. Returns: see above
  753. Cond: --
  754. */
  755. ULONG PUBLIC CountActionItems(
  756. PRECLIST prl)
  757. {
  758. PRECITEM pri;
  759. ULONG ulc;
  760. for (pri = prl->priFirst, ulc = 0; pri; pri = pri->priNext)
  761. {
  762. if (IsFileRecItem(pri) &&
  763. RIU_SKIP != pri->dwUser &&
  764. RIA_NOTHING != pri->riaction &&
  765. RIA_BROKEN_MERGE != pri->riaction)
  766. {
  767. ulc++;
  768. pri->dwUser = RIU_SHOWSTATUS;
  769. }
  770. }
  771. return ulc;
  772. }
  773. /*----------------------------------------------------------
  774. Purpose: Displays appropriate error message on update errors
  775. Returns: --
  776. Cond: --
  777. */
  778. void PRIVATE HandleUpdateErrors(
  779. HWND hwndOwner,
  780. HRESULT hres,
  781. UINT uFlags) // RF_*
  782. {
  783. // Is this an update while adding files?
  784. if (IsFlagSet(uFlags, RF_ONADD))
  785. {
  786. // Yes
  787. static SETbl const c_rgseUpdateOnAdd[] = {
  788. // The out of memory message should be handled by caller
  789. { E_TR_DEST_OPEN_FAILED, IDS_ERR_ADD_READONLY, MB_WARNING },
  790. { E_TR_DEST_WRITE_FAILED, IDS_ERR_ADD_FULLDISK, MB_WARNING },
  791. { E_TR_UNAVAILABLE_VOLUME, IDS_ERR_ADD_UNAVAIL_VOL, MB_WARNING },
  792. { E_TR_SRC_OPEN_FAILED, IDS_ERR_ADD_SOURCE_FILE, MB_WARNING },
  793. };
  794. SEMsgBox(hwndOwner, IDS_CAP_ADD, hres, c_rgseUpdateOnAdd, ARRAYSIZE(c_rgseUpdateOnAdd));
  795. }
  796. else
  797. {
  798. // No
  799. static SETbl const c_rgseUpdate[] = {
  800. { E_TR_OUT_OF_MEMORY, IDS_OOM_UPDATE, MB_ERROR },
  801. { E_TR_DEST_OPEN_FAILED, IDS_ERR_READONLY, MB_INFO },
  802. { E_TR_DEST_WRITE_FAILED, IDS_ERR_FULLDISK, MB_WARNING },
  803. { E_TR_UNAVAILABLE_VOLUME, IDS_ERR_UPD_UNAVAIL_VOL,MB_WARNING },
  804. { E_TR_FILE_CHANGED, IDS_ERR_FILE_CHANGED, MB_INFO },
  805. { E_TR_SRC_OPEN_FAILED, IDS_ERR_SOURCE_FILE, MB_WARNING },
  806. };
  807. SEMsgBox(hwndOwner, IDS_CAP_UPDATE, hres, c_rgseUpdate, ARRAYSIZE(c_rgseUpdate));
  808. }
  809. }
  810. typedef struct tagPROGPARAM
  811. {
  812. HWND hwndProgress;
  813. WORD wPosMax;
  814. WORD wPosBase;
  815. WORD wPosPrev;
  816. BOOL bSkip;
  817. } PROGPARAM, * PPROGPARAM;
  818. /*----------------------------------------------------------
  819. Purpose: Status procedure that is called during a single
  820. ReconcileItem call.
  821. Returns: varies
  822. Cond: --
  823. */
  824. BOOL CALLBACK RecStatusProc(
  825. RECSTATUSPROCMSG msg,
  826. LPARAM lParam,
  827. LPARAM lParamUser)
  828. {
  829. BOOL bRet;
  830. PRECSTATUSUPDATE prsu = (PRECSTATUSUPDATE)lParam;
  831. PPROGPARAM pprogparam = (PPROGPARAM)lParamUser;
  832. HWND hwndProgress = pprogparam->hwndProgress;
  833. WORD wPos;
  834. bRet = !UpdBar_QueryAbort(hwndProgress);
  835. switch (msg)
  836. {
  837. case RS_BEGIN_COPY:
  838. case RS_DELTA_COPY:
  839. case RS_END_COPY:
  840. case RS_BEGIN_MERGE:
  841. case RS_DELTA_MERGE:
  842. case RS_END_MERGE:
  843. #ifdef NEW_REC
  844. case RS_BEGIN_DELETE:
  845. case RS_DELTA_DELETE:
  846. case RS_END_DELETE:
  847. #endif
  848. TRACE_MSG(TF_PROGRESS, TEXT("Reconcile progress = %lu of %lu"), prsu->ulProgress, prsu->ulScale);
  849. ASSERT(prsu->ulProgress <= prsu->ulScale);
  850. if (0 < prsu->ulScale && !pprogparam->bSkip)
  851. {
  852. wPos = LOWORD(LODWORD( (((__int64)pprogparam->wPosMax * prsu->ulProgress) / prsu->ulScale) ));
  853. TRACE_MSG(TF_PROGRESS, TEXT("Max wPos = %u, new wPos = %u, old wPos = %u"),
  854. pprogparam->wPosMax, wPos, pprogparam->wPosPrev);
  855. if (wPos > pprogparam->wPosPrev && wPos < pprogparam->wPosMax)
  856. {
  857. WORD wPosReal = pprogparam->wPosBase + wPos;
  858. TRACE_MSG(TF_PROGRESS, TEXT("Setting real position = %u"), wPosReal);
  859. UpdBar_SetPos(hwndProgress, wPosReal);
  860. pprogparam->wPosPrev = wPos;
  861. }
  862. }
  863. break;
  864. default:
  865. ASSERT(0);
  866. break;
  867. }
  868. return bRet;
  869. }
  870. /*----------------------------------------------------------
  871. Purpose: Decides the description string while updating. The
  872. string is something like "Copying from 'Foo' to 'Bar'"
  873. or "Merging files in 'Foo' and 'Bar'"
  874. Returns: string in pszBuf
  875. Cond: --
  876. */
  877. void PRIVATE DecideDescString(
  878. LPCTSTR pszBrfPath,
  879. PRECITEM pri,
  880. LPTSTR pszBuf,
  881. int cbBuf,
  882. LPTSTR pszPathBuf)
  883. {
  884. HRESULT hres;
  885. RA_ITEM * pitem;
  886. ASSERT(pszBrfPath);
  887. ASSERT(pri);
  888. ASSERT(pszBuf);
  889. hres = RAI_CreateFromRecItem(&pitem, pszBrfPath, pri);
  890. if (SUCCEEDED(hres))
  891. {
  892. UINT ids;
  893. LPTSTR pszMsg;
  894. LPCTSTR pszFrom;
  895. LPCTSTR pszTo;
  896. lstrcpy(pszPathBuf, pitem->siInside.pszDir);
  897. PathAppend(pszPathBuf, pitem->pszName);
  898. switch (pitem->uAction)
  899. {
  900. case RAIA_TOOUT:
  901. ids = IDS_UPDATE_Copy;
  902. pszFrom = PathFindFileName(pitem->siInside.pszDir);
  903. pszTo = PathFindFileName(pitem->siOutside.pszDir);
  904. break;
  905. case RAIA_TOIN:
  906. ids = IDS_UPDATE_Copy;
  907. pszFrom = PathFindFileName(pitem->siOutside.pszDir);
  908. pszTo = PathFindFileName(pitem->siInside.pszDir);
  909. break;
  910. case RAIA_MERGE:
  911. ids = IDS_UPDATE_Merge;
  912. // (Arbitrary)
  913. pszFrom = PathFindFileName(pitem->siInside.pszDir);
  914. pszTo = PathFindFileName(pitem->siOutside.pszDir);
  915. break;
  916. case RAIA_DELETEOUT:
  917. ids = IDS_UPDATE_Delete;
  918. pszFrom = PathFindFileName(pitem->siOutside.pszDir);
  919. pszTo = NULL;
  920. break;
  921. case RAIA_DELETEIN:
  922. ids = IDS_UPDATE_Delete;
  923. pszFrom = PathFindFileName(pitem->siInside.pszDir);
  924. pszTo = NULL;
  925. break;
  926. default:
  927. ASSERT(0);
  928. ids = 0;
  929. break;
  930. }
  931. if (ConstructMessage(&pszMsg, g_hinst, MAKEINTRESOURCE(ids),
  932. pszFrom, pszTo))
  933. {
  934. lstrcpyn(pszBuf, pszMsg, cbBuf);
  935. GFree(pszMsg);
  936. }
  937. else
  938. *pszBuf = 0;
  939. }
  940. else
  941. *pszBuf = 0;
  942. }
  943. /*----------------------------------------------------------
  944. Purpose: Reconcile a given reclist
  945. Returns: standard result
  946. Cond: --
  947. */
  948. HRESULT PUBLIC Sync_ReconcileRecList(
  949. PRECLIST prl, // ptr to reclist
  950. LPCTSTR pszBrfPath,
  951. HWND hwndProgress,
  952. UINT uFlags) // RF_*
  953. {
  954. HRESULT hres;
  955. if (prl)
  956. {
  957. HWND hwndOwner = GetParent(hwndProgress);
  958. HWND hwndStatusText = UpdBar_GetStatusWindow(hwndProgress);
  959. TCHAR szPath[MAX_PATH];
  960. TCHAR sz[MAXBUFLEN];
  961. TWINRESULT tr;
  962. PRECITEM pri;
  963. PROGPARAM progparam;
  964. ULONG ulcItems;
  965. WORD wDelta;
  966. DEBUG_CODE( Sync_DumpRecList(TR_SUCCESS, prl, TEXT("Updating")); )
  967. hres = NOERROR; // assume success
  968. // Grab the mutex to delay any further calculation in any
  969. // Briefcase views' secondary threads until we're done
  970. // processing here.
  971. Delay_Own();
  972. // Determine the range of the progress bar
  973. UpdBar_SetRange(hwndProgress, MAX_RANGE);
  974. ulcItems = CountActionItems(prl);
  975. if (0 < ulcItems)
  976. wDelta = (WORD)(MAX_RANGE / ulcItems);
  977. else
  978. wDelta = 0;
  979. progparam.hwndProgress = hwndProgress;
  980. // Start updating
  981. Sync_BeginRec(prl->hbr);
  982. ulcItems = 0;
  983. for (pri = prl->priFirst; pri; pri = pri->priNext)
  984. {
  985. // Did the user explicitly skip this recitem or
  986. // is this a broken merge?
  987. if (RIU_SKIP == pri->dwUser ||
  988. RIA_BROKEN_MERGE == pri->riaction)
  989. {
  990. // Yes; don't call ReconcileItem
  991. continue;
  992. }
  993. // Is something going to be done to this recitem?
  994. if (RIU_SHOWSTATUS == pri->dwUser)
  995. {
  996. // Yes; update the name of the file we're updating
  997. UpdBar_SetName(hwndProgress, pri->pcszName);
  998. DecideDescString(pszBrfPath, pri, sz, ARRAYSIZE(sz), szPath);
  999. UpdBar_SetDescription(hwndProgress, sz);
  1000. ASSERT(0 < wDelta);
  1001. progparam.wPosBase = (WORD)(wDelta * ulcItems);
  1002. progparam.wPosMax = wDelta;
  1003. progparam.wPosPrev = 0;
  1004. progparam.bSkip = FALSE;
  1005. }
  1006. else
  1007. {
  1008. progparam.bSkip = TRUE;
  1009. }
  1010. // Call ReconcileItem even for nops, so the recnode states
  1011. // will be updated by the engine.
  1012. tr = Sync_ReconcileItem(pri, RecStatusProc, (LPARAM)&progparam,
  1013. RI_FL_FEEDBACK_WINDOW_VALID, hwndProgress, hwndStatusText);
  1014. if (TR_SUCCESS != tr &&
  1015. IsFileRecItem(pri)) // ignore folder recitem errors
  1016. {
  1017. // On some conditions, stop updating completely
  1018. hres = HRESULT_FROM_TR(tr);
  1019. switch (hres)
  1020. {
  1021. case E_TR_OUT_OF_MEMORY:
  1022. case E_TR_RH_LOAD_FAILED: {
  1023. // Couldn't load the merge handler. Tell the user but
  1024. // continue on...
  1025. int id = MsgBox(hwndOwner,
  1026. MAKEINTRESOURCE(IDS_ERR_NO_MERGE_HANDLER),
  1027. MAKEINTRESOURCE(IDS_CAP_UPDATE),
  1028. NULL,
  1029. MB_WARNING | MB_OKCANCEL,
  1030. PathGetDisplayName(szPath, sz));
  1031. if (IDOK == id)
  1032. break; // continue updating other files
  1033. }
  1034. goto StopUpdating;
  1035. case E_TR_DELETED_TWIN:
  1036. // Allow the updating to continue.
  1037. break;
  1038. case E_TR_DEST_OPEN_FAILED:
  1039. case E_TR_FILE_CHANGED:
  1040. if (IsFlagClear(uFlags, RF_ONADD))
  1041. {
  1042. // Allow the updating to continue. Remember the
  1043. // latest error for the end.
  1044. break;
  1045. }
  1046. // Fall thru
  1047. // | |
  1048. // v v
  1049. default:
  1050. goto StopUpdating;
  1051. }
  1052. }
  1053. // Was something done to this recitem?
  1054. if (RIU_SHOWSTATUS == pri->dwUser)
  1055. {
  1056. // Yes; update the progress bar
  1057. UpdBar_SetPos(hwndProgress, (WORD)(wDelta * ++ulcItems));
  1058. }
  1059. // Check if the Cancel button was pressed
  1060. if (UpdBar_QueryAbort(hwndProgress))
  1061. {
  1062. hres = E_ABORT;
  1063. break;
  1064. }
  1065. }
  1066. StopUpdating:
  1067. if (FAILED(hres))
  1068. {
  1069. Sync_DumpRecItem(tr, pri, NULL);
  1070. HandleUpdateErrors(hwndOwner, hres, uFlags);
  1071. if (IsFlagSet(uFlags, RF_ONADD))
  1072. {
  1073. // Hack: since the caller also handles some error messages,
  1074. // return a generic failure code to prevent repeated
  1075. // error messages.
  1076. hres = E_FAIL;
  1077. }
  1078. }
  1079. // Were there any items at all?
  1080. else if (0 == prl->ulcItems)
  1081. {
  1082. // No
  1083. MsgBox(hwndOwner, MAKEINTRESOURCE(IDS_MSG_NoMatchingFiles),
  1084. MAKEINTRESOURCE(IDS_CAP_UPDATE), NULL, MB_INFO);
  1085. }
  1086. Sync_EndRec(prl->hbr);
  1087. Delay_Release();
  1088. }
  1089. else
  1090. hres = E_INVALIDARG;
  1091. return hres;
  1092. }
  1093. /*----------------------------------------------------------
  1094. Purpose: Status procedure that is called during a single
  1095. ReconcileItem call.
  1096. Returns: varies
  1097. Cond: --
  1098. */
  1099. BOOL CALLBACK CreateRecListProc(
  1100. CREATERECLISTPROCMSG msg,
  1101. LPARAM lParam,
  1102. LPARAM lParamUser)
  1103. {
  1104. return !AbortEvt_Query((PABORTEVT)lParamUser);
  1105. }
  1106. /*----------------------------------------------------------
  1107. Purpose: Creates a reclist and optionally shows a progress
  1108. bar during the creation.
  1109. Returns: standard result
  1110. Cond: --
  1111. */
  1112. HRESULT PUBLIC Sync_CreateRecListEx(
  1113. HTWINLIST htl,
  1114. PABORTEVT pabortevt,
  1115. PRECLIST * pprl)
  1116. {
  1117. TWINRESULT tr;
  1118. ASSERT(pprl);
  1119. tr = Sync_CreateRecList(htl, CreateRecListProc, (LPARAM)pabortevt, pprl);
  1120. return HRESULT_FROM_TR(tr);
  1121. }
  1122. /*----------------------------------------------------------
  1123. Purpose: Return true if the file or folder is a twin.
  1124. There are some cases when this function cannot successfully
  1125. determine this unless the caller first tells it explicitly
  1126. whether the object is a file or folder. Otherwise this
  1127. function will attempt to determine this on its own.
  1128. Returns: S_OK if it is a twin
  1129. S_FALSE if it is not
  1130. any other is an error
  1131. Cond: --
  1132. */
  1133. HRESULT PUBLIC Sync_IsTwin(
  1134. HBRFCASE hbrfcase,
  1135. LPCTSTR pszPath,
  1136. UINT uFlags) // SF_* flags
  1137. {
  1138. HRESULT hres;
  1139. TWINRESULT tr;
  1140. ASSERT(pszPath);
  1141. // The caller may already know whether this is a twin or not.
  1142. // Remind him.
  1143. if (IsFlagSet(uFlags, SF_ISTWIN))
  1144. return S_OK;
  1145. else if (IsFlagSet(uFlags, SF_ISNOTTWIN))
  1146. return S_FALSE;
  1147. // Is this a folder?
  1148. if (IsFlagSet(uFlags, SF_ISFOLDER) ||
  1149. PathIsDirectory(pszPath))
  1150. {
  1151. // Yes; is it a twin?
  1152. BOOL bRet;
  1153. tr = Sync_IsFolder(hbrfcase, pszPath, &bRet);
  1154. if (TR_SUCCESS == tr)
  1155. {
  1156. // Yes/no
  1157. hres = bRet ? S_OK : S_FALSE;
  1158. }
  1159. else
  1160. {
  1161. // Error
  1162. hres = HRESULT_FROM_TR(tr);
  1163. }
  1164. }
  1165. else
  1166. {
  1167. // No
  1168. HOBJECTTWIN hot;
  1169. TCHAR szDir[MAX_PATH];
  1170. lstrcpy(szDir, pszPath);
  1171. PathRemoveFileSpec(szDir);
  1172. tr = Sync_GetObject(hbrfcase, szDir, PathFindFileName(pszPath), &hot);
  1173. if (TR_SUCCESS == tr)
  1174. {
  1175. // Is it a twin?
  1176. if (NULL != hot)
  1177. {
  1178. // Yes
  1179. Sync_ReleaseTwin(hot);
  1180. hres = S_OK;
  1181. }
  1182. else
  1183. {
  1184. // No; (no need to release a null handle)
  1185. hres = S_FALSE;
  1186. }
  1187. }
  1188. else
  1189. {
  1190. // Error
  1191. hres = HRESULT_FROM_TR(tr);
  1192. }
  1193. }
  1194. return hres;
  1195. }
  1196. /*----------------------------------------------------------
  1197. Purpose: Create a reclist with everything in it.
  1198. Returns: standard result
  1199. Cond: Caller must destroy the reclist
  1200. */
  1201. HRESULT PUBLIC Sync_CreateCompleteRecList(
  1202. HBRFCASE hbrf,
  1203. PABORTEVT pabortevt,
  1204. PRECLIST * pprl)
  1205. {
  1206. HRESULT hres = E_OUTOFMEMORY;
  1207. HTWINLIST htl;
  1208. ASSERT(pprl);
  1209. *pprl = NULL;
  1210. if (TR_SUCCESS == Sync_CreateTwinList(hbrf, &htl))
  1211. {
  1212. Sync_AddAllToTwinList(htl);
  1213. hres = Sync_CreateRecListEx(htl, pabortevt, pprl);
  1214. Sync_DestroyTwinList(htl);
  1215. }
  1216. return hres;
  1217. }
  1218. /*----------------------------------------------------------
  1219. Purpose: Add a twin to the twinlist given the pathname. If the
  1220. pathname is not a twin, we don't add it.
  1221. Returns: TRUE on success, even when this isn't a twin
  1222. Cond: Caller must destroy the folder list if lplpftl is not NULL
  1223. */
  1224. BOOL PUBLIC Sync_AddPathToTwinList(
  1225. HBRFCASE hbrf,
  1226. HTWINLIST htl,
  1227. LPCTSTR lpcszPath, // Path
  1228. PFOLDERTWINLIST * lplpftl) // May be NULL
  1229. {
  1230. BOOL bRet = FALSE;
  1231. ASSERT(lpcszPath);
  1232. ASSERT(htl);
  1233. if (lplpftl)
  1234. *lplpftl = NULL;
  1235. if (lpcszPath)
  1236. {
  1237. if (PathIsDirectory(lpcszPath))
  1238. {
  1239. BOOL fIsTwin = FALSE;
  1240. PFOLDERTWINLIST lpftl;
  1241. // We only want to return false if we couldn't mark something
  1242. // that should have been marked. If this isn't a twin,
  1243. // we still succeed.
  1244. bRet = TRUE;
  1245. Sync_IsFolder(hbrf, lpcszPath, &fIsTwin);
  1246. if (fIsTwin) // Is this actually twinned?
  1247. {
  1248. // This is a folder twin. Add to reclist "the folder way".
  1249. //
  1250. if (Sync_CreateFolderList(hbrf, lpcszPath, &lpftl) != TR_SUCCESS)
  1251. bRet = FALSE;
  1252. else
  1253. {
  1254. PCFOLDERTWIN lpcfolder;
  1255. ASSERT(lpftl->pcftFirst);
  1256. // only mark the ones that aren't in other briefcases
  1257. //
  1258. lpcfolder = lpftl->pcftFirst;
  1259. while (lpcfolder)
  1260. {
  1261. Sync_AddToTwinList(htl, lpcfolder->hftOther);
  1262. lpcfolder = lpcfolder->pcftNext;
  1263. }
  1264. if (lplpftl)
  1265. *lplpftl = lpftl;
  1266. else
  1267. Sync_DestroyFolderList(lpftl);
  1268. }
  1269. }
  1270. }
  1271. else
  1272. {
  1273. HOBJECTTWIN hot = NULL;
  1274. TCHAR szDir[MAX_PATH];
  1275. // Add the twins to the reclist "the object way"
  1276. //
  1277. lstrcpy(szDir, lpcszPath);
  1278. PathRemoveFileSpec(szDir);
  1279. Sync_GetObject(hbrf, szDir, PathFindFileName(lpcszPath), &hot);
  1280. if (hot) // Is this actually a twin?
  1281. {
  1282. // yep
  1283. Sync_AddToTwinList(htl, hot);
  1284. Sync_ReleaseTwin(hot);
  1285. }
  1286. if (lplpftl)
  1287. *lplpftl = NULL;
  1288. bRet = TRUE;
  1289. }
  1290. }
  1291. return bRet;
  1292. }
  1293. /*----------------------------------------------------------
  1294. Purpose: Asks the user to confirm splitting one or more files.
  1295. Returns: IDYES or IDNO
  1296. Cond: --
  1297. */
  1298. int PRIVATE ConfirmSplit(
  1299. HWND hwndOwner,
  1300. LPCTSTR pszPath,
  1301. UINT cFiles)
  1302. {
  1303. int idRet;
  1304. ASSERT(pszPath);
  1305. ASSERT(1 <= cFiles);
  1306. // Multiple files?
  1307. if (1 < cFiles)
  1308. {
  1309. // Yes
  1310. idRet = MsgBox(hwndOwner,
  1311. MAKEINTRESOURCE(IDS_MSG_ConfirmMultiSplit),
  1312. MAKEINTRESOURCE(IDS_CAP_ConfirmMultiSplit),
  1313. LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_MULT)),
  1314. MB_QUESTION,
  1315. cFiles);
  1316. }
  1317. else
  1318. {
  1319. // No
  1320. UINT ids;
  1321. UINT idi;
  1322. TCHAR szName[MAX_PATH];
  1323. if (PathIsDirectory(pszPath))
  1324. {
  1325. ids = IDS_MSG_ConfirmFolderSplit;
  1326. idi = IDI_SPLIT_FOLDER;
  1327. }
  1328. else
  1329. {
  1330. ids = IDS_MSG_ConfirmFileSplit;
  1331. idi = IDI_SPLIT_FILE;
  1332. }
  1333. idRet = MsgBox(hwndOwner,
  1334. MAKEINTRESOURCE(ids),
  1335. MAKEINTRESOURCE(IDS_CAP_ConfirmSplit),
  1336. LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
  1337. MB_QUESTION,
  1338. PathGetDisplayName(pszPath, szName));
  1339. }
  1340. return idRet;
  1341. }
  1342. /*----------------------------------------------------------
  1343. Purpose: Splits a path from its sync copy. Private function
  1344. called by Sync_Split.
  1345. Returns: standard result
  1346. S_OK if it is split
  1347. Cond: --
  1348. */
  1349. HRESULT PRIVATE SplitPath(
  1350. HBRFCASE hbrf,
  1351. LPCTSTR pszPath,
  1352. HWND hwndOwner,
  1353. UINT uFlags) // SF_* flags
  1354. {
  1355. HRESULT hres;
  1356. TWINRESULT tr;
  1357. TCHAR sz[MAX_PATH];
  1358. if (pszPath)
  1359. {
  1360. // Is the object a folder?
  1361. if (IsFlagSet(uFlags, SF_ISFOLDER) ||
  1362. PathIsDirectory(pszPath))
  1363. {
  1364. // Yup
  1365. BOOL bIsTwin;
  1366. if (IsFlagSet(uFlags, SF_ISTWIN)) // Optimization
  1367. {
  1368. tr = TR_SUCCESS;
  1369. bIsTwin = TRUE;
  1370. }
  1371. else if (IsFlagSet(uFlags, SF_ISNOTTWIN)) // Optimization
  1372. {
  1373. tr = TR_SUCCESS;
  1374. bIsTwin = FALSE;
  1375. }
  1376. else
  1377. {
  1378. tr = Sync_IsFolder(hbrf, pszPath, &bIsTwin);
  1379. }
  1380. // Is this folder a twin?
  1381. if (TR_SUCCESS == tr)
  1382. {
  1383. if (bIsTwin)
  1384. {
  1385. // Yes; delete all the twin handles associated with it
  1386. PFOLDERTWINLIST lpftl;
  1387. tr = Sync_CreateFolderList(hbrf, pszPath, &lpftl);
  1388. if (TR_SUCCESS == tr)
  1389. {
  1390. PCFOLDERTWIN lpcfolder;
  1391. ASSERT(lpftl);
  1392. for (lpcfolder = lpftl->pcftFirst; lpcfolder;
  1393. lpcfolder = lpcfolder->pcftNext)
  1394. {
  1395. Sync_DeleteTwin(lpcfolder->hftOther);
  1396. }
  1397. Sync_DestroyFolderList(lpftl);
  1398. if (IsFlagClear(uFlags, SF_QUIET))
  1399. {
  1400. // Send a notification so it is redrawn.
  1401. PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE);
  1402. }
  1403. hres = NOERROR;
  1404. }
  1405. }
  1406. else if (IsFlagClear(uFlags, SF_QUIET))
  1407. {
  1408. // No
  1409. MsgBox(hwndOwner,
  1410. MAKEINTRESOURCE(IDS_MSG_FolderAlreadyOrphan),
  1411. MAKEINTRESOURCE(IDS_CAP_Split),
  1412. LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FOLDER)),
  1413. MB_INFO,
  1414. PathGetDisplayName(pszPath, sz));
  1415. hres = S_FALSE;
  1416. }
  1417. else
  1418. {
  1419. hres = S_FALSE;
  1420. }
  1421. }
  1422. }
  1423. else
  1424. {
  1425. // No; it is a file
  1426. HOBJECTTWIN hot;
  1427. ULONG ulc;
  1428. lstrcpy(sz, pszPath);
  1429. PathRemoveFileSpec(sz);
  1430. // Is this file a twin?
  1431. // (We need the twin handle below, so we cannot take
  1432. // advantage of SF_ISTWIN or SF_ISNOTTWIN.)
  1433. tr = Sync_GetObject(hbrf, sz, PathFindFileName(pszPath), &hot);
  1434. if (TR_SUCCESS == tr)
  1435. {
  1436. if (hot)
  1437. {
  1438. // Yes; is this inside a folder twin?
  1439. // (If we remove this check, the engine needs to be able
  1440. // to exclude specific files from a folder twin.)
  1441. tr = Sync_CountSourceFolders(hot, &ulc);
  1442. if (TR_SUCCESS == tr)
  1443. {
  1444. if (0 < ulc)
  1445. {
  1446. // Yes; can't do it
  1447. if (IsFlagClear(uFlags, SF_QUIET))
  1448. {
  1449. UINT rgids[2] = { IDS_ERR_1_CantSplit, IDS_ERR_2_CantSplit };
  1450. LPTSTR psz;
  1451. if (FmtString(&psz, IDS_ERR_F_CantSplit, rgids, ARRAYSIZE(rgids)))
  1452. {
  1453. // This object twin belongs to a folder twin. We can't
  1454. // allow this action.
  1455. MsgBox(hwndOwner, psz, MAKEINTRESOURCE(IDS_CAP_STATUS),
  1456. LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FILE)), MB_ERROR);
  1457. GFree(psz);
  1458. }
  1459. }
  1460. hres = S_FALSE;
  1461. }
  1462. else
  1463. {
  1464. // No; delete the twin
  1465. Sync_DeleteTwin(hot);
  1466. if (IsFlagClear(uFlags, SF_QUIET))
  1467. {
  1468. // Send a notification so it's redrawn immediately.
  1469. PathNotifyShell(pszPath, NSE_UPDATEITEM, FALSE);
  1470. }
  1471. hres = NOERROR;
  1472. }
  1473. }
  1474. Sync_ReleaseTwin(hot);
  1475. }
  1476. else if (IsFlagClear(uFlags, SF_QUIET))
  1477. {
  1478. // No
  1479. MsgBox(hwndOwner,
  1480. MAKEINTRESOURCE(IDS_MSG_FileAlreadyOrphan),
  1481. MAKEINTRESOURCE(IDS_CAP_Split),
  1482. LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_SPLIT_FILE)),
  1483. MB_INFO,
  1484. PathGetDisplayName(pszPath, sz));
  1485. hres = S_FALSE;
  1486. }
  1487. }
  1488. }
  1489. if (TR_SUCCESS != tr)
  1490. hres = HRESULT_FROM_TR(tr);
  1491. }
  1492. else
  1493. hres = S_FALSE;
  1494. return hres;
  1495. }
  1496. /*----------------------------------------------------------
  1497. Purpose: Deletes a series of twins from the engine database.
  1498. The user is optionally asked to confirm the action.
  1499. If a file is an orphan, the user is optionally
  1500. notified. The user is also optionally notified
  1501. of any errors.
  1502. Returns: standard result
  1503. S_OK if anything was deleted
  1504. Cond: --
  1505. */
  1506. HRESULT PUBLIC Sync_Split(
  1507. HBRFCASE hbrf,
  1508. LPCTSTR pszList,
  1509. UINT cFiles,
  1510. HWND hwndOwner,
  1511. UINT uFlags)
  1512. {
  1513. HRESULT hres;
  1514. UINT id;
  1515. TCHAR szCanon[MAX_PATH];
  1516. TCHAR sz[MAX_PATH];
  1517. ASSERT(pszList);
  1518. ASSERT(0 < cFiles);
  1519. // Special precondition: is it a single file?
  1520. if (1 == cFiles)
  1521. {
  1522. // Yes; is it a twin?
  1523. BrfPathCanonicalize(pszList, szCanon);
  1524. hres = Sync_IsTwin(hbrf, szCanon, uFlags);
  1525. if (S_FALSE == hres)
  1526. {
  1527. // No; tell the user. Don't bother confirming the action first.
  1528. if (IsFlagClear(uFlags, SF_QUIET))
  1529. {
  1530. UINT ids;
  1531. UINT idi;
  1532. if (IsFlagSet(uFlags, SF_ISFOLDER) ||
  1533. PathIsDirectory(szCanon))
  1534. {
  1535. ids = IDS_MSG_FolderAlreadyOrphan;
  1536. idi = IDI_SPLIT_FOLDER;
  1537. }
  1538. else
  1539. {
  1540. ids = IDS_MSG_FileAlreadyOrphan;
  1541. idi = IDI_SPLIT_FILE;
  1542. }
  1543. MsgBox(hwndOwner,
  1544. MAKEINTRESOURCE(ids),
  1545. MAKEINTRESOURCE(IDS_CAP_Split),
  1546. LoadIcon(g_hinst, MAKEINTRESOURCE(idi)),
  1547. MB_INFO,
  1548. PathGetDisplayName(szCanon, sz));
  1549. }
  1550. }
  1551. else if (S_OK == hres)
  1552. {
  1553. // Yes
  1554. if (IsFlagClear(uFlags, SF_NOCONFIRM))
  1555. id = ConfirmSplit(hwndOwner, szCanon, 1);
  1556. else
  1557. id = IDYES;
  1558. if (IDYES == id)
  1559. {
  1560. hres = SplitPath(hbrf, szCanon, hwndOwner, uFlags);
  1561. if (IsFlagClear(uFlags, SF_QUIET))
  1562. {
  1563. SHChangeNotifyHandleEvents();
  1564. }
  1565. }
  1566. else
  1567. hres = S_FALSE;
  1568. }
  1569. }
  1570. // Multiselection: ask the user first
  1571. else
  1572. {
  1573. if (IsFlagClear(uFlags, SF_NOCONFIRM))
  1574. id = ConfirmSplit(hwndOwner, pszList, cFiles);
  1575. else
  1576. id = IDYES;
  1577. if (IDYES == id)
  1578. {
  1579. // Remove all the files from the engine database
  1580. LPCTSTR psz;
  1581. UINT i;
  1582. HRESULT hresT;
  1583. hres = S_FALSE; // assume success but nothing done
  1584. for (i = 0, psz = pszList; i < cFiles; i++)
  1585. {
  1586. // Get dragged file/folder name
  1587. //
  1588. BrfPathCanonicalize(psz, szCanon);
  1589. hresT = SplitPath(hbrf, szCanon, hwndOwner, uFlags);
  1590. if (S_OK == hresT)
  1591. hres = S_OK; // (Don't set back to FALSE once it is TRUE)
  1592. else if (FAILED(hresT))
  1593. {
  1594. hres = hresT;
  1595. break;
  1596. }
  1597. DataObj_NextFile(psz); // Set psz to next file in list
  1598. }
  1599. if (IsFlagClear(uFlags, SF_QUIET))
  1600. {
  1601. SHChangeNotifyHandleEvents(); // (Do this after the loop)
  1602. }
  1603. }
  1604. else
  1605. hres = S_FALSE;
  1606. }
  1607. return hres;
  1608. }
  1609. /*----------------------------------------------------------
  1610. Purpose: Change the recitem action and the two recnodes of
  1611. importance to the specified action.
  1612. Returns: --
  1613. Cond: --
  1614. */
  1615. void PUBLIC Sync_ChangeRecItemAction(
  1616. PRECITEM pri,
  1617. LPCTSTR pszBrfPath,
  1618. LPCTSTR pszInsideDir, // Folder inside the briefcase
  1619. UINT riaction) // One of RAIA_* values to change to
  1620. {
  1621. HRESULT hres;
  1622. PRECNODE prnInside;
  1623. PRECNODE prnOutside;
  1624. // Determine which node is inside the briefcase and which one is
  1625. // outside.
  1626. //
  1627. hres = Sync_GetNodePair(pri, pszBrfPath, pszInsideDir, &prnInside, &prnOutside);
  1628. if (SUCCEEDED(hres))
  1629. {
  1630. ASSERT(prnInside);
  1631. ASSERT(prnOutside);
  1632. switch(riaction)
  1633. {
  1634. case RAIA_TOIN:
  1635. pri->dwUser = RIU_CHANGED;
  1636. pri->riaction = RIA_COPY;
  1637. prnInside->rnaction = RNA_COPY_TO_ME;
  1638. prnOutside->rnaction = RNA_COPY_FROM_ME;
  1639. break;
  1640. case RAIA_TOOUT:
  1641. pri->dwUser = RIU_CHANGED;
  1642. pri->riaction = RIA_COPY;
  1643. prnInside->rnaction = RNA_COPY_FROM_ME;
  1644. prnOutside->rnaction = RNA_COPY_TO_ME;
  1645. break;
  1646. case RAIA_SKIP:
  1647. pri->dwUser = RIU_SKIP;
  1648. break;
  1649. case RAIA_MERGE:
  1650. pri->dwUser = RIU_CHANGED;
  1651. pri->riaction = RIA_MERGE;
  1652. prnInside->rnaction = RNA_MERGE_ME;
  1653. prnOutside->rnaction = RNA_MERGE_ME;
  1654. break;
  1655. #ifdef NEW_REC
  1656. case RAIA_DONTDELETE:
  1657. if (RNA_DELETE_ME == prnInside->rnaction)
  1658. {
  1659. pri->dwUser = RIU_CHANGED;
  1660. pri->riaction = RIA_NOTHING;
  1661. prnInside->rnaction = RNA_NOTHING;
  1662. }
  1663. else if (RNA_DELETE_ME == prnOutside->rnaction)
  1664. {
  1665. pri->dwUser = RIU_CHANGED;
  1666. pri->riaction = RIA_NOTHING;
  1667. prnOutside->rnaction = RNA_NOTHING;
  1668. }
  1669. break;
  1670. case RAIA_DELETEIN:
  1671. pri->dwUser = RIU_CHANGED;
  1672. pri->riaction = RIA_DELETE;
  1673. prnInside->rnaction = RNA_DELETE_ME;
  1674. prnOutside->rnaction = RNA_NOTHING;
  1675. break;
  1676. case RAIA_DELETEOUT:
  1677. pri->dwUser = RIU_CHANGED;
  1678. pri->riaction = RIA_DELETE;
  1679. prnInside->rnaction = RNA_NOTHING;
  1680. prnOutside->rnaction = RNA_DELETE_ME;
  1681. break;
  1682. #endif
  1683. default:
  1684. // (The other values don't make sense here)
  1685. ASSERT(0);
  1686. break;
  1687. }
  1688. }
  1689. }
  1690. ///////////////////////////////////////////////////// PRIVATE FUNCTIONS
  1691. #ifdef DEBUG
  1692. /*----------------------------------------------------------
  1693. Purpose: Dumps the contents of the given twin structure to
  1694. to debug out
  1695. Returns: --
  1696. Cond: --
  1697. */
  1698. void PUBLIC Sync_FnDump(
  1699. LPVOID lpvBuf,
  1700. UINT cbBuf)
  1701. {
  1702. int bDump;
  1703. #define szDumpTwin TEXT("Dump TWIN: ")
  1704. #define szDumpSp TEXT(" ")
  1705. ENTEREXCLUSIVE();
  1706. {
  1707. bDump = IsFlagSet(g_uDumpFlags, DF_CREATETWIN);
  1708. }
  1709. LEAVEEXCLUSIVE();
  1710. if (!bDump)
  1711. return ;
  1712. if (cbBuf == sizeof(NEWOBJECTTWIN))
  1713. {
  1714. PNEWOBJECTTWIN lpnot = (PNEWOBJECTTWIN)lpvBuf;
  1715. TRACE_MSG(TF_ALWAYS, TEXT("%s.Folder1 = {%s}\r\n%s.Folder2 = {%s}\r\n%s.Name = {%s}\r\n"),
  1716. (LPTSTR)szDumpTwin, lpnot->pcszFolder1,
  1717. (LPTSTR)szDumpSp, lpnot->pcszFolder2,
  1718. (LPTSTR)szDumpSp, lpnot->pcszName);
  1719. }
  1720. else if (cbBuf == sizeof(NEWFOLDERTWIN))
  1721. {
  1722. PNEWFOLDERTWIN lpnft = (PNEWFOLDERTWIN)lpvBuf;
  1723. TRACE_MSG(TF_ALWAYS, TEXT("%s.Folder1 = {%s}\r\n%s.Folder2 = {%s}\r\n%s.Name = {%s}\r\n%s.dwFlags = 0x%04lx\r\n"),
  1724. (LPTSTR)szDumpTwin, lpnft->pcszFolder1,
  1725. (LPTSTR)szDumpSp, lpnft->pcszFolder2,
  1726. (LPTSTR)szDumpSp, lpnft->pcszName,
  1727. (LPTSTR)szDumpSp, (DWORD)lpnft->dwFlags);
  1728. }
  1729. }
  1730. /*----------------------------------------------------------
  1731. Purpose: Return English form of RIA_ flags
  1732. Returns:
  1733. Cond: --
  1734. */
  1735. LPTSTR PRIVATE LpszFromItemAction(
  1736. ULONG riaction)
  1737. {
  1738. switch (riaction)
  1739. {
  1740. DEBUG_CASE_STRING( RIA_NOTHING );
  1741. DEBUG_CASE_STRING( RIA_COPY );
  1742. DEBUG_CASE_STRING( RIA_MERGE );
  1743. DEBUG_CASE_STRING( RIA_BROKEN_MERGE );
  1744. #ifdef NEW_REC
  1745. DEBUG_CASE_STRING( RIA_DELETE );
  1746. #endif
  1747. default: return TEXT("RIA unknown");
  1748. }
  1749. }
  1750. /*----------------------------------------------------------
  1751. Purpose: Return English form of RNA_ flags
  1752. Returns:
  1753. Cond: --
  1754. */
  1755. LPTSTR PRIVATE LpszFromNodeAction(
  1756. ULONG rnaction)
  1757. {
  1758. switch (rnaction)
  1759. {
  1760. DEBUG_CASE_STRING( RNA_NOTHING );
  1761. DEBUG_CASE_STRING( RNA_COPY_TO_ME );
  1762. DEBUG_CASE_STRING( RNA_COPY_FROM_ME );
  1763. DEBUG_CASE_STRING( RNA_MERGE_ME );
  1764. #ifdef NEW_REC
  1765. DEBUG_CASE_STRING( RNA_DELETE_ME );
  1766. #endif
  1767. default: return TEXT("RNA unknown");
  1768. }
  1769. }
  1770. /*----------------------------------------------------------
  1771. Purpose: Return English form of RNS_ flags
  1772. Returns:
  1773. Cond: --
  1774. */
  1775. LPTSTR PRIVATE LpszFromNodeState(
  1776. ULONG rnstate)
  1777. {
  1778. switch (rnstate)
  1779. {
  1780. #ifdef NEW_REC
  1781. DEBUG_CASE_STRING( RNS_NEVER_RECONCILED );
  1782. #endif
  1783. DEBUG_CASE_STRING( RNS_UNAVAILABLE );
  1784. DEBUG_CASE_STRING( RNS_DOES_NOT_EXIST );
  1785. DEBUG_CASE_STRING( RNS_DELETED );
  1786. DEBUG_CASE_STRING( RNS_NOT_RECONCILED );
  1787. DEBUG_CASE_STRING( RNS_UP_TO_DATE );
  1788. DEBUG_CASE_STRING( RNS_CHANGED );
  1789. default: return TEXT("RNS unknown");
  1790. }
  1791. }
  1792. /*----------------------------------------------------------
  1793. Purpose: Dump the RECNODE
  1794. Returns:
  1795. Cond: --
  1796. */
  1797. void PUBLIC Sync_DumpRecNode(
  1798. TWINRESULT tr,
  1799. PRECNODE lprn)
  1800. {
  1801. BOOL bDump;
  1802. TCHAR szBuf[MAXMSGLEN];
  1803. #define szDumpLabel TEXT("\tDump RECNODE: ")
  1804. #define szDumpMargin TEXT("\t ")
  1805. ENTEREXCLUSIVE();
  1806. {
  1807. bDump = IsFlagSet(g_uDumpFlags, DF_RECNODE);
  1808. }
  1809. LEAVEEXCLUSIVE();
  1810. if (!bDump || lprn == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER)
  1811. return ;
  1812. wsprintf(szBuf, TEXT("%s.Folder = {%s}\r\n"), (LPTSTR)szDumpLabel, lprn->pcszFolder);
  1813. OutputDebugString(szBuf);
  1814. wsprintf(szBuf, TEXT("%s.hObjectTwin = %lx\r\n"), (LPTSTR)szDumpMargin, lprn->hObjectTwin);
  1815. OutputDebugString(szBuf);
  1816. wsprintf(szBuf, TEXT("%s.rnstate = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromNodeState(lprn->rnstate));
  1817. OutputDebugString(szBuf);
  1818. wsprintf(szBuf, TEXT("%s.rnaction = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromNodeAction(lprn->rnaction));
  1819. OutputDebugString(szBuf);
  1820. OutputDebugString(TEXT("\r\n"));
  1821. #undef szDumpLabel
  1822. #undef szDumpMargin
  1823. }
  1824. /*----------------------------------------------------------
  1825. Purpose: Dump the RECITEM
  1826. Returns:
  1827. Cond: --
  1828. */
  1829. void PUBLIC Sync_DumpRecItem(
  1830. TWINRESULT tr,
  1831. PRECITEM lpri,
  1832. LPCTSTR pszMsg)
  1833. {
  1834. BOOL bDump;
  1835. PRECNODE lprn;
  1836. TCHAR szBuf[MAXMSGLEN];
  1837. #define szDumpLabel TEXT("Dump RECITEM: ")
  1838. #define szDumpMargin TEXT(" ")
  1839. ENTEREXCLUSIVE();
  1840. {
  1841. bDump = IsFlagSet(g_uDumpFlags, DF_RECITEM);
  1842. }
  1843. LEAVEEXCLUSIVE();
  1844. if (!bDump || lpri == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER)
  1845. return ;
  1846. if (pszMsg)
  1847. TRACE_MSG(TF_ALWAYS, pszMsg);
  1848. wsprintf(szBuf, TEXT("tr = %s\r\n"), (LPTSTR)SzFromTR(tr));
  1849. OutputDebugString(szBuf);
  1850. wsprintf(szBuf, TEXT("%s.Name = {%s}\r\n"), (LPTSTR)szDumpLabel, lpri->pcszName);
  1851. OutputDebugString(szBuf);
  1852. wsprintf(szBuf, TEXT("%s.hTwinFamily = %lx\r\n"), (LPTSTR)szDumpMargin, lpri->hTwinFamily);
  1853. OutputDebugString(szBuf);
  1854. wsprintf(szBuf, TEXT("%s.ulcNodes = %lu\r\n"), (LPTSTR)szDumpMargin, lpri->ulcNodes);
  1855. OutputDebugString(szBuf);
  1856. wsprintf(szBuf, TEXT("%s.riaction = %s\r\n"), (LPTSTR)szDumpMargin, LpszFromItemAction(lpri->riaction));
  1857. OutputDebugString(szBuf);
  1858. lprn = lpri->prnFirst;
  1859. while (lprn)
  1860. {
  1861. Sync_DumpRecNode(tr, lprn);
  1862. lprn = lprn->prnNext;
  1863. }
  1864. #undef szDumpLabel
  1865. #undef szDumpMargin
  1866. }
  1867. /*----------------------------------------------------------
  1868. Purpose: Dump the RECLIST
  1869. Returns:
  1870. Cond: --
  1871. */
  1872. void PUBLIC Sync_DumpRecList(
  1873. TWINRESULT tr,
  1874. PRECLIST lprl,
  1875. LPCTSTR pszMsg)
  1876. {
  1877. BOOL bDump;
  1878. PRECITEM lpri;
  1879. TCHAR szBuf[MAXMSGLEN];
  1880. #define szDumpLabel TEXT("Dump RECLIST: ")
  1881. ENTEREXCLUSIVE();
  1882. {
  1883. bDump = IsFlagSet(g_uDumpFlags, DF_RECLIST);
  1884. }
  1885. LEAVEEXCLUSIVE();
  1886. if (!bDump)
  1887. return ;
  1888. if (pszMsg)
  1889. TRACE_MSG(TF_ALWAYS, pszMsg);
  1890. // Note we only dump on TR_SUCCESS
  1891. //
  1892. wsprintf(szBuf, TEXT("tr = %s\r\n"), (LPTSTR)SzFromTR(tr));
  1893. OutputDebugString(szBuf);
  1894. if (lprl == NULL || tr == TR_OUT_OF_MEMORY || tr == TR_INVALID_PARAMETER)
  1895. return ;
  1896. wsprintf(szBuf, TEXT("%s.ulcItems = %lu\r\n"), (LPTSTR)szDumpLabel, lprl->ulcItems);
  1897. OutputDebugString(szBuf);
  1898. lpri = lprl->priFirst;
  1899. while (lpri)
  1900. {
  1901. Sync_DumpRecItem(TR_SUCCESS, lpri, NULL);
  1902. lpri = lpri->priNext;
  1903. }
  1904. #undef szDumpLabel
  1905. }
  1906. /*----------------------------------------------------------
  1907. Purpose: Dump the FOLDERTWIN
  1908. Returns: --
  1909. Cond: --
  1910. */
  1911. void PUBLIC Sync_DumpFolderTwin(
  1912. PCFOLDERTWIN pft)
  1913. {
  1914. BOOL bDump;
  1915. TCHAR szBuf[MAXMSGLEN];
  1916. #define szDumpLabel TEXT("Dump FOLDERTWIN: ")
  1917. #define szDumpMargin TEXT(" ")
  1918. ENTEREXCLUSIVE();
  1919. {
  1920. bDump = IsFlagSet(g_uDumpFlags, DF_FOLDERTWIN);
  1921. }
  1922. LEAVEEXCLUSIVE();
  1923. if (!bDump || pft == NULL)
  1924. return ;
  1925. wsprintf(szBuf, TEXT("%s.Name = {%s}\r\n"), (LPTSTR)szDumpLabel, pft->pcszName);
  1926. OutputDebugString(szBuf);
  1927. wsprintf(szBuf, TEXT("%s.pszSrcFolder = {%s}\r\n"), (LPTSTR)szDumpMargin, pft->pcszSrcFolder);
  1928. OutputDebugString(szBuf);
  1929. wsprintf(szBuf, TEXT("%s.pszOtherFolder = {%s}\r\n"), (LPTSTR)szDumpMargin, pft->pcszOtherFolder);
  1930. OutputDebugString(szBuf);
  1931. wsprintf(szBuf, TEXT("%s.dwFlags = %lx\r\n"), (LPTSTR)szDumpMargin, pft->dwFlags);
  1932. OutputDebugString(szBuf);
  1933. wsprintf(szBuf, TEXT("%s.dwUser = %lx\r\n"), (LPTSTR)szDumpMargin, pft->dwUser);
  1934. OutputDebugString(szBuf);
  1935. #undef szDumpLabel
  1936. #undef szDumpMargin
  1937. }
  1938. /*----------------------------------------------------------
  1939. Purpose: Dump the FOLDERTWINLIST
  1940. Returns: --
  1941. Cond: --
  1942. */
  1943. void PUBLIC Sync_DumpFolderTwinList(
  1944. PFOLDERTWINLIST pftl,
  1945. LPCTSTR pszMsg)
  1946. {
  1947. BOOL bDump;
  1948. PCFOLDERTWIN pft;
  1949. TCHAR szBuf[MAXMSGLEN];
  1950. #define szDumpLabel TEXT("Dump FOLDERTWINLIST: ")
  1951. ENTEREXCLUSIVE();
  1952. {
  1953. bDump = IsFlagSet(g_uDumpFlags, DF_FOLDERTWIN);
  1954. }
  1955. LEAVEEXCLUSIVE();
  1956. if (!bDump)
  1957. return ;
  1958. if (pszMsg)
  1959. TRACE_MSG(TF_ALWAYS, pszMsg);
  1960. if (pftl == NULL)
  1961. return ;
  1962. wsprintf(szBuf, TEXT("%s.ulcItems = %lu\r\n"), (LPTSTR)szDumpLabel, pftl->ulcItems);
  1963. OutputDebugString(szBuf);
  1964. for (pft = pftl->pcftFirst; pft; pft = pft->pcftNext)
  1965. {
  1966. Sync_DumpFolderTwin(pft);
  1967. }
  1968. #undef szDumpLabel
  1969. }
  1970. #endif