Leaked source code of windows server 2003
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.

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