whelp.pas 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 1998 by Berczi Gabor
  5. Help support & Borland OA .HLP reader objects and routines
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$R-}
  13. unit WHelp;
  14. interface
  15. uses Objects;
  16. const
  17. MinFormatVersion = $34;
  18. Signature = '$*$* &&&&$*$'#0;
  19. ncRawChar = $F;
  20. ncRepChar = $E;
  21. rtFileHeader = Byte ($0);
  22. rtContext = Byte ($1);
  23. rtText = Byte ($2);
  24. rtKeyWord = Byte ($3);
  25. rtIndex = Byte ($4);
  26. rtCompression = Byte ($5);
  27. rtIndexTags = Byte ($6);
  28. ctNone = $00;
  29. ctNibble = $02;
  30. hscLineBreak = #0;
  31. hscLink = #2;
  32. hscLineStart = #3;
  33. hscCode = #5;
  34. hscCenter = #10;
  35. hscRight = #11;
  36. type
  37. FileStamp = array [0..32] of char; {+ null terminator + $1A }
  38. FileSignature = array [0..12] of char; {+ null terminator }
  39. THelpCtx = longint;
  40. THLPVersion = packed record
  41. FormatVersion : byte;
  42. TextVersion : byte;
  43. end;
  44. THLPRecordHeader = packed record
  45. RecType : byte; {TPRecType}
  46. RecLength : word;
  47. end;
  48. THLPContextPos = packed record
  49. LoW: word;
  50. HiB: byte;
  51. end;
  52. THLPContexts = packed record
  53. ContextCount : word;
  54. Contexts : array[0..0] of THLPContextPos;
  55. end;
  56. THLPFileHeader = packed record
  57. Options : word;
  58. MainIndexScreen : word;
  59. MaxScreenSize : word;
  60. Height : byte;
  61. Width : byte;
  62. LeftMargin : byte;
  63. end;
  64. THLPCompression = packed record
  65. CompType : byte;
  66. CharTable : array [0..13] of byte;
  67. end;
  68. THLPIndexDescriptor = packed record
  69. LengthCode : byte;
  70. UniqueChars : array [0..0] of byte;
  71. Context : word;
  72. end;
  73. THLPIndexTable = packed record
  74. IndexCount : word;
  75. Entries : record end;
  76. end;
  77. THLPKeywordDescriptor = packed record
  78. KwContext : word;
  79. end;
  80. THLPKeyWordRecord = packed record
  81. UpContext : word;
  82. DownContext : word;
  83. KeyWordCount : word;
  84. Keywords : array[0..0] of THLPKeywordDescriptor;
  85. end;
  86. TRecord = packed record
  87. SClass : byte;
  88. Size : word;
  89. Data : pointer;
  90. end;
  91. PIndexEntry = ^TIndexEntry;
  92. TIndexEntry = packed record
  93. Tag : PString;
  94. HelpCtx : THelpCtx;
  95. FileID : word;
  96. end;
  97. PKeywordDescriptor = ^TKeywordDescriptor;
  98. TKeywordDescriptor = packed record
  99. FileID : word;
  100. Context : THelpCtx;
  101. end;
  102. PKeywordDescriptors = ^TKeywordDescriptors;
  103. TKeywordDescriptors = array[0..10900] of TKeywordDescriptor;
  104. PTopic = ^TTopic;
  105. TTopic = object
  106. HelpCtx : THelpCtx;
  107. FileOfs : longint;
  108. TextSize : sw_word;
  109. Text : PByteArray;
  110. LinkCount : sw_word;
  111. Links : PKeywordDescriptors;
  112. LastAccess : longint;
  113. FileID : word;
  114. Param : PString;
  115. function LinkSize: sw_word;
  116. end;
  117. PTopicCollection = ^TTopicCollection;
  118. TTopicCollection = object(TSortedCollection)
  119. function At(Index: sw_Integer): PTopic;
  120. procedure FreeItem(Item: Pointer); virtual;
  121. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  122. function SearchTopic(AHelpCtx: THelpCtx): PTopic;
  123. end;
  124. PIndexEntryCollection = ^TIndexEntryCollection;
  125. TIndexEntryCollection = object(TSortedCollection)
  126. function At(Index: Sw_Integer): PIndexEntry;
  127. procedure FreeItem(Item: Pointer); virtual;
  128. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  129. end;
  130. PHelpFile = ^THelpFile;
  131. THelpFile = object(TObject)
  132. ID : word;
  133. Topics : PTopicCollection;
  134. IndexEntries : PIndexEntryCollection;
  135. constructor Init(AID: word);
  136. function LoadTopic(HelpCtx: THelpCtx): PTopic; virtual;
  137. destructor Done; virtual;
  138. public
  139. function LoadIndex: boolean; virtual;
  140. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  141. function ReadTopic(T: PTopic): boolean; virtual;
  142. private
  143. procedure MaintainTopicCache;
  144. end;
  145. POAHelpFile = ^TOAHelpFile;
  146. TOAHelpFile = object(THelpFile)
  147. Version : THLPVersion;
  148. Header : THLPFileHeader;
  149. Compression : THLPCompression;
  150. constructor Init(AFileName: string; AID: word);
  151. destructor Done; virtual;
  152. public
  153. function LoadIndex: boolean; virtual;
  154. function ReadTopic(T: PTopic): boolean; virtual;
  155. public { protected }
  156. F: PStream;
  157. TopicsRead : boolean;
  158. IndexTableRead : boolean;
  159. CompressionRead: boolean;
  160. IndexTagsRead : boolean;
  161. IndexTagsPos : longint;
  162. IndexTablePos : longint;
  163. function ReadHeader: boolean;
  164. function ReadTopics: boolean;
  165. function ReadIndexTable: boolean;
  166. function ReadCompression: boolean;
  167. function ReadIndexTags: boolean;
  168. function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  169. end;
  170. PHelpFileCollection = PCollection;
  171. PHelpFacility = ^THelpFacility;
  172. THelpFacility = object(TObject)
  173. HelpFiles: PHelpFileCollection;
  174. IndexTabSize: sw_integer;
  175. constructor Init;
  176. function AddOAHelpFile(FileName: string): boolean;
  177. function AddHTMLHelpFile(FileName, TOCEntry: string): boolean;
  178. function LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
  179. function TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
  180. function BuildIndexTopic: PTopic; virtual;
  181. destructor Done; virtual;
  182. private
  183. LastID: word;
  184. function SearchFile(ID: byte): PHelpFile;
  185. function SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  186. function SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  187. function AddFile(H: PHelpFile): boolean;
  188. end;
  189. const TopicCacheSize : sw_integer = 10;
  190. HelpStreamBufSize : sw_integer = 4096;
  191. HelpFacility : PHelpFacility = nil;
  192. MaxHelpTopicSize : sw_word = MaxBytes;
  193. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string): PTopic;
  194. procedure DisposeTopic(P: PTopic);
  195. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  196. procedure DisposeIndexEntry(P: PIndexEntry);
  197. implementation
  198. uses
  199. Dos,
  200. {$ifdef Linux}
  201. linux,
  202. {$endif Linux}
  203. {$ifdef Win32}
  204. windows,
  205. {$endif Win32}
  206. WUtils,WViews,WHTMLHlp;
  207. Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
  208. {$IFDEF LINUX}
  209. var
  210. tv : TimeVal;
  211. tz : TimeZone;
  212. begin
  213. GetTimeOfDay(tv); {Timezone no longer used?}
  214. GetDosTicks:=((tv.Sec mod 86400) div 60)*1092+((tv.Sec mod 60)*1000000+tv.USec) div 54945;
  215. end;
  216. {$endif Linux}
  217. {$ifdef Win32}
  218. begin
  219. GetDosTicks:=(Windows.GetTickCount*5484) div 100;
  220. end;
  221. {$endif Win32}
  222. {$ifdef go32v2}
  223. begin
  224. GetDosTicks:=MemL[$40:$6c];
  225. end;
  226. {$endif go32v2}
  227. {$ifdef TP}
  228. begin
  229. GetDosTicks:=MemL[$40:$6c];
  230. end;
  231. {$endif go32v2}
  232. procedure DisposeRecord(var R: TRecord);
  233. begin
  234. with R do
  235. if (Size>0) and (Data<>nil) then FreeMem(Data, Size);
  236. FillChar(R, SizeOf(R), 0);
  237. end;
  238. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string): PTopic;
  239. var P: PTopic;
  240. begin
  241. New(P); FillChar(P^,SizeOf(P^), 0);
  242. P^.HelpCtx:=HelpCtx; P^.FileOfs:=Pos; P^.FileID:=FileID;
  243. P^.Param:=NewStr(Param);
  244. NewTopic:=P;
  245. end;
  246. procedure DisposeTopic(P: PTopic);
  247. begin
  248. if P<>nil then
  249. begin
  250. if (P^.TextSize>0) and (P^.Text<>nil) then
  251. FreeMem(P^.Text,P^.TextSize);
  252. P^.Text:=nil;
  253. if (P^.LinkCount>0) and (P^.Links<>nil) then
  254. FreeMem(P^.Links,P^.LinkSize);
  255. P^.Links:=nil;
  256. if P^.Param<>nil then DisposeStr(P^.Param); P^.Param:=nil;
  257. Dispose(P);
  258. end;
  259. end;
  260. function CloneTopic(T: PTopic): PTopic;
  261. var NT: PTopic;
  262. begin
  263. New(NT); Move(T^,NT^,SizeOf(NT^));
  264. if NT^.Text<>nil then
  265. begin GetMem(NT^.Text,NT^.TextSize); Move(T^.Text^,NT^.Text^,NT^.TextSize); end;
  266. if NT^.Links<>nil then
  267. begin GetMem(NT^.Links,NT^.LinkSize); Move(T^.Links^,NT^.Links^,NT^.LinkSize); end;
  268. if NT^.Param<>nil then
  269. NT^.Param:=NewStr(T^.Param^);
  270. CloneTopic:=NT;
  271. end;
  272. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  273. var P: PIndexEntry;
  274. begin
  275. New(P); FillChar(P^,SizeOf(P^), 0);
  276. P^.Tag:=NewStr(Tag); P^.FileID:=FileID; P^.HelpCtx:=HelpCtx;
  277. NewIndexEntry:=P;
  278. end;
  279. procedure DisposeIndexEntry(P: PIndexEntry);
  280. begin
  281. if P<>nil then
  282. begin
  283. if P^.Tag<>nil then DisposeStr(P^.Tag);
  284. Dispose(P);
  285. end;
  286. end;
  287. function TTopic.LinkSize: sw_word;
  288. begin
  289. LinkSize:=LinkCount*SizeOf(Links^[0]);
  290. end;
  291. function TTopicCollection.At(Index: sw_Integer): PTopic;
  292. begin
  293. At:=inherited At(Index);
  294. end;
  295. procedure TTopicCollection.FreeItem(Item: Pointer);
  296. begin
  297. if Item<>nil then DisposeTopic(Item);
  298. end;
  299. function TTopicCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  300. var K1: PTopic absolute Key1;
  301. K2: PTopic absolute Key2;
  302. R: Sw_integer;
  303. begin
  304. if K1^.HelpCtx<K2^.HelpCtx then R:=-1 else
  305. if K1^.HelpCtx>K2^.HelpCtx then R:= 1 else
  306. R:=0;
  307. Compare:=R;
  308. end;
  309. function TTopicCollection.SearchTopic(AHelpCtx: THelpCtx): PTopic;
  310. var T: TTopic;
  311. P: PTopic;
  312. Index: sw_integer;
  313. begin
  314. T.HelpCtx:=AHelpCtx;
  315. if Search(@T,Index) then
  316. P:=At(Index)
  317. else
  318. P:=nil;
  319. SearchTopic:=P;
  320. end;
  321. function TIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  322. begin
  323. At:=inherited At(Index);
  324. end;
  325. procedure TIndexEntryCollection.FreeItem(Item: Pointer);
  326. begin
  327. if Item<>nil then DisposeIndexEntry(Item);
  328. end;
  329. function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  330. var K1: PIndexEntry absolute Key1;
  331. K2: PIndexEntry absolute Key2;
  332. R: Sw_integer;
  333. S1,S2: string;
  334. begin
  335. S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
  336. if S1<S2 then R:=-1 else
  337. if S1>S2 then R:=1 else
  338. R:=0;
  339. Compare:=R;
  340. end;
  341. constructor THelpFile.Init(AID: word);
  342. begin
  343. inherited Init;
  344. ID:=AID;
  345. New(Topics, Init(500,500));
  346. New(IndexEntries, Init(200,100));
  347. end;
  348. function THelpFile.LoadTopic(HelpCtx: THelpCtx): PTopic;
  349. var T: PTopic;
  350. begin
  351. T:=SearchTopic(HelpCtx);
  352. if (T<>nil) then
  353. if T^.Text=nil then
  354. begin
  355. MaintainTopicCache;
  356. if ReadTopic(T)=false then
  357. T:=nil;
  358. if (T<>nil) and (T^.Text=nil) then T:=nil;
  359. end;
  360. if T<>nil then
  361. begin T^.LastAccess:=GetDosTicks; T:=CloneTopic(T); end;
  362. LoadTopic:=T;
  363. end;
  364. function THelpFile.LoadIndex: boolean;
  365. begin
  366. Abstract;
  367. LoadIndex:=false; { remove warning }
  368. end;
  369. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  370. var T: PTopic;
  371. begin
  372. T:=Topics^.SearchTopic(HelpCtx);
  373. SearchTopic:=T;
  374. end;
  375. function THelpFile.ReadTopic(T: PTopic): boolean;
  376. begin
  377. Abstract;
  378. ReadTopic:=false; { remove warning }
  379. end;
  380. procedure THelpFile.MaintainTopicCache;
  381. var Count: sw_integer;
  382. MinP: PTopic;
  383. MinLRU: longint;
  384. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  385. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  386. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  387. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; MinP:=P; end; end;
  388. var P: PTopic;
  389. begin
  390. Count:=0; Topics^.ForEach(@CountThem);
  391. if (Count>=TopicCacheSize) then
  392. begin
  393. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  394. if P<>nil then
  395. begin
  396. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  397. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  398. end;
  399. end;
  400. end;
  401. destructor THelpFile.Done;
  402. begin
  403. if Topics<>nil then Dispose(Topics, Done);
  404. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  405. inherited Done;
  406. end;
  407. constructor TOAHelpFile.Init(AFileName: string; AID: word);
  408. var OK: boolean;
  409. FS,L: longint;
  410. R: TRecord;
  411. begin
  412. inherited Init(AID);
  413. F:=New(PBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  414. OK:=F<>nil;
  415. if OK then OK:=(F^.Status=stOK);
  416. if OK then
  417. begin
  418. FS:=F^.GetSize;
  419. OK:=ReadHeader;
  420. end;
  421. while OK do
  422. begin
  423. L:=F^.GetPos;
  424. if (L>=FS) then Break;
  425. OK:=ReadRecord(R,false);
  426. if (OK=false) or (R.SClass=0) or (R.Size=0) then Break;
  427. case R.SClass of
  428. rtContext : begin F^.Seek(L); OK:=ReadTopics; end;
  429. rtText : {Skip};
  430. rtKeyword : {Skip};
  431. rtIndex : begin IndexTablePos:=L; {OK:=ReadIndexTable; }end;
  432. rtCompression : begin F^.Seek(L); OK:=ReadCompression; end;
  433. rtIndexTags : begin IndexTagsPos:=L; {OK:=ReadIndexTags; }end;
  434. else {Skip};
  435. end;
  436. if OK then
  437. begin Inc(L, SizeOf(THLPRecordHeader)); Inc(L, R.Size); F^.Seek(L); OK:=(F^.Status=stOK); end
  438. end;
  439. OK:=OK and (TopicsRead=true);
  440. if OK=false then Fail;
  441. end;
  442. function TOAHelpFile.LoadIndex: boolean;
  443. begin
  444. LoadIndex:=ReadIndexTable;
  445. end;
  446. function TOAHelpFile.ReadHeader: boolean;
  447. var S: string;
  448. P: longint;
  449. R: TRecord;
  450. OK: boolean;
  451. begin
  452. F^.Seek(0);
  453. F^.Read(S[1],128); S[0]:=#255;
  454. OK:=(F^.Status=stOK); P:=Pos(Signature,S);
  455. OK:=OK and (P>0);
  456. if OK then
  457. begin
  458. F^.Seek(P+length(Signature)-1);
  459. F^.Read(Version,SizeOf(Version));
  460. OK:=(F^.Status=stOK) and (Version.FormatVersion>=MinFormatVersion);
  461. if OK then OK:=ReadRecord(R,true);
  462. OK:=OK and (R.SClass=rtFileHeader) and (R.Size=SizeOf(Header));
  463. if OK then Move(R.Data^,Header,SizeOf(Header));
  464. DisposeRecord(R);
  465. end;
  466. ReadHeader:=OK;
  467. end;
  468. function TOAHelpFile.ReadTopics: boolean;
  469. var OK: boolean;
  470. R: TRecord;
  471. L,I: longint;
  472. function GetCtxPos(C: THLPContextPos): longint;
  473. begin
  474. GetCtxPos:=longint(C.HiB) shl 16 + C.LoW;
  475. end;
  476. begin
  477. OK:=ReadRecord(R, true);
  478. if OK then
  479. with THLPContexts(R.Data^) do
  480. for I:=1 to longint(ContextCount)-1 do
  481. begin
  482. if Topics^.Count=MaxCollectionSize then Break;
  483. L:=GetCtxPos(Contexts[I]);
  484. if (L and $800000)<>0 then L:=not L;
  485. if (L=-1) and (Header.MainIndexScreen>0) then
  486. L:=GetCtxPos(Contexts[Header.MainIndexScreen]);
  487. if (L>0) then
  488. Topics^.Insert(NewTopic(ID,I,L,''));
  489. end;
  490. DisposeRecord(R);
  491. TopicsRead:=OK;
  492. ReadTopics:=OK;
  493. end;
  494. function TOAHelpFile.ReadIndexTable: boolean;
  495. var OK: boolean;
  496. R: TRecord;
  497. I: longint;
  498. LastTag,S: string;
  499. CurPtr: sw_word;
  500. HelpCtx: THelpCtx;
  501. LenCode,CopyCnt,AddLen: byte;
  502. type pword = ^word;
  503. begin
  504. if IndexTableRead then OK:=true else
  505. begin
  506. LastTag:=''; CurPtr:=0;
  507. OK:=(IndexTablePos<>0);
  508. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  509. if OK then OK:=ReadRecord(R, true);
  510. if OK then
  511. with THLPIndexTable(R.Data^) do
  512. for I:=0 to IndexCount-1 do
  513. begin
  514. LenCode:=PByteArray(@Entries)^[CurPtr];
  515. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  516. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  517. LastTag:=copy(LastTag,1,CopyCnt)+S;
  518. HelpCtx:=PWord(@PByteArray(@Entries)^[CurPtr+1+AddLen])^;
  519. IndexEntries^.Insert(NewIndexEntry(LastTag,ID,HelpCtx));
  520. Inc(CurPtr,1+AddLen+2);
  521. end;
  522. DisposeRecord(R);
  523. IndexTableRead:=OK;
  524. end;
  525. ReadIndexTable:=OK;
  526. end;
  527. function TOAHelpFile.ReadCompression: boolean;
  528. var OK: boolean;
  529. R: TRecord;
  530. begin
  531. OK:=ReadRecord(R, true);
  532. OK:=OK and (R.Size=SizeOf(THLPCompression));
  533. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  534. DisposeRecord(R);
  535. CompressionRead:=OK;
  536. ReadCompression:=OK;
  537. end;
  538. function TOAHelpFile.ReadIndexTags: boolean;
  539. var OK: boolean;
  540. begin
  541. OK:={ReadRecord(R, true)}true;
  542. IndexTagsRead:=OK;
  543. ReadIndexTags:=OK;
  544. end;
  545. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  546. var OK: boolean;
  547. H: THLPRecordHeader;
  548. begin
  549. FillChar(R, SizeOf(R), 0);
  550. F^.Read(H,SizeOf(H));
  551. OK:=F^.Status=stOK;
  552. if OK then
  553. begin
  554. R.SClass:=H.RecType; R.Size:=H.RecLength;
  555. if (R.Size>0) and ReadData then
  556. begin
  557. GetMem(R.Data,R.Size);
  558. F^.Read(R.Data^,R.Size);
  559. OK:=F^.Status=stOK;
  560. end;
  561. if OK=false then DisposeRecord(R);
  562. end;
  563. ReadRecord:=OK;
  564. end;
  565. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  566. var SrcPtr,DestPtr,TopicSize: sw_word;
  567. NewR: TRecord;
  568. function ExtractTextRec(var R: TRecord): boolean;
  569. function GetNextNibble: byte;
  570. var B,N: byte;
  571. begin
  572. B:=PByteArray(R.Data)^[SrcPtr div 2];
  573. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  574. Inc(SrcPtr);
  575. GetNextNibble:=N;
  576. end;
  577. procedure AddChar(C: char);
  578. begin
  579. if Assigned(NewR.Data) then
  580. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  581. Inc(DestPtr);
  582. end;
  583. var OK: boolean;
  584. C: char;
  585. P: pointer;
  586. function GetNextChar: char;
  587. var C: char;
  588. I,N,Cnt: byte;
  589. begin
  590. N:=GetNextNibble;
  591. case N of
  592. $00 : C:=#0;
  593. $01..$0D : C:=chr(Compression.CharTable[N]);
  594. ncRawChar : begin
  595. I:=GetNextNibble;
  596. C:=chr(I+GetNextNibble shl 4);
  597. end;
  598. ncRepChar : begin
  599. Cnt:=2+GetNextNibble;
  600. C:=GetNextChar{$ifdef FPC}(){$endif};
  601. for I:=1 to Cnt-1 do AddChar(C);
  602. end;
  603. end;
  604. GetNextChar:=C;
  605. end;
  606. begin
  607. OK:=Compression.CompType in[ctNone,ctNibble];
  608. if OK then
  609. case Compression.CompType of
  610. ctNone : ;
  611. ctNibble :
  612. begin
  613. NewR.SClass:=0;
  614. NewR.Size:=0;
  615. NewR.Data:=nil;
  616. SrcPtr:=0; DestPtr:=0;
  617. while SrcPtr<(R.Size*2) do
  618. begin
  619. C:=GetNextChar;
  620. AddChar(C);
  621. end;
  622. TopicSize:=DestPtr;
  623. NewR.SClass:=R.SClass;
  624. NewR.Size:=Min(MaxHelpTopicSize,TopicSize);
  625. GetMem(NewR.Data, NewR.Size);
  626. SrcPtr:=0; DestPtr:=0;
  627. while SrcPtr<(R.Size*2) do
  628. begin
  629. C:=GetNextChar;
  630. AddChar(C);
  631. end;
  632. DisposeRecord(R); R:=NewR;
  633. if (R.Size>DestPtr) then
  634. begin
  635. P:=R.Data; GetMem(R.Data,DestPtr);
  636. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  637. end;
  638. end;
  639. else OK:=false;
  640. end;
  641. ExtractTextRec:=OK;
  642. end;
  643. var OK: boolean;
  644. TextR,KeyWR: TRecord;
  645. I: sw_word;
  646. begin
  647. OK:=T<>nil;
  648. if OK and (T^.Text=nil) then
  649. begin
  650. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  651. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  652. if OK then OK:=ReadRecord(TextR,true);
  653. OK:=OK and (TextR.SClass=rtText);
  654. if OK then OK:=ReadRecord(KeyWR,true);
  655. OK:=OK and (KeyWR.SClass=rtKeyword);
  656. if OK then OK:=ExtractTextRec(TextR);
  657. if OK then
  658. begin
  659. if TextR.Size>0 then
  660. begin
  661. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  662. TextR.Data:=nil; TextR.Size:=0;
  663. end;
  664. with THLPKeywordRecord(KeyWR.Data^) do
  665. begin
  666. T^.LinkCount:=KeywordCount;
  667. GetMem(T^.Links,T^.LinkSize);
  668. if KeywordCount>0 then
  669. for I:=0 to KeywordCount-1 do
  670. begin
  671. T^.Links^[I].Context:=Keywords[I].KwContext;
  672. T^.Links^[I].FileID:=ID;
  673. end;
  674. end;
  675. end;
  676. DisposeRecord(TextR); DisposeRecord(KeyWR);
  677. end;
  678. ReadTopic:=OK;
  679. end;
  680. destructor TOAHelpFile.Done;
  681. begin
  682. if F<>nil then Dispose(F, Done);
  683. inherited Done;
  684. end;
  685. constructor THelpFacility.Init;
  686. begin
  687. inherited Init;
  688. New(HelpFiles, Init(10,10));
  689. IndexTabSize:=40;
  690. end;
  691. function THelpFacility.AddOAHelpFile(FileName: string): boolean;
  692. var H: PHelpFile;
  693. begin
  694. H:=New(POAHelpFile, Init(FileName, LastID+1));
  695. AddOAHelpFile:=AddFile(H);
  696. end;
  697. function THelpFacility.AddHTMLHelpFile(FileName, TOCEntry: string): boolean;
  698. var H: PHelpFile;
  699. begin
  700. H:=New(PHTMLHelpFile, Init(FileName, LastID+1, TOCEntry));
  701. AddHTMLHelpFile:=AddFile(H);;
  702. end;
  703. function THelpFacility.AddFile(H: PHelpFile): boolean;
  704. begin
  705. if H<>nil then
  706. begin
  707. HelpFiles^.Insert(H);
  708. Inc(LastID);
  709. end;
  710. AddFile:=H<>nil;
  711. end;
  712. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  713. var P: PTopic;
  714. HelpFile: PHelpFile;
  715. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  716. begin
  717. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  718. Search:=P<>nil;
  719. end;
  720. begin
  721. HelpFile:=nil;
  722. if SourceFileID=0 then P:=nil else
  723. begin
  724. HelpFile:=SearchFile(SourceFileID);
  725. P:=SearchTopicInHelpFile(HelpFile,Context);
  726. end;
  727. if P=nil then HelpFiles^.FirstThat(@Search);
  728. if P=nil then HelpFile:=nil;
  729. SearchTopicOwner:=HelpFile;
  730. end;
  731. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  732. var P: PTopic;
  733. H: PHelpFile;
  734. begin
  735. if (SourceFileID=0) and (Context=0) then
  736. P:=BuildIndexTopic else
  737. begin
  738. H:=SearchTopicOwner(SourceFileID,Context);
  739. if (H=nil) then P:=nil else
  740. P:=H^.LoadTopic(Context);
  741. end;
  742. LoadTopic:=P;
  743. end;
  744. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  745. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  746. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  747. begin
  748. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  749. end;
  750. var P: PIndexEntry;
  751. begin
  752. H^.LoadIndex;
  753. P:=H^.IndexEntries^.FirstThat(@Search);
  754. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  755. ScanHelpFile:=P<>nil;
  756. end;
  757. begin
  758. Keyword:=UpcaseStr(Keyword);
  759. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  760. end;
  761. function THelpFacility.BuildIndexTopic: PTopic;
  762. var T: PTopic;
  763. Keywords: PIndexEntryCollection;
  764. Lines: PUnsortedStringCollection;
  765. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  766. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  767. begin
  768. Keywords^.Insert(P);
  769. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  770. end;
  771. begin
  772. H^.LoadIndex;
  773. if Keywords^.Count<MaxCollectionSize then
  774. H^.IndexEntries^.FirstThat(@InsertKeywords);
  775. end;
  776. procedure AddLine(S: string);
  777. begin
  778. if S='' then S:=' ';
  779. Lines^.Insert(NewStr(S));
  780. end;
  781. procedure RenderTopic;
  782. var Size,CurPtr,I: sw_word;
  783. S: string;
  784. function CountSize(P: PString): boolean; {$ifndef FPC}far;{$endif}
  785. begin Inc(Size, length(P^)+1); CountSize:=Size>MaxHelpTopicSize-300; end;
  786. begin
  787. Size:=0; Lines^.FirstThat(@CountSize);
  788. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  789. CurPtr:=0;
  790. for I:=0 to Lines^.Count-1 do
  791. begin
  792. S:=Lines^.At(I)^;
  793. Size:=length(S)+1; S[Size]:=hscLineBreak;
  794. Move(S[1],PByteArray(T^.Text)^[CurPtr],Size);
  795. Inc(CurPtr,Size);
  796. if CurPtr>=T^.TextSize then Break;
  797. end;
  798. end;
  799. var Line: string;
  800. procedure FlushLine;
  801. begin
  802. if Line<>'' then AddLine(Line); Line:='';
  803. end;
  804. var KWCount,NLFlag: sw_integer;
  805. LastFirstChar: char;
  806. procedure NewSection(FirstChar: char);
  807. begin
  808. if FirstChar<=#64 then FirstChar:=#32;
  809. FlushLine;
  810. AddLine('');
  811. AddLine(FirstChar);
  812. AddLine('');
  813. LastFirstChar:=FirstChar;
  814. NLFlag:=0;
  815. end;
  816. procedure AddKeyword(KWS: string);
  817. begin
  818. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  819. if (KWCount=1) or
  820. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  821. NewSection(Upcase(KWS[1]));
  822. if (NLFlag mod 2)=0
  823. then Line:=' '+#2+KWS+#2
  824. else begin
  825. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  826. FlushLine;
  827. end;
  828. Inc(NLFlag);
  829. end;
  830. var KW: PIndexEntry;
  831. I: sw_integer;
  832. begin
  833. New(Keywords, Init(5000,1000));
  834. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  835. New(Lines, Init((Keywords^.Count div 2)+100,100));
  836. T:=NewTopic(0,0,0,'');
  837. if HelpFiles^.Count=0 then
  838. begin
  839. AddLine('');
  840. AddLine(' No help files installed.')
  841. end else
  842. begin
  843. AddLine(' Help index');
  844. KWCount:=0; Line:='';
  845. T^.LinkCount:=Keywords^.Count;
  846. GetMem(T^.Links,T^.LinkSize);
  847. for I:=0 to Keywords^.Count-1 do
  848. begin
  849. KW:=Keywords^.At(I);
  850. AddKeyword(KW^.Tag^);
  851. T^.Links^[I].Context:=longint(KW^.HelpCtx); T^.Links^[I].FileID:=KW^.FileID;
  852. end;
  853. FlushLine;
  854. AddLine('');
  855. end;
  856. RenderTopic;
  857. Dispose(Lines, Done);
  858. Keywords^.DeleteAll; Dispose(Keywords, Done);
  859. BuildIndexTopic:=T;
  860. end;
  861. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  862. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  863. begin
  864. Match:=(P^.ID=ID);
  865. end;
  866. begin
  867. SearchFile:=HelpFiles^.FirstThat(@Match);
  868. end;
  869. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  870. var P: PTopic;
  871. begin
  872. if F=nil then P:=nil else
  873. P:=F^.SearchTopic(Context);
  874. SearchTopicInHelpFile:=P;
  875. end;
  876. destructor THelpFacility.Done;
  877. begin
  878. inherited Done;
  879. Dispose(HelpFiles, Done);
  880. end;
  881. END.
  882. {
  883. $Log$
  884. Revision 1.17 2000-02-07 11:47:25 pierre
  885. * Remove 64Kb limitation for FPC by Gabor
  886. Revision 1.16 2000/01/03 14:59:03 marco
  887. * Fixed Linux code that got time of day. Removed Timezone parameter
  888. Revision 1.15 1999/08/16 18:25:29 peter
  889. * Adjusting the selection when the editor didn't contain any line.
  890. * Reserved word recognition redesigned, but this didn't affect the overall
  891. syntax highlight speed remarkably (at least not on my Amd-K6/350).
  892. The syntax scanner loop is a bit slow but the main problem is the
  893. recognition of special symbols. Switching off symbol processing boosts
  894. the performance up to ca. 200%...
  895. * The editor didn't allow copying (for ex to clipboard) of a single character
  896. * 'File|Save as' caused permanently run-time error 3. Not any more now...
  897. * Compiler Messages window (actually the whole desktop) did not act on any
  898. keypress when compilation failed and thus the window remained visible
  899. + Message windows are now closed upon pressing Esc
  900. + At 'Run' the IDE checks whether any sources are modified, and recompiles
  901. only when neccessary
  902. + BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
  903. + LineSelect (Ctrl+K+L) implemented
  904. * The IDE had problems closing help windows before saving the desktop
  905. Revision 1.14 1999/07/18 16:26:42 florian
  906. * IDE compiles with for Win32 and basic things are working
  907. Revision 1.13 1999/04/13 10:47:51 daniel
  908. * Fixed for Linux
  909. Revision 1.12 1999/04/07 21:56:00 peter
  910. + object support for browser
  911. * html help fixes
  912. * more desktop saving things
  913. * NODEBUG directive to exclude debugger
  914. Revision 1.11 1999/03/16 12:38:16 peter
  915. * tools macro fixes
  916. + tph writer
  917. + first things for resource files
  918. Revision 1.10 1999/03/08 14:58:19 peter
  919. + prompt with dialogs for tools
  920. Revision 1.9 1999/03/03 16:44:05 pierre
  921. * TPH reader fix from Peter
  922. Revision 1.8 1999/03/01 15:42:11 peter
  923. + Added dummy entries for functions not yet implemented
  924. * MenuBar didn't update itself automatically on command-set changes
  925. * Fixed Debugging/Profiling options dialog
  926. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  927. set
  928. * efBackSpaceUnindents works correctly
  929. + 'Messages' window implemented
  930. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  931. + Added TP message-filter support (for ex. you can call GREP thru
  932. GREP2MSG and view the result in the messages window - just like in TP)
  933. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  934. so topic search didn't work...
  935. * In FPHELP.PAS there were still context-variables defined as word instead
  936. of THelpCtx
  937. * StdStatusKeys() was missing from the statusdef for help windows
  938. + Topic-title for index-table can be specified when adding a HTML-files
  939. Revision 1.6 1999/02/20 15:18:35 peter
  940. + ctrl-c capture with confirm dialog
  941. + ascii table in the tools menu
  942. + heapviewer
  943. * empty file fixed
  944. * fixed callback routines in fpdebug to have far for tp7
  945. Revision 1.5 1999/02/19 15:43:22 peter
  946. * compatibility fixes for FV
  947. Revision 1.4 1999/02/18 13:44:37 peter
  948. * search fixed
  949. + backward search
  950. * help fixes
  951. * browser updates
  952. Revision 1.3 1999/02/08 10:37:46 peter
  953. + html helpviewer
  954. Revision 1.2 1998/12/28 15:47:56 peter
  955. + Added user screen support, display & window
  956. + Implemented Editor,Mouse Options dialog
  957. + Added location of .INI and .CFG file
  958. + Option (INI) file managment implemented (see bottom of Options Menu)
  959. + Switches updated
  960. + Run program
  961. Revision 1.4 1998/12/22 10:39:55 peter
  962. + options are now written/read
  963. + find and replace routines
  964. }