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.

483 lines
14 KiB

  1. //
  2. // normal.cpp
  3. //
  4. #include "private.h"
  5. #include "normal.h"
  6. #include "txtcache.h"
  7. //+---------------------------------------------------------------------------
  8. //
  9. // GetTextComplete
  10. //
  11. // Wrapper for GetText that keeps asking until the input buffers are full.
  12. //----------------------------------------------------------------------------
  13. HRESULT GetTextComplete(ITextStoreACP *ptsi, LONG acpStart, LONG acpEnd,
  14. WCHAR *pchPlain, ULONG cchPlainReq,
  15. ULONG *pcchPlainOut, TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoOut,
  16. LONG *pacpNext)
  17. {
  18. ULONG cchPlainOut;
  19. ULONG ulRunInfoOut;
  20. BOOL fNoMoreSpace;
  21. HRESULT hr;
  22. fNoMoreSpace = FALSE;
  23. *pcchPlainOut = 0;
  24. *pulRunInfoOut = 0;
  25. while (TRUE)
  26. {
  27. Perf_IncCounter(PERF_NORM_GETTEXTCOMPLETE);
  28. hr = CProcessTextCache::GetText(ptsi, acpStart, acpEnd, pchPlain, cchPlainReq, &cchPlainOut,
  29. prgRunInfo, ulRunInfoReq, &ulRunInfoOut, pacpNext);
  30. if (hr != S_OK)
  31. break;
  32. if (cchPlainOut == 0 && ulRunInfoOut == 0)
  33. break; // eod
  34. if (cchPlainReq > 0 && cchPlainOut > 0)
  35. {
  36. cchPlainReq -= cchPlainOut;
  37. *pcchPlainOut += cchPlainOut;
  38. if (cchPlainReq == 0)
  39. {
  40. fNoMoreSpace = TRUE;
  41. }
  42. else
  43. {
  44. pchPlain += cchPlainOut;
  45. }
  46. }
  47. if (ulRunInfoReq > 0)
  48. {
  49. Assert(ulRunInfoOut > 0 && prgRunInfo->uCount > 0); // app bug?
  50. if (ulRunInfoOut == 0)
  51. break; // woah, app bug, avoid infinite loop
  52. ulRunInfoReq -= ulRunInfoOut;
  53. *pulRunInfoOut += ulRunInfoOut;
  54. if (ulRunInfoReq == 0)
  55. {
  56. fNoMoreSpace = TRUE;
  57. }
  58. else
  59. {
  60. prgRunInfo += ulRunInfoOut;
  61. }
  62. }
  63. if (fNoMoreSpace)
  64. break; // buffers full
  65. if (*pacpNext == acpEnd)
  66. break; // got it all
  67. acpStart = *pacpNext;
  68. Assert(acpStart < acpEnd);
  69. }
  70. return hr;
  71. }
  72. //+---------------------------------------------------------------------------
  73. //
  74. // PlainTextOffset
  75. //
  76. // NB: current implementation always skips hidden text.
  77. //----------------------------------------------------------------------------
  78. HRESULT PlainTextOffset(ITextStoreACP *ptsi, LONG ichAppBase, LONG iAppOffset, LONG *piPlainOffset)
  79. {
  80. BOOL fNeg;
  81. HRESULT hr;
  82. ULONG uRunInfoLen;
  83. ULONG cch;
  84. ULONG cchPlain;
  85. TS_RUNINFO *pri;
  86. TS_RUNINFO *priStop;
  87. TS_RUNINFO rgRunInfo[32];
  88. *piPlainOffset = 0;
  89. if (iAppOffset == 0)
  90. return S_OK;
  91. fNeg = FALSE;
  92. if (iAppOffset < 0)
  93. {
  94. fNeg = TRUE;
  95. ichAppBase += iAppOffset;
  96. iAppOffset = -iAppOffset;
  97. }
  98. cchPlain = 0;
  99. do
  100. {
  101. Perf_IncCounter(PERF_PTO_GETTEXT);
  102. hr = CProcessTextCache::GetText(ptsi, ichAppBase, ichAppBase + iAppOffset, NULL, 0, &cch,
  103. rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &ichAppBase);
  104. if (hr != S_OK)
  105. goto Exit;
  106. if (uRunInfoLen == 0 || rgRunInfo[0].uCount == 0)
  107. {
  108. Assert(0); // this should never happen, it means cicero is referencing a position past end-of-doc
  109. hr = E_UNEXPECTED;
  110. goto Exit;
  111. }
  112. cch = 0;
  113. pri = rgRunInfo;
  114. priStop = rgRunInfo + uRunInfoLen;
  115. while (pri < priStop)
  116. {
  117. if (pri->type == TS_RT_PLAIN)
  118. {
  119. cchPlain += pri->uCount;
  120. }
  121. iAppOffset -= pri->uCount;
  122. pri++;
  123. }
  124. } while (iAppOffset > 0);
  125. *piPlainOffset = fNeg ? -(LONG)cchPlain : cchPlain;
  126. hr = S_OK;
  127. Exit:
  128. return hr;
  129. }
  130. //+---------------------------------------------------------------------------
  131. //
  132. // AppTextOffsetForward
  133. //
  134. // piAppOffset, on return, points just past the iPlainOffset plain char or eod.
  135. // Use AppTextOffsetNorm for a normalized return value!
  136. //
  137. // Returns S_FALSE if clipped due to bod or eod.
  138. //----------------------------------------------------------------------------
  139. inline IsPlainRun(TS_RUNINFO *pri, BOOL fSkipHidden)
  140. {
  141. return (pri->type == TS_RT_PLAIN ||
  142. (!fSkipHidden && pri->type == TS_RT_HIDDEN));
  143. }
  144. HRESULT AppTextOffsetForward(ITextStoreACP *ptsi, LONG ichAppBase, LONG iPlainOffset, LONG *piAppOffset, DWORD dwFlags)
  145. {
  146. LONG acpStart;
  147. LONG acpEnd;
  148. HRESULT hr;
  149. ULONG uRunInfoLen;
  150. ULONG cch;
  151. ULONG cchRead;
  152. ULONG cchACP;
  153. TS_RUNINFO *pri;
  154. TS_RUNINFO *priStop;
  155. ULONG i;
  156. WCHAR *pch;
  157. TS_RUNINFO rgRunInfo[32];
  158. WCHAR ach[256];
  159. BOOL fIgnoreRegions = (dwFlags & ATO_IGNORE_REGIONS);
  160. BOOL fSkipHidden = (dwFlags & ATO_SKIP_HIDDEN);
  161. Perf_IncCounter(PERF_ATOF_COUNTER);
  162. Assert(iPlainOffset > 0);
  163. Assert(*piAppOffset == 0);
  164. cchACP = 0;
  165. // Issue: use TsSF_REGIONS
  166. cchRead = (ULONG)(fIgnoreRegions ? 0 : ARRAYSIZE(ach)); // we only need text if looking for regions
  167. do
  168. {
  169. acpStart = ichAppBase;
  170. acpEnd = (ichAppBase + iPlainOffset) < 0 ? LONG_MAX : ichAppBase + iPlainOffset;
  171. Assert(acpEnd >= acpStart);
  172. Perf_IncCounter(PERF_ATOF_GETTEXT_COUNTER);
  173. hr = CProcessTextCache::GetText(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
  174. rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEnd);
  175. if (hr != S_OK)
  176. {
  177. Assert(0);
  178. goto Exit;
  179. }
  180. if (uRunInfoLen == 0) // hit eod?
  181. {
  182. hr = S_FALSE;
  183. break;
  184. }
  185. pri = rgRunInfo;
  186. priStop = rgRunInfo + uRunInfoLen;
  187. pch = &ach[0];
  188. while (pri != priStop)
  189. {
  190. Assert(pri->uCount > 0); // runs should always be at least one char long
  191. // scan for region boundary if necessary
  192. if (!fIgnoreRegions && pri->type != TS_RT_OPAQUE)
  193. {
  194. if (IsPlainRun(pri, fSkipHidden))
  195. {
  196. // run is plain or hidden text (and we want to count hidden text)
  197. for (i=0; i<pri->uCount; i++)
  198. {
  199. if (*pch == TS_CHAR_REGION)
  200. {
  201. // we hit a region boundary, pull out!
  202. cchACP += i;
  203. hr = S_FALSE; // for normalization
  204. goto ExitOK;
  205. }
  206. pch++;
  207. }
  208. }
  209. else
  210. {
  211. // run is hidden text, which we want to skip over
  212. pch += pri->uCount;
  213. }
  214. }
  215. cchACP += pri->uCount;
  216. if (IsPlainRun(pri, fSkipHidden))
  217. {
  218. iPlainOffset -= pri->uCount;
  219. }
  220. ichAppBase += pri->uCount;
  221. pri++;
  222. }
  223. }
  224. while (iPlainOffset != 0);
  225. ExitOK:
  226. *piAppOffset = cchACP;
  227. Exit:
  228. return hr;
  229. }
  230. //+---------------------------------------------------------------------------
  231. //
  232. // AppTextOffsetBackward
  233. //
  234. // piAppOffset, on return, points just past the iPlainOffset plain char or eod.
  235. // Use AppTextOffsetNorm for a normalized return value!
  236. //
  237. // Returns S_FALSE if clipped due to bod or eod.
  238. //----------------------------------------------------------------------------
  239. HRESULT AppTextOffsetBackward(ITextStoreACP *ptsi, LONG ichAppBase, LONG iPlainOffset, LONG *piAppOffset, DWORD dwFlags)
  240. {
  241. LONG acpStart;
  242. LONG acpEnd;
  243. LONG acpEndOut;
  244. HRESULT hr;
  245. ULONG uRunInfoLen;
  246. ULONG cch;
  247. ULONG cchRead;
  248. ULONG cchACP;
  249. TS_RUNINFO *pri;
  250. TS_RUNINFO *priStop;
  251. ULONG i;
  252. TS_RUNINFO rgRunInfo[32];
  253. WCHAR *pch;
  254. WCHAR ach[256];
  255. BOOL fIgnoreRegions = (dwFlags & ATO_IGNORE_REGIONS);
  256. BOOL fSkipHidden = (dwFlags & ATO_SKIP_HIDDEN);
  257. Assert(iPlainOffset < 0);
  258. Assert(*piAppOffset == 0);
  259. cchACP = 0;
  260. // Issue: use TsSF_REGIONS
  261. cchRead = (ULONG)(fIgnoreRegions ? 0 : ARRAYSIZE(ach)); // we only need text if looking for regions
  262. do
  263. {
  264. Assert(iPlainOffset < 0); // if this is >= 0, we or the app messed up the formatting run count
  265. acpStart = ichAppBase + (fIgnoreRegions ? iPlainOffset : max(iPlainOffset, -(LONG)ARRAYSIZE(ach)));
  266. acpStart = max(acpStart, 0); // handle top-of-doc collisions
  267. acpEnd = ichAppBase;
  268. Assert(acpEnd >= acpStart);
  269. hr = GetTextComplete(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
  270. rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEndOut);
  271. if (hr != S_OK)
  272. {
  273. Assert(0);
  274. goto Exit;
  275. }
  276. if (uRunInfoLen == 0) // hit eod?
  277. {
  278. hr = S_FALSE;
  279. break;
  280. }
  281. // it's possible the GetText above didn't return everything we asked for....
  282. // this happens when our format buffer isn't large enough
  283. if (acpEndOut != acpEnd)
  284. {
  285. // so let's be conservative and ask for something we know should succeed
  286. acpStart = ichAppBase - ARRAYSIZE(rgRunInfo);
  287. Assert(acpStart >= 0); // the prev GetText should have succeeded if there were fewer chars than we're asking for now....
  288. Assert(acpEnd - acpStart < -iPlainOffset); // again, we shouldn't get this far if we already asked for fewer chars
  289. Assert(ARRAYSIZE(rgRunInfo) < ARRAYSIZE(ach)); // want to ask for the max we can handle in the worst case
  290. Assert(acpEnd == ichAppBase);
  291. hr = GetTextComplete(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
  292. rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEndOut);
  293. if (hr != S_OK)
  294. {
  295. Assert(0);
  296. goto Exit;
  297. }
  298. if (uRunInfoLen == 0) // hit eod?
  299. {
  300. Assert(0); // this should never happen, because the original call for more chars returned non-zero!
  301. goto Exit;
  302. }
  303. Assert(acpEnd == acpEndOut);
  304. }
  305. pri = rgRunInfo + uRunInfoLen - 1;
  306. priStop = rgRunInfo - 1;
  307. pch = &ach[cch-1];
  308. while (pri != priStop)
  309. {
  310. Assert(pri->uCount > 0); // runs should always be at least one char long
  311. // scan for region boundary if necessary
  312. if (!fIgnoreRegions && pri->type != TS_RT_OPAQUE)
  313. {
  314. if (IsPlainRun(pri, fSkipHidden))
  315. {
  316. // run is plain or hidden text (and we want to count hidden text)
  317. for (i=0; i<pri->uCount; i++)
  318. {
  319. if (*pch == TS_CHAR_REGION)
  320. {
  321. // we hit a region boundary, pull out!
  322. cchACP += i;
  323. hr = S_FALSE; // for normalization
  324. goto ExitOK;
  325. }
  326. pch--;
  327. }
  328. }
  329. else
  330. {
  331. // run is hidden text, which we want to skip over
  332. pch -= pri->uCount;
  333. }
  334. }
  335. cchACP += pri->uCount;
  336. if (IsPlainRun(pri, fSkipHidden))
  337. {
  338. iPlainOffset += (LONG)pri->uCount;
  339. }
  340. ichAppBase -= (LONG)pri->uCount;
  341. pri--;
  342. }
  343. // also check for top-of-doc
  344. if (ichAppBase == 0)
  345. {
  346. hr = S_FALSE;
  347. break;
  348. }
  349. } while (iPlainOffset != 0);
  350. ExitOK:
  351. *piAppOffset = -(LONG)cchACP;
  352. Exit:
  353. return hr;
  354. }
  355. #ifdef UNUSED
  356. //+---------------------------------------------------------------------------
  357. //
  358. // AppTextOffsetNorm
  359. //
  360. // Returns a normalized acp offset that spans the specificed number of plain chars --
  361. // so the return offset is just short of (lPlainOffset + 1), or at eod. Returns
  362. // S_FALSE if the initial call to AppTextOffset gets clipped because of eod.
  363. //----------------------------------------------------------------------------
  364. HRESULT AppTextOffsetNorm(ITextStoreACP *ptsi, LONG acpAppBase, LONG lPlainOffset, LONG *plAppOffset)
  365. {
  366. HRESULT hr;
  367. Perf_IncCounter(PERF_ATON_COUNTER);
  368. // if caller wants a neg offset, return value is already
  369. // guarenteed normalized -- just before a plain text char.
  370. // Otherwise, ask for the offset of the next char, then
  371. // step back one char.
  372. if ((lPlainOffset < LONG_MAX) && (lPlainOffset >= 0))
  373. {
  374. lPlainOffset++;
  375. }
  376. hr = AppTextOffset(ptsi, acpAppBase, lPlainOffset, plAppOffset, FALSE);
  377. if (*plAppOffset > 0)
  378. {
  379. if ((lPlainOffset < LONG_MAX) && (hr == S_OK)) // could be S_FALSE if hit eod
  380. {
  381. // step back, and we're normalized
  382. (*plAppOffset)--;
  383. }
  384. }
  385. else if (*plAppOffset < 0)
  386. {
  387. // if we moved backwards there's only one case to
  388. // worry about: if we hit a region boundary. Then
  389. // we need to normalize.
  390. if (hr == S_FALSE)
  391. {
  392. *plAppOffset = Normalize(ptsi, acpAppBase + *plAppOffset) - acpAppBase;
  393. }
  394. }
  395. #ifndef PERF_DUMP
  396. Assert(*plAppOffset == Normalize(ptsi, acpAppBase + *plAppOffset) - acpAppBase);
  397. #endif
  398. return hr;
  399. }
  400. #endif // UNUSED