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.

1590 lines
39 KiB

  1. // Dynamic Array APIs
  2. #include "ctlspriv.h"
  3. //
  4. // Heapsort is a bit slower, but it doesn't use any stack or memory...
  5. // Mergesort takes a bit of memory (O(n)) and stack (O(log(n)), but very fast...
  6. //
  7. #ifdef WIN32
  8. #define MERGESORT
  9. #else
  10. #define USEHEAPSORT
  11. #endif
  12. #ifdef DEBUG
  13. #define DSA_MAGIC ('S' | ('A' << 8))
  14. #define IsDSA(pdsa) ((pdsa) && (pdsa)->magic == DSA_MAGIC)
  15. #define DPA_MAGIC ('P' | ('A' << 8))
  16. #define IsDPA(pdpa) ((pdpa) && (pdpa)->magic == DPA_MAGIC)
  17. #else
  18. #define IsDSA(pdsa)
  19. #define IsDPA(pdsa)
  20. #endif
  21. typedef struct {
  22. void FAR* FAR* pp;
  23. PFNDPACOMPARE pfnCmp;
  24. LPARAM lParam;
  25. int cp;
  26. #ifdef MERGESORT
  27. void FAR* FAR* ppT;
  28. #endif
  29. } SORTPARAMS;
  30. BOOL NEAR DPA_QuickSort(SORTPARAMS FAR* psp);
  31. BOOL NEAR DPA_QuickSort2(int i, int j, SORTPARAMS FAR* psp);
  32. BOOL NEAR DPA_HeapSort(SORTPARAMS FAR* psp);
  33. void NEAR DPA_HeapSortPushDown(int first, int last, SORTPARAMS FAR* psp);
  34. BOOL NEAR DPA_MergeSort(SORTPARAMS FAR* psp);
  35. void NEAR DPA_MergeSort2(SORTPARAMS FAR* psp, int iFirst, int cItems);
  36. //========== Dynamic structure array ====================================
  37. // Dynamic structure array
  38. typedef struct _DSA {
  39. // NOTE: The following field MUST be defined at the beginning of the
  40. // structure in order for GetItemCount() to work.
  41. //
  42. int cItem; // # of elements in dsa
  43. void FAR* aItem; // memory for elements
  44. int cItemAlloc; // # items which fit in aItem
  45. int cbItem; // size of each item
  46. int cItemGrow; // # items to grow cItemAlloc by
  47. #ifdef DEBUG
  48. UINT magic;
  49. #endif
  50. } DSA;
  51. #define DSA_PITEM(pdsa, index) ((void FAR*)(((BYTE FAR*)(pdsa)->aItem) + ((index) * (pdsa)->cbItem)))
  52. #ifdef DEBUG
  53. #define BF_ONDAVALIDATE 0x00001000
  54. void DABreakFn(void)
  55. {
  56. if (IsFlagSet(g_dwBreakFlags, BF_ONDAVALIDATE))
  57. ASSERT(0);
  58. }
  59. #define DABreak() DABreakFn()
  60. #else
  61. #define DABreak()
  62. #endif
  63. HDSA WINAPI DSA_Create(int cbItem, int cItemGrow)
  64. {
  65. HDSA pdsa = Alloc(sizeof(DSA));
  66. ASSERT(cbItem);
  67. if (pdsa)
  68. {
  69. ASSERT(pdsa->cItem == 0);
  70. ASSERT(pdsa->cItemAlloc == 0);
  71. pdsa->cbItem = cbItem;
  72. pdsa->cItemGrow = (cItemGrow == 0 ? 1 : cItemGrow);
  73. ASSERT(pdsa->aItem == NULL);
  74. #ifdef DEBUG
  75. pdsa->magic = DSA_MAGIC;
  76. #endif
  77. }
  78. return pdsa;
  79. }
  80. BOOL WINAPI DSA_Destroy(HDSA pdsa)
  81. {
  82. if (pdsa == NULL) // allow NULL for low memory cases
  83. return TRUE;
  84. // Components rely on not having to check for NULL
  85. ASSERT(IsDSA(pdsa));
  86. #ifdef DEBUG
  87. pdsa->cItem = 0;
  88. pdsa->cItemAlloc = 0;
  89. pdsa->cbItem = 0;
  90. pdsa->magic = 0;
  91. #endif
  92. if (pdsa->aItem && !Free(pdsa->aItem))
  93. return FALSE;
  94. return Free(pdsa);
  95. }
  96. void WINAPI DSA_EnumCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, LPVOID pData)
  97. {
  98. int i;
  99. if (!pdsa)
  100. return;
  101. ASSERT(IsDSA(pdsa));
  102. for (i = 0; i < pdsa->cItem; i++) {
  103. if (!pfnCB(DSA_GetItemPtr(pdsa, i), pData))
  104. break;
  105. }
  106. }
  107. void WINAPI DSA_DestroyCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, LPVOID pData)
  108. {
  109. DSA_EnumCallback(pdsa, pfnCB, pData);
  110. DSA_Destroy(pdsa);
  111. }
  112. BOOL WINAPI DSA_GetItem(HDSA pdsa, int index, void FAR* pitem)
  113. {
  114. ASSERT(IsDSA(pdsa));
  115. ASSERT(pitem);
  116. if (index < 0 || index >= pdsa->cItem)
  117. {
  118. #ifdef DEBUG
  119. // Don't assert if index == pdsa->cItems as some clients simply want to walk the list and no need to call getcount...
  120. if (index != pdsa->cItem)
  121. {
  122. DebugMsg(DM_ERROR, TEXT("DSA: GetItem: Invalid index: %d"), index);
  123. DABreak();
  124. }
  125. #endif
  126. return FALSE;
  127. }
  128. hmemcpy(pitem, DSA_PITEM(pdsa, index), pdsa->cbItem);
  129. return TRUE;
  130. }
  131. void FAR* WINAPI DSA_GetItemPtr(HDSA pdsa, int index)
  132. {
  133. ASSERT(IsDSA(pdsa));
  134. if (index < 0 || index >= pdsa->cItem)
  135. {
  136. DebugMsg(DM_ERROR, TEXT("DSA: GetItemPtr: Invalid index: %d"), index);
  137. // DABreak(); // caller knows
  138. return NULL;
  139. }
  140. return DSA_PITEM(pdsa, index);
  141. }
  142. BOOL WINAPI DSA_SetItem(HDSA pdsa, int index, void FAR* pitem)
  143. {
  144. ASSERT(pitem);
  145. ASSERT(IsDSA(pdsa));
  146. if (index < 0)
  147. {
  148. DebugMsg(DM_ERROR, TEXT("DSA: SetItem: Invalid index: %d"), index);
  149. DABreak();
  150. return FALSE;
  151. }
  152. if (index >= pdsa->cItem)
  153. {
  154. if (index + 1 > pdsa->cItemAlloc)
  155. {
  156. int cItemAlloc = (((index + 1) + pdsa->cItemGrow - 1) / pdsa->cItemGrow) * pdsa->cItemGrow;
  157. void FAR* aItemNew = ReAlloc(pdsa->aItem, cItemAlloc * pdsa->cbItem);
  158. if (!aItemNew)
  159. return FALSE;
  160. pdsa->aItem = aItemNew;
  161. pdsa->cItemAlloc = cItemAlloc;
  162. }
  163. pdsa->cItem = index + 1;
  164. }
  165. hmemcpy(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
  166. return TRUE;
  167. }
  168. int WINAPI DSA_InsertItem(HDSA pdsa, int index, void FAR* pitem)
  169. {
  170. ASSERT(pitem);
  171. ASSERT(IsDSA(pdsa));
  172. if (index < 0)
  173. {
  174. DebugMsg(DM_ERROR, TEXT("DSA: InsertItem: Invalid index: %d"), index);
  175. DABreak();
  176. return -1;
  177. }
  178. if (index > pdsa->cItem)
  179. index = pdsa->cItem;
  180. if (pdsa->cItem + 1 > pdsa->cItemAlloc)
  181. {
  182. void FAR* aItemNew = ReAlloc(pdsa->aItem,
  183. (pdsa->cItemAlloc + pdsa->cItemGrow) * pdsa->cbItem);
  184. if (!aItemNew)
  185. return -1;
  186. pdsa->aItem = aItemNew;
  187. pdsa->cItemAlloc += pdsa->cItemGrow;
  188. }
  189. if (index < pdsa->cItem)
  190. {
  191. hmemcpy(DSA_PITEM(pdsa, index + 1), DSA_PITEM(pdsa, index),
  192. (pdsa->cItem - index) * pdsa->cbItem);
  193. }
  194. pdsa->cItem++;
  195. hmemcpy(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
  196. return index;
  197. }
  198. BOOL WINAPI DSA_DeleteItem(HDSA pdsa, int index)
  199. {
  200. ASSERT(IsDSA(pdsa));
  201. if (index < 0 || index >= pdsa->cItem)
  202. {
  203. DebugMsg(DM_ERROR, TEXT("DSA: DeleteItem: Invalid index: %d"), index);
  204. DABreak();
  205. return FALSE;
  206. }
  207. if (index < pdsa->cItem - 1)
  208. {
  209. hmemcpy(DSA_PITEM(pdsa, index), DSA_PITEM(pdsa, index + 1),
  210. (pdsa->cItem - (index + 1)) * pdsa->cbItem);
  211. }
  212. pdsa->cItem--;
  213. if (pdsa->cItemAlloc - pdsa->cItem > pdsa->cItemGrow)
  214. {
  215. void FAR* aItemNew = ReAlloc(pdsa->aItem,
  216. (pdsa->cItemAlloc - pdsa->cItemGrow) * pdsa->cbItem);
  217. if (aItemNew)
  218. pdsa->aItem = aItemNew;
  219. else
  220. {
  221. // If the shrink fails, then just continue with the old (slightly
  222. // too big) allocation. Go ahead and let cItemAlloc decrease
  223. // so we don't keep trying to realloc smaller
  224. }
  225. pdsa->cItemAlloc -= pdsa->cItemGrow;
  226. }
  227. return TRUE;
  228. }
  229. BOOL WINAPI DSA_DeleteAllItems(HDSA pdsa)
  230. {
  231. ASSERT(IsDSA(pdsa));
  232. if (pdsa->aItem && !Free(pdsa->aItem))
  233. return FALSE;
  234. pdsa->aItem = NULL;
  235. pdsa->cItem = pdsa->cItemAlloc = 0;
  236. return TRUE;
  237. }
  238. //================== Dynamic pointer array implementation ===========
  239. typedef struct _DPA {
  240. // NOTE: The following two fields MUST be defined in this order, at
  241. // the beginning of the structure in order for the macro APIs to work.
  242. //
  243. int cp;
  244. void FAR* FAR* pp;
  245. HANDLE hheap; // Heap to allocate from if NULL use shared
  246. int cpAlloc;
  247. int cpGrow;
  248. #ifdef DEBUG
  249. UINT magic;
  250. #endif
  251. } DPA;
  252. HDPA WINAPI DPA_Create(int cpGrow)
  253. {
  254. return DPA_CreateEx(cpGrow, NULL);
  255. }
  256. // Should nuke the standard DPA above...
  257. HDPA WINAPI DPA_CreateEx(int cpGrow, HANDLE hheap)
  258. {
  259. HDPA pdpa;
  260. if (hheap == NULL)
  261. {
  262. #ifdef WIN32
  263. #ifdef WINNT
  264. hheap = GetProcessHeap();
  265. #else
  266. hheap = GetSharedHeapHandle();
  267. #endif
  268. #endif
  269. pdpa = ALLOC_NULLHEAP(hheap, sizeof(DPA));
  270. }
  271. else
  272. pdpa = ControlAlloc(hheap, sizeof(DPA));
  273. if (pdpa)
  274. {
  275. ASSERT(pdpa->cp == 0);
  276. ASSERT(pdpa->cpAlloc == 0);
  277. pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
  278. ASSERT(pdpa->pp == NULL);
  279. pdpa->hheap = hheap;
  280. #ifdef DEBUG
  281. pdpa->magic = DPA_MAGIC;
  282. #endif
  283. }
  284. return pdpa;
  285. }
  286. BOOL WINAPI DPA_Destroy(HDPA pdpa)
  287. {
  288. if (pdpa == NULL) // allow NULL for low memory cases, still assert
  289. return TRUE;
  290. ASSERT(IsDPA(pdpa));
  291. #ifndef UNIX
  292. ASSERT(pdpa->hheap);
  293. #endif
  294. #ifdef DEBUG
  295. pdpa->cp = 0;
  296. pdpa->cpAlloc = 0;
  297. pdpa->magic = 0;
  298. #endif
  299. if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
  300. return FALSE;
  301. return ControlFree(pdpa->hheap, pdpa);
  302. }
  303. HDPA WINAPI DPA_Clone(HDPA pdpa, HDPA pdpaNew)
  304. {
  305. BOOL fAlloc = FALSE;
  306. if (!pdpaNew)
  307. {
  308. pdpaNew = DPA_CreateEx(pdpa->cpGrow, pdpa->hheap);
  309. if (!pdpaNew)
  310. return NULL;
  311. fAlloc = TRUE;
  312. }
  313. if (!DPA_Grow(pdpaNew, pdpa->cpAlloc)) {
  314. if (!fAlloc)
  315. DPA_Destroy(pdpaNew);
  316. return NULL;
  317. }
  318. pdpaNew->cp = pdpa->cp;
  319. hmemcpy(pdpaNew->pp, pdpa->pp, pdpa->cp * sizeof(void FAR*));
  320. return pdpaNew;
  321. }
  322. void FAR* WINAPI DPA_GetPtr(HDPA pdpa, INT_PTR index)
  323. {
  324. ASSERT(IsDPA(pdpa));
  325. if (!pdpa || index < 0 || index >= pdpa->cp)
  326. return NULL;
  327. return pdpa->pp[index];
  328. }
  329. int WINAPI DPA_GetPtrIndex(HDPA pdpa, void FAR* p)
  330. {
  331. void FAR* FAR* pp;
  332. void FAR* FAR* ppMax;
  333. ASSERT(IsDPA(pdpa));
  334. if (pdpa && pdpa->pp)
  335. {
  336. pp = pdpa->pp;
  337. ppMax = pp + pdpa->cp;
  338. for ( ; pp < ppMax; pp++)
  339. {
  340. if (*pp == p)
  341. return (int) (pp - pdpa->pp);
  342. }
  343. }
  344. return -1;
  345. }
  346. BOOL WINAPI DPA_Grow(HDPA pdpa, int cpAlloc)
  347. {
  348. ASSERT(IsDPA(pdpa));
  349. if (!pdpa)
  350. return FALSE;
  351. if (cpAlloc > pdpa->cpAlloc)
  352. {
  353. void FAR* FAR* ppNew;
  354. cpAlloc = ((cpAlloc + pdpa->cpGrow - 1) / pdpa->cpGrow) * pdpa->cpGrow;
  355. if (pdpa->pp)
  356. ppNew = (void FAR* FAR*)ControlReAlloc(pdpa->hheap, pdpa->pp, cpAlloc * sizeof(void FAR*));
  357. else
  358. ppNew = (void FAR* FAR*)ControlAlloc(pdpa->hheap, cpAlloc * sizeof(void FAR*));
  359. if (!ppNew)
  360. return FALSE;
  361. pdpa->pp = ppNew;
  362. pdpa->cpAlloc = cpAlloc;
  363. //
  364. // Grow more agressively as we get bigger, up to a maximum of
  365. // 512 at a time. Note, we'll only hit our outer bound growth
  366. // at a time limit once we've already got that many items in the
  367. // DPA anyway...
  368. //
  369. if (pdpa->cpGrow < 256)
  370. {
  371. pdpa->cpGrow = pdpa->cpGrow << 1;
  372. }
  373. }
  374. return TRUE;
  375. }
  376. BOOL WINAPI DPA_SetPtr(HDPA pdpa, int index, void FAR* p)
  377. {
  378. ASSERT(IsDPA(pdpa));
  379. if (!pdpa)
  380. return FALSE;
  381. if (index < 0)
  382. {
  383. DebugMsg(DM_ERROR, TEXT("DPA: SetPtr: Invalid index: %d"), index);
  384. DABreak();
  385. return FALSE;
  386. }
  387. if (index >= pdpa->cp)
  388. {
  389. if (!DPA_Grow(pdpa, index + 1))
  390. return FALSE;
  391. // If we grew by more than one, must zero-init all the stuff in the middle
  392. ZeroMemory(pdpa->pp + pdpa->cp, sizeof(LPVOID) * (index - pdpa->cp));
  393. pdpa->cp = index + 1;
  394. }
  395. pdpa->pp[index] = p;
  396. return TRUE;
  397. }
  398. int WINAPI DPA_InsertPtr(HDPA pdpa, int index, void FAR* p)
  399. {
  400. ASSERT(IsDPA(pdpa));
  401. if (!pdpa)
  402. return -1;
  403. if (index < 0)
  404. {
  405. DebugMsg(DM_ERROR, TEXT("DPA: InsertPtr: Invalid index: %d"), index);
  406. DABreak();
  407. return -1;
  408. }
  409. if (index > pdpa->cp)
  410. index = pdpa->cp;
  411. // Make sure we have room for one more item
  412. //
  413. if (pdpa->cp + 1 > pdpa->cpAlloc)
  414. {
  415. if (!DPA_Grow(pdpa, pdpa->cp + 1))
  416. return -1;
  417. }
  418. // If we are inserting, we need to slide everybody up
  419. //
  420. if (index < pdpa->cp)
  421. {
  422. hmemcpy(&pdpa->pp[index + 1], &pdpa->pp[index],
  423. (pdpa->cp - index) * sizeof(void FAR*));
  424. }
  425. pdpa->pp[index] = p;
  426. pdpa->cp++;
  427. return index;
  428. }
  429. void FAR* WINAPI DPA_DeletePtr(HDPA pdpa, int index)
  430. {
  431. void FAR* p;
  432. ASSERT(IsDPA(pdpa));
  433. if (!pdpa)
  434. return FALSE;
  435. if (index < 0 || index >= pdpa->cp)
  436. {
  437. DebugMsg(DM_ERROR, TEXT("DPA: DeltePtr: Invalid index: %d"), index);
  438. DABreak();
  439. return NULL;
  440. }
  441. p = pdpa->pp[index];
  442. if (index < pdpa->cp - 1)
  443. {
  444. hmemcpy(&pdpa->pp[index], &pdpa->pp[index + 1],
  445. (pdpa->cp - (index + 1)) * sizeof(void FAR*));
  446. }
  447. pdpa->cp--;
  448. if (pdpa->cpAlloc - pdpa->cp > pdpa->cpGrow)
  449. {
  450. void FAR* FAR* ppNew;
  451. ppNew = ControlReAlloc(pdpa->hheap, pdpa->pp, (pdpa->cpAlloc - pdpa->cpGrow) * sizeof(void FAR*));
  452. if (ppNew)
  453. pdpa->pp = ppNew;
  454. else
  455. {
  456. // If the shrink fails, then just continue with the old (slightly
  457. // too big) allocation. Go ahead and let cpAlloc decrease
  458. // so we don't keep trying to realloc smaller
  459. }
  460. pdpa->cpAlloc -= pdpa->cpGrow;
  461. }
  462. return p;
  463. }
  464. BOOL WINAPI DPA_DeleteAllPtrs(HDPA pdpa)
  465. {
  466. if (!pdpa)
  467. return FALSE;
  468. ASSERT(IsDPA(pdpa));
  469. if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
  470. return FALSE;
  471. pdpa->pp = NULL;
  472. pdpa->cp = pdpa->cpAlloc = 0;
  473. return TRUE;
  474. }
  475. void WINAPI DPA_EnumCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, LPVOID pData)
  476. {
  477. int i;
  478. if (!pdpa)
  479. return;
  480. ASSERT(IsDPA(pdpa));
  481. for (i = 0; i < pdpa->cp; i++) {
  482. if (!pfnCB(DPA_FastGetPtr(pdpa, i), pData))
  483. break;
  484. }
  485. }
  486. void WINAPI DPA_DestroyCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, LPVOID pData)
  487. {
  488. DPA_EnumCallback(pdpa, pfnCB, pData);
  489. DPA_Destroy(pdpa);
  490. }
  491. typedef struct _DPASTREAMHEADER
  492. {
  493. DWORD cbSize; // Size of entire stream
  494. DWORD dwVersion; // For versioning
  495. int celem;
  496. } DPASTREAMHEADER;
  497. #define DPASTREAM_VERSION 1
  498. /*----------------------------------------------------------
  499. Purpose: Saves the DPA to a stream by writing out a header,
  500. and then calling the given callback to write each
  501. element.
  502. The callback can end the write early by returning
  503. something other than S_OK. Returning an error will
  504. cancel the entire write. Returning S_FALSE will
  505. stop the write.
  506. Returns: S_OK or S_FALSE for success.
  507. S_FALSE only if callback stops early
  508. errors
  509. */
  510. HRESULT
  511. WINAPI
  512. DPA_SaveStream(
  513. IN HDPA pdpa,
  514. IN PFNDPASTREAM pfn,
  515. IN IStream * pstm,
  516. IN LPVOID pvInstData)
  517. {
  518. HRESULT hres = E_INVALIDARG;
  519. if (IS_VALID_HANDLE(pdpa, DPA) &&
  520. IS_VALID_CODE_PTR(pstm, IStream *) &&
  521. IS_VALID_CODE_PTR(pfn, PFNDPASTREAM))
  522. {
  523. DPASTREAMHEADER header;
  524. LARGE_INTEGER dlibMove = { 0 };
  525. ULARGE_INTEGER ulPosBegin;
  526. // Get the current seek position, so we can update the header
  527. // once we know how much we've written
  528. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin);
  529. if (SUCCEEDED(hres))
  530. {
  531. // Write the header (we will update some of this once we're
  532. // finished)
  533. header.cbSize = 0;
  534. header.dwVersion = DPASTREAM_VERSION;
  535. header.celem = 0;
  536. // First write out the header
  537. hres = pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
  538. if (SUCCEEDED(hres))
  539. {
  540. DPASTREAMINFO info;
  541. int cel = DPA_GetPtrCount(pdpa);
  542. LPVOID * ppv = DPA_GetPtrPtr(pdpa);
  543. // This keeps the count of what is actually written
  544. info.iPos = 0;
  545. // Write each element
  546. for (; 0 < cel; cel--, ppv++)
  547. {
  548. info.pvItem = *ppv;
  549. hres = pfn(&info, pstm, pvInstData);
  550. // Returning S_FALSE from callback means it didn't
  551. // write anything for this element, so don't increment
  552. // the iPos (which refers to the count written).
  553. if (S_OK == hres)
  554. info.iPos++;
  555. else if (FAILED(hres))
  556. {
  557. hres = S_FALSE;
  558. break;
  559. }
  560. }
  561. if (FAILED(hres))
  562. {
  563. // Reposition pointer to beginning
  564. dlibMove.LowPart = ulPosBegin.LowPart;
  565. dlibMove.HighPart = ulPosBegin.HighPart;
  566. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  567. }
  568. else
  569. {
  570. ULARGE_INTEGER ulPosEnd;
  571. // Calculate how much was written
  572. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR,
  573. &ulPosEnd);
  574. if (SUCCEEDED(hres))
  575. {
  576. // We only save the low part
  577. ASSERT(ulPosEnd.HighPart == ulPosBegin.HighPart);
  578. // Update the header
  579. header.celem = info.iPos;
  580. header.cbSize = ulPosEnd.LowPart - ulPosBegin.LowPart;
  581. dlibMove.LowPart = ulPosBegin.LowPart;
  582. dlibMove.HighPart = ulPosBegin.HighPart;
  583. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  584. pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
  585. // Reposition pointer
  586. dlibMove.LowPart = ulPosEnd.LowPart;
  587. dlibMove.HighPart = ulPosEnd.HighPart;
  588. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  589. }
  590. }
  591. }
  592. }
  593. }
  594. return hres;
  595. }
  596. /*----------------------------------------------------------
  597. Purpose: Loads the DPA from a stream by calling the given callback
  598. to read each element.
  599. The callback can end the read early by returning
  600. something other than S_OK.
  601. Returns: S_OK on success
  602. S_FALSE if the callback aborted early or the stream ended
  603. abruptly. DPA is partially filled.
  604. error on anything else
  605. */
  606. HRESULT
  607. WINAPI
  608. DPA_LoadStream(
  609. OUT HDPA * ppdpa,
  610. IN PFNDPASTREAM pfn,
  611. IN IStream * pstm,
  612. IN LPVOID pvInstData)
  613. {
  614. HRESULT hres = E_INVALIDARG;
  615. if (IS_VALID_WRITE_PTR(ppdpa, HDPA) &&
  616. IS_VALID_CODE_PTR(pstm, IStream *) &&
  617. IS_VALID_CODE_PTR(pfn, PFNDPASTREAM))
  618. {
  619. DPASTREAMHEADER header;
  620. LARGE_INTEGER dlibMove = { 0 };
  621. ULARGE_INTEGER ulPosBegin;
  622. ULONG cbRead;
  623. *ppdpa = NULL;
  624. // Get the current seek position so we can position pointer
  625. // correctly upon error.
  626. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin);
  627. if (SUCCEEDED(hres))
  628. {
  629. // Read the header
  630. hres = pstm->lpVtbl->Read(pstm, &header, sizeof(header), &cbRead);
  631. if (SUCCEEDED(hres))
  632. {
  633. if (sizeof(header) > cbRead ||
  634. sizeof(header) > header.cbSize ||
  635. DPASTREAM_VERSION != header.dwVersion)
  636. {
  637. hres = E_FAIL;
  638. }
  639. else
  640. {
  641. // Create the list
  642. HDPA pdpa = DPA_Create(header.celem);
  643. if ( !pdpa || !DPA_Grow(pdpa, header.celem))
  644. hres = E_OUTOFMEMORY;
  645. else
  646. {
  647. // Read each element
  648. DPASTREAMINFO info;
  649. LPVOID * ppv = DPA_GetPtrPtr(pdpa);
  650. for (info.iPos = 0; info.iPos < header.celem; )
  651. {
  652. info.pvItem = NULL;
  653. hres = pfn(&info, pstm, pvInstData);
  654. // Returning S_FALSE from the callback means
  655. // it skipped this stream element.
  656. // Don't increment iPos (which refers to the
  657. // count read).
  658. if (S_OK == hres)
  659. {
  660. *ppv = info.pvItem;
  661. info.iPos++;
  662. ppv++;
  663. }
  664. else if (FAILED(hres))
  665. {
  666. hres = S_FALSE;
  667. break;
  668. }
  669. }
  670. pdpa->cp = info.iPos;
  671. *ppdpa = pdpa;
  672. }
  673. }
  674. // Reposition pointer if we failed
  675. if (S_OK != hres)
  676. {
  677. if (S_FALSE == hres)
  678. {
  679. // Position pointer to the end
  680. dlibMove.LowPart = ulPosBegin.LowPart + header.cbSize;
  681. }
  682. else
  683. {
  684. // Position pointer to beginning
  685. dlibMove.LowPart = ulPosBegin.LowPart;
  686. }
  687. dlibMove.HighPart = ulPosBegin.HighPart;
  688. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  689. }
  690. }
  691. }
  692. ASSERT(SUCCEEDED(hres) && *ppdpa ||
  693. FAILED(hres) && NULL == *ppdpa);
  694. }
  695. return hres;
  696. }
  697. /*----------------------------------------------------------
  698. Purpose: Merge two DPAs. This takes two arrays and merges the
  699. source array into the destination.
  700. Merge options:
  701. DPAM_SORTED The arrays are already sorted; don't sort
  702. DPAM_UNION The resulting array is the union of all elements
  703. in both arrays.
  704. DPAM_INTERSECT Only elements in the source array that intersect
  705. with the dest array are merged.
  706. DPAM_NORMAL Like DPAM_INTERSECT except the dest array
  707. also maintains its original, additional elements.
  708. Returns: S_OK for success.
  709. errors if merge fails
  710. Cond: --
  711. */
  712. BOOL
  713. WINAPI
  714. DPA_Merge(
  715. IN HDPA pdpaDest,
  716. IN HDPA pdpaSrc,
  717. IN DWORD dwFlags,
  718. IN PFNDPACOMPARE pfnCompare,
  719. IN PFNDPAMERGE pfnMerge,
  720. IN LPARAM lParam)
  721. {
  722. BOOL bRet = FALSE;
  723. if (IS_VALID_HANDLE(pdpaSrc, DPA) &&
  724. IS_VALID_HANDLE(pdpaDest, DPA) &&
  725. IS_VALID_CODE_PTR(pfnCompare, PFNDPACOMPARE) &&
  726. IS_VALID_CODE_PTR(pfnMerge, PFNDPAMERGE))
  727. {
  728. int iSrc;
  729. int iDest;
  730. int nCmp;
  731. LPVOID * ppvSrc;
  732. LPVOID * ppvDest;
  733. bRet = TRUE;
  734. // Are the arrays already sorted?
  735. if ( !(dwFlags & DPAM_SORTED) )
  736. {
  737. // No; sort them
  738. DPA_Sort(pdpaSrc, pfnCompare, lParam);
  739. DPA_Sort(pdpaDest, pfnCompare, lParam);
  740. }
  741. // This merges in-place. The size of the resulting DPA
  742. // depends on the options:
  743. //
  744. // DPAM_NORMAL Same size as the dest DPA before
  745. // the merge.
  746. //
  747. // DPAM_UNION Min size is the larger of the two.
  748. // Max size is the sum of the two.
  749. //
  750. // DPAM_INTERSECT Min size is zero.
  751. // Max size is the smaller of the two.
  752. //
  753. // We iterate backwards to minimize the amount of moves we
  754. // incur by calling DPA_DeletePtr.
  755. //
  756. iSrc = pdpaSrc->cp - 1;
  757. iDest = pdpaDest->cp - 1;
  758. ppvSrc = &DPA_FastGetPtr(pdpaSrc, iSrc);
  759. ppvDest = &DPA_FastGetPtr(pdpaDest, iDest);
  760. while (0 <= iSrc && 0 <= iDest)
  761. {
  762. LPVOID pv;
  763. nCmp = pfnCompare(*ppvDest, *ppvSrc, lParam);
  764. if (0 == nCmp)
  765. {
  766. // Elements match; merge them.
  767. pv = pfnMerge(DPAMM_MERGE, *ppvDest, *ppvSrc, lParam);
  768. if (NULL == pv)
  769. {
  770. bRet = FALSE;
  771. break;
  772. }
  773. *ppvDest = pv;
  774. iSrc--;
  775. ppvSrc--;
  776. iDest--;
  777. ppvDest--;
  778. }
  779. else if (0 < nCmp)
  780. {
  781. // pvSrc < pvDest. The source array doesn't have pvDest.
  782. if (dwFlags & DPAM_INTERSECT)
  783. {
  784. // Delete pvDest
  785. pfnMerge(DPAMM_DELETE, DPA_DeletePtr(pdpaDest, iDest), NULL, lParam);
  786. }
  787. else
  788. {
  789. ; // Keep it (do nothing)
  790. }
  791. // Move onto the next element in the dest array
  792. iDest--;
  793. ppvDest--;
  794. }
  795. else
  796. {
  797. // pvSrc > pvDest. The dest array doesn't have pvSrc.
  798. if (dwFlags & DPAM_UNION)
  799. {
  800. // Add pvSrc
  801. pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam);
  802. if (NULL == pv)
  803. {
  804. bRet = FALSE;
  805. break;
  806. }
  807. DPA_InsertPtr(pdpaDest, iDest+1, pv);
  808. // DPA_InsertPtr may end up reallocating the pointer array
  809. // thus making ppvDest invalid
  810. ppvDest = &DPA_FastGetPtr(pdpaDest, iDest);
  811. }
  812. else
  813. {
  814. ; // Skip it (do nothing)
  815. }
  816. // Move onto the next element in the source array
  817. iSrc--;
  818. ppvSrc--;
  819. }
  820. }
  821. // there are some items left in src
  822. if ((dwFlags & DPAM_UNION) && 0 <= iSrc)
  823. {
  824. for (; 0 <= iSrc; iSrc--, ppvSrc--)
  825. {
  826. LPVOID pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam);
  827. if (NULL == pv)
  828. {
  829. bRet = FALSE;
  830. break;
  831. }
  832. DPA_InsertPtr(pdpaDest, 0, pv);
  833. }
  834. }
  835. }
  836. return bRet;
  837. }
  838. BOOL WINAPI DPA_Sort(HDPA pdpa, PFNDPACOMPARE pfnCmp, LPARAM lParam)
  839. {
  840. SORTPARAMS sp;
  841. sp.cp = pdpa->cp;
  842. sp.pp = pdpa->pp;
  843. sp.pfnCmp = pfnCmp;
  844. sp.lParam = lParam;
  845. #ifdef USEQUICKSORT
  846. return DPA_QuickSort(&sp);
  847. #endif
  848. #ifdef USEHEAPSORT
  849. return DPA_HeapSort(&sp);
  850. #endif
  851. #ifdef MERGESORT
  852. return DPA_MergeSort(&sp);
  853. #endif
  854. }
  855. #ifdef USEQUICKSORT
  856. BOOL NEAR DPA_QuickSort(SORTPARAMS FAR* psp)
  857. {
  858. return DPA_QuickSort2(0, psp->cp - 1, psp);
  859. }
  860. BOOL NEAR DPA_QuickSort2(int i, int j, SORTPARAMS FAR* psp)
  861. {
  862. void FAR* FAR* pp = psp->pp;
  863. LPARAM lParam = psp->lParam;
  864. PFNDPACOMPARE pfnCmp = psp->pfnCmp;
  865. int iPivot;
  866. void FAR* pFirst;
  867. int k;
  868. int result;
  869. iPivot = -1;
  870. pFirst = pp[i];
  871. for (k = i + 1; k <= j; k++)
  872. {
  873. result = (*pfnCmp)(pp[k], pFirst, lParam);
  874. if (result > 0)
  875. {
  876. iPivot = k;
  877. break;
  878. }
  879. else if (result < 0)
  880. {
  881. iPivot = i;
  882. break;
  883. }
  884. }
  885. if (iPivot != -1)
  886. {
  887. int l = i;
  888. int r = j;
  889. void FAR* pivot = pp[iPivot];
  890. do
  891. {
  892. void FAR* p;
  893. p = pp[l];
  894. pp[l] = pp[r];
  895. pp[r] = p;
  896. while ((*pfnCmp)(pp[l], pivot, lParam) < 0)
  897. l++;
  898. while ((*pfnCmp)(pp[r], pivot, lParam) >= 0)
  899. r--;
  900. } while (l <= r);
  901. if (l - 1 > i)
  902. DPA_QuickSort2(i, l - 1, psp);
  903. if (j > l)
  904. DPA_QuickSort2(l, j, psp);
  905. }
  906. return TRUE;
  907. }
  908. #endif // USEQUICKSORT
  909. #ifdef USEHEAPSORT
  910. void NEAR DPA_HeapSortPushDown(int first, int last, SORTPARAMS FAR* psp)
  911. {
  912. void FAR* FAR* pp = psp->pp;
  913. LPARAM lParam = psp->lParam;
  914. PFNDPACOMPARE pfnCmp = psp->pfnCmp;
  915. int r;
  916. int r2;
  917. void FAR* p;
  918. r = first;
  919. while (r <= last / 2)
  920. {
  921. int wRTo2R;
  922. r2 = r * 2;
  923. wRTo2R = (*pfnCmp)(pp[r-1], pp[r2-1], lParam);
  924. if (r2 == last)
  925. {
  926. if (wRTo2R < 0)
  927. {
  928. p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p;
  929. }
  930. break;
  931. }
  932. else
  933. {
  934. int wR2toR21 = (*pfnCmp)(pp[r2-1], pp[r2+1-1], lParam);
  935. if (wRTo2R < 0 && wR2toR21 >= 0)
  936. {
  937. p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p;
  938. r = r2;
  939. }
  940. else if ((*pfnCmp)(pp[r-1], pp[r2+1-1], lParam) < 0 && wR2toR21 < 0)
  941. {
  942. p = pp[r-1]; pp[r-1] = pp[r2+1-1]; pp[r2+1-1] = p;
  943. r = r2 + 1;
  944. }
  945. else
  946. {
  947. break;
  948. }
  949. }
  950. }
  951. }
  952. BOOL NEAR DPA_HeapSort(SORTPARAMS FAR* psp)
  953. {
  954. void FAR* FAR* pp = psp->pp;
  955. int c = psp->cp;
  956. int i;
  957. for (i = c / 2; i >= 1; i--)
  958. DPA_HeapSortPushDown(i, c, psp);
  959. for (i = c; i >= 2; i--)
  960. {
  961. void FAR* p = pp[0]; pp[0] = pp[i-1]; pp[i-1] = p;
  962. DPA_HeapSortPushDown(1, i - 1, psp);
  963. }
  964. return TRUE;
  965. }
  966. #endif // USEHEAPSORT
  967. #if defined(MERGESORT) && defined(WIN32)
  968. #define SortCompare(psp, pp1, i1, pp2, i2) \
  969. (psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))
  970. //
  971. // This function merges two sorted lists and makes one sorted list.
  972. // psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
  973. //
  974. void NEAR DPA_MergeThem(SORTPARAMS FAR* psp, int iFirst, int cItems)
  975. {
  976. //
  977. // Notes:
  978. // This function is separated from DPA_MergeSort2() to avoid comsuming
  979. // stack variables. Never inline this.
  980. //
  981. int cHalf = cItems/2;
  982. int iIn1, iIn2, iOut;
  983. LPVOID * ppvSrc = &psp->pp[iFirst];
  984. // Copy the first part to temp storage so we can write directly into
  985. // the final buffer. Note that this takes at most psp->cp/2 DWORD's
  986. hmemcpy(psp->ppT, ppvSrc, cHalf*sizeof(LPVOID));
  987. for (iIn1=0, iIn2=cHalf, iOut=0;;)
  988. {
  989. if (SortCompare(psp, psp->ppT, iIn1, ppvSrc, iIn2) <= 0) {
  990. ppvSrc[iOut++] = psp->ppT[iIn1++];
  991. if (iIn1==cHalf) {
  992. // We used up the first half; the rest of the second half
  993. // should already be in place
  994. break;
  995. }
  996. } else {
  997. ppvSrc[iOut++] = ppvSrc[iIn2++];
  998. if (iIn2==cItems) {
  999. // We used up the second half; copy the rest of the first half
  1000. // into place
  1001. hmemcpy(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(LPVOID));
  1002. break;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. //
  1008. // This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
  1009. //
  1010. void NEAR DPA_MergeSort2(SORTPARAMS FAR* psp, int iFirst, int cItems)
  1011. {
  1012. //
  1013. // Notes:
  1014. // This function is recursively called. Therefore, we should minimize
  1015. // the number of local variables and parameters. At this point, we
  1016. // use one local variable and three parameters.
  1017. //
  1018. int cHalf;
  1019. switch(cItems)
  1020. {
  1021. case 1:
  1022. return;
  1023. case 2:
  1024. // Swap them, if they are out of order.
  1025. if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0)
  1026. {
  1027. psp->ppT[0] = psp->pp[iFirst];
  1028. psp->pp[iFirst] = psp->pp[iFirst+1];
  1029. psp->pp[iFirst+1] = psp->ppT[0];
  1030. }
  1031. break;
  1032. default:
  1033. cHalf = cItems/2;
  1034. // Sort each half
  1035. DPA_MergeSort2(psp, iFirst, cHalf);
  1036. DPA_MergeSort2(psp, iFirst+cHalf, cItems-cHalf);
  1037. // Then, merge them.
  1038. DPA_MergeThem(psp, iFirst, cItems);
  1039. break;
  1040. }
  1041. }
  1042. BOOL NEAR DPA_MergeSort(SORTPARAMS FAR* psp)
  1043. {
  1044. if (psp->cp==0)
  1045. return TRUE;
  1046. // Note that we divide by 2 below; we want to round down
  1047. psp->ppT = LocalAlloc(LPTR, psp->cp/2 * sizeof(LPVOID));
  1048. if (!psp->ppT)
  1049. return FALSE;
  1050. DPA_MergeSort2(psp, 0, psp->cp);
  1051. LocalFree(psp->ppT);
  1052. return TRUE;
  1053. }
  1054. #endif // MERGESORT
  1055. // Search function
  1056. //
  1057. int WINAPI DPA_Search(HDPA pdpa, void FAR* pFind, int iStart,
  1058. PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT options)
  1059. {
  1060. int cp = DPA_GetPtrCount(pdpa);
  1061. ASSERT(pfnCompare);
  1062. ASSERT(0 <= iStart);
  1063. // Only allow these wierd flags if the list is sorted
  1064. ASSERT((options & DPAS_SORTED) || !(options & (DPAS_INSERTBEFORE | DPAS_INSERTAFTER)));
  1065. if (!(options & DPAS_SORTED))
  1066. {
  1067. // Not sorted: do linear search.
  1068. int i;
  1069. for (i = iStart; i < cp; i++)
  1070. {
  1071. if (0 == pfnCompare(pFind, DPA_FastGetPtr(pdpa, i), lParam))
  1072. return i;
  1073. }
  1074. return -1;
  1075. }
  1076. else
  1077. {
  1078. // Search the array using binary search. If several adjacent
  1079. // elements match the target element, the index of the first
  1080. // matching element is returned.
  1081. int iRet = -1; // assume no match
  1082. BOOL bFound = FALSE;
  1083. int nCmp = 0;
  1084. int iLow = 0; // Don't bother using iStart for binary search
  1085. int iMid = 0;
  1086. int iHigh = cp - 1;
  1087. // (OK for cp == 0)
  1088. while (iLow <= iHigh)
  1089. {
  1090. iMid = (iLow + iHigh) / 2;
  1091. nCmp = pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid), lParam);
  1092. if (0 > nCmp)
  1093. iHigh = iMid - 1; // First is smaller
  1094. else if (0 < nCmp)
  1095. iLow = iMid + 1; // First is larger
  1096. else
  1097. {
  1098. // Match; search back for first match
  1099. bFound = TRUE;
  1100. while (0 < iMid)
  1101. {
  1102. if (0 != pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid-1), lParam))
  1103. break;
  1104. else
  1105. iMid--;
  1106. }
  1107. break;
  1108. }
  1109. }
  1110. if (bFound)
  1111. {
  1112. ASSERT(0 <= iMid);
  1113. iRet = iMid;
  1114. }
  1115. // Did the search fail AND
  1116. // is one of the strange search flags set?
  1117. if (!bFound && (options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)))
  1118. {
  1119. // Yes; return the index where the target should be inserted
  1120. // if not found
  1121. if (0 < nCmp) // First is larger
  1122. iRet = iLow;
  1123. else
  1124. iRet = iMid;
  1125. // (We don't distinguish between the two flags anymore)
  1126. }
  1127. else if ( !(options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)) )
  1128. {
  1129. // Sanity check with linear search
  1130. ASSERT(DPA_Search(pdpa, pFind, iStart, pfnCompare, lParam, options & ~DPAS_SORTED) == iRet);
  1131. }
  1132. return iRet;
  1133. }
  1134. }
  1135. //===========================================================================
  1136. //
  1137. // String ptr management routines
  1138. //
  1139. // Copy as much of *psz to *pszBuf as will fit
  1140. //
  1141. // Warning: this same code is duplicated below.
  1142. //
  1143. int WINAPI Str_GetPtr(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf)
  1144. {
  1145. int cchToCopy;
  1146. if (!pszCurrent)
  1147. {
  1148. ASSERT(FALSE);
  1149. if (cchBuf > 0)
  1150. *pszBuf = TEXT('\0');
  1151. return 0;
  1152. }
  1153. cchToCopy = lstrlen(pszCurrent);
  1154. // if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
  1155. if (!pszBuf || !cchBuf)
  1156. return cchToCopy + 1;
  1157. if (cchToCopy >= cchBuf)
  1158. cchToCopy = cchBuf - 1;
  1159. hmemcpy(pszBuf, pszCurrent, cchToCopy * SIZEOF(TCHAR));
  1160. pszBuf[cchToCopy] = TEXT('\0');
  1161. return cchToCopy + 1;
  1162. }
  1163. #ifdef DEBUG
  1164. //
  1165. // Str_GetPtr0 is just like Str_GetPtr except that it doesn't assert if
  1166. // pszCurrent = NULL.
  1167. //
  1168. int WINAPI Str_GetPtr0(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf)
  1169. {
  1170. return Str_GetPtr(pszCurrent ? pszCurrent : c_szNULL, pszBuf, cchBuf);
  1171. }
  1172. #endif
  1173. #ifdef UNICODE
  1174. //
  1175. // If we are build Unicode, then this is the ANSI version
  1176. // of the above function.
  1177. //
  1178. int WINAPI Str_GetPtrA(LPCSTR pszCurrent, LPSTR pszBuf, int cchBuf)
  1179. {
  1180. int cchToCopy;
  1181. if (!pszCurrent)
  1182. {
  1183. ASSERT(FALSE);
  1184. if (cchBuf > 0)
  1185. *pszBuf = '\0';
  1186. return 0;
  1187. }
  1188. cchToCopy = lstrlenA(pszCurrent);
  1189. // if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
  1190. if (!pszBuf || !cchBuf)
  1191. return cchToCopy + 1;
  1192. if (cchToCopy >= cchBuf)
  1193. cchToCopy = cchBuf - 1;
  1194. // BUGBUG: Must call TruncateString, as we may be in the middle of DBCS char
  1195. hmemcpy(pszBuf, pszCurrent, cchToCopy * SIZEOF(CHAR));
  1196. pszBuf[cchToCopy] = TEXT('\0');
  1197. return cchToCopy + 1;
  1198. }
  1199. #else
  1200. //
  1201. // Unicode stub if this code is built ANSI
  1202. //
  1203. int WINAPI Str_GetPtrW(LPCWSTR psz, LPWSTR pszBuf, int cchBuf)
  1204. {
  1205. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  1206. return -1;
  1207. }
  1208. #endif
  1209. #ifdef WIN32
  1210. //
  1211. // This function is not exported.
  1212. //
  1213. BOOL Str_Set(LPTSTR *ppsz, LPCTSTR psz)
  1214. {
  1215. if (!psz || (psz == LPSTR_TEXTCALLBACK))
  1216. {
  1217. if (*ppsz)
  1218. {
  1219. if (*ppsz != (LPSTR_TEXTCALLBACK))
  1220. LocalFree(*ppsz);
  1221. }
  1222. *ppsz = (LPTSTR)psz;
  1223. }
  1224. else
  1225. {
  1226. LPTSTR pszNew = *ppsz;
  1227. UINT cbSize = (lstrlen(psz) + 1) * sizeof(TCHAR);
  1228. if (pszNew == LPSTR_TEXTCALLBACK)
  1229. pszNew = NULL;
  1230. pszNew = CCLocalReAlloc(pszNew, cbSize);
  1231. if (!pszNew)
  1232. return FALSE;
  1233. lstrcpy(pszNew, psz);
  1234. *ppsz = pszNew;
  1235. }
  1236. return TRUE;
  1237. }
  1238. #endif
  1239. // Set *ppszCurrent to a copy of pszNew, and free the previous value, if necessary
  1240. //
  1241. // WARNING: This same code is duplicated below
  1242. //
  1243. BOOL WINAPI Str_SetPtr(LPTSTR * ppszCurrent, LPCTSTR pszNew)
  1244. {
  1245. int cchLength;
  1246. LPTSTR pszOld;
  1247. LPTSTR pszNewCopy = NULL;
  1248. if (pszNew)
  1249. {
  1250. cchLength = lstrlen(pszNew);
  1251. // alloc a new buffer w/ room for the null terminator
  1252. pszNewCopy = (LPTSTR) Alloc((cchLength + 1) * SIZEOF(TCHAR));
  1253. if (!pszNewCopy)
  1254. return FALSE;
  1255. lstrcpyn(pszNewCopy, pszNew, cchLength + 1);
  1256. }
  1257. pszOld = InterlockedExchangePointer((LPVOID *)ppszCurrent, pszNewCopy);
  1258. if (pszOld)
  1259. Free(pszOld);
  1260. return TRUE;
  1261. }
  1262. #ifdef UNICODE
  1263. //
  1264. // ANSI stub when built Unicode.
  1265. //
  1266. BOOL WINAPI Str_SetPtrA(LPSTR * ppszCurrent, LPCSTR pszNew)
  1267. {
  1268. int cchLength;
  1269. LPSTR pszOld;
  1270. LPSTR pszNewCopy = NULL;
  1271. if (pszNew)
  1272. {
  1273. cchLength = lstrlenA(pszNew);
  1274. // alloc a new buffer w/ room for the null terminator
  1275. pszNewCopy = (LPSTR) Alloc((cchLength + 1) * SIZEOF(CHAR));
  1276. if (!pszNewCopy)
  1277. return FALSE;
  1278. lstrcpynA(pszNewCopy, pszNew, cchLength + 1);
  1279. }
  1280. pszOld = InterlockedExchangePointer((LPVOID *)ppszCurrent, pszNewCopy);
  1281. if (pszOld)
  1282. Free(pszOld);
  1283. return TRUE;
  1284. }
  1285. #else
  1286. // Unicode stub if this is built ANSI
  1287. BOOL WINAPI Str_SetPtrW(LPWSTR *ppwzCurrent, LPCWSTR pszNew)
  1288. {
  1289. int cchLength;
  1290. LPWSTR pwzOld;
  1291. LPWSTR pwzNewCopy = NULL;
  1292. if (pszNew)
  1293. {
  1294. cchLength = lstrlenW(pszNew); // Yes this is implemented on Win95.
  1295. // alloc a new buffer w/ room for the null terminator
  1296. pwzNewCopy = (LPWSTR) Alloc((cchLength + 1) * SIZEOF(WCHAR));
  1297. if (!pwzNewCopy)
  1298. return FALSE;
  1299. // lstrcpynW is thunked in unicwrap.cpp for Win95 machines.
  1300. StrCpyNW(pwzNewCopy, pszNew, cchLength + 1);
  1301. }
  1302. pwzOld = InterlockedExchangePointer((LPVOID *)ppwzCurrent, pwzNewCopy);
  1303. if (pwzOld)
  1304. Free(pwzOld);
  1305. return TRUE;
  1306. }
  1307. #endif