whelp.pas 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  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 : word;
  109. Text : PByteArray;
  110. LinkCount : word;
  111. Links : PKeywordDescriptors;
  112. LastAccess : longint;
  113. FileID : word;
  114. Param : PString;
  115. function LinkSize: 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 = 65520;
  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,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: 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 T:=nil;
  357. if (T<>nil) and (T^.Text=nil) then T:=nil;
  358. end;
  359. if T<>nil then
  360. begin T^.LastAccess:=GetDosTicks; T:=CloneTopic(T); end;
  361. LoadTopic:=T;
  362. end;
  363. function THelpFile.LoadIndex: boolean;
  364. begin
  365. Abstract;
  366. LoadIndex:=false; { remove warning }
  367. end;
  368. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  369. var T: PTopic;
  370. begin
  371. T:=Topics^.SearchTopic(HelpCtx);
  372. SearchTopic:=T;
  373. end;
  374. function THelpFile.ReadTopic(T: PTopic): boolean;
  375. begin
  376. Abstract;
  377. ReadTopic:=false; { remove warning }
  378. end;
  379. procedure THelpFile.MaintainTopicCache;
  380. var Count: sw_integer;
  381. MinP: PTopic;
  382. MinLRU: longint;
  383. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  384. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  385. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  386. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; MinP:=P; end; end;
  387. var P: PTopic;
  388. begin
  389. Count:=0; Topics^.ForEach(@CountThem);
  390. if (Count>=TopicCacheSize) then
  391. begin
  392. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  393. if P<>nil then
  394. begin
  395. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  396. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  397. end;
  398. end;
  399. end;
  400. destructor THelpFile.Done;
  401. begin
  402. if Topics<>nil then Dispose(Topics, Done);
  403. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  404. inherited Done;
  405. end;
  406. constructor TOAHelpFile.Init(AFileName: string; AID: word);
  407. var OK: boolean;
  408. FS,L: longint;
  409. R: TRecord;
  410. begin
  411. inherited Init(AID);
  412. F:=New(PBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  413. OK:=F<>nil;
  414. if OK then OK:=(F^.Status=stOK);
  415. if OK then
  416. begin
  417. FS:=F^.GetSize;
  418. OK:=ReadHeader;
  419. end;
  420. while OK do
  421. begin
  422. L:=F^.GetPos;
  423. if (L>=FS) then Break;
  424. OK:=ReadRecord(R,false);
  425. if (OK=false) or (R.SClass=0) or (R.Size=0) then Break;
  426. case R.SClass of
  427. rtContext : begin F^.Seek(L); OK:=ReadTopics; end;
  428. rtText : {Skip};
  429. rtKeyword : {Skip};
  430. rtIndex : begin IndexTablePos:=L; {OK:=ReadIndexTable; }end;
  431. rtCompression : begin F^.Seek(L); OK:=ReadCompression; end;
  432. rtIndexTags : begin IndexTagsPos:=L; {OK:=ReadIndexTags; }end;
  433. else {Skip};
  434. end;
  435. if OK then
  436. begin Inc(L, SizeOf(THLPRecordHeader)); Inc(L, R.Size); F^.Seek(L); OK:=(F^.Status=stOK); end
  437. end;
  438. OK:=OK and (TopicsRead=true);
  439. if OK=false then Fail;
  440. end;
  441. function TOAHelpFile.LoadIndex: boolean;
  442. begin
  443. LoadIndex:=ReadIndexTable;
  444. end;
  445. function TOAHelpFile.ReadHeader: boolean;
  446. var S: string;
  447. P: longint;
  448. R: TRecord;
  449. OK: boolean;
  450. begin
  451. F^.Seek(0);
  452. F^.Read(S[1],128); S[0]:=#255;
  453. OK:=(F^.Status=stOK); P:=Pos(Signature,S);
  454. OK:=OK and (P>0);
  455. if OK then
  456. begin
  457. F^.Seek(P+length(Signature)-1);
  458. F^.Read(Version,SizeOf(Version));
  459. OK:=(F^.Status=stOK) and (Version.FormatVersion>=MinFormatVersion);
  460. if OK then OK:=ReadRecord(R,true);
  461. OK:=OK and (R.SClass=rtFileHeader) and (R.Size=SizeOf(Header));
  462. if OK then Move(R.Data^,Header,SizeOf(Header));
  463. DisposeRecord(R);
  464. end;
  465. ReadHeader:=OK;
  466. end;
  467. function TOAHelpFile.ReadTopics: boolean;
  468. var OK: boolean;
  469. R: TRecord;
  470. L,I: longint;
  471. function GetCtxPos(C: THLPContextPos): longint;
  472. begin
  473. GetCtxPos:=longint(C.HiB) shl 16 + C.LoW;
  474. end;
  475. begin
  476. OK:=ReadRecord(R, true);
  477. if OK then
  478. with THLPContexts(R.Data^) do
  479. for I:=1 to longint(ContextCount)-1 do
  480. begin
  481. if Topics^.Count=MaxCollectionSize then Break;
  482. L:=GetCtxPos(Contexts[I]);
  483. if (L and $800000)<>0 then L:=not L;
  484. if (L=-1) and (Header.MainIndexScreen>0) then
  485. L:=GetCtxPos(Contexts[Header.MainIndexScreen]);
  486. if (L>0) then
  487. Topics^.Insert(NewTopic(ID,I,L,''));
  488. end;
  489. DisposeRecord(R);
  490. TopicsRead:=OK;
  491. ReadTopics:=OK;
  492. end;
  493. function TOAHelpFile.ReadIndexTable: boolean;
  494. var OK: boolean;
  495. R: TRecord;
  496. I: longint;
  497. LastTag,S: string;
  498. CurPtr,HelpCtx: word;
  499. LenCode,CopyCnt,AddLen: byte;
  500. begin
  501. if IndexTableRead then OK:=true else
  502. begin
  503. LastTag:=''; CurPtr:=0;
  504. OK:=(IndexTablePos<>0);
  505. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  506. if OK then OK:=ReadRecord(R, true);
  507. if OK then
  508. with THLPIndexTable(R.Data^) do
  509. for I:=0 to IndexCount-1 do
  510. begin
  511. LenCode:=PByteArray(@Entries)^[CurPtr];
  512. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  513. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  514. LastTag:=copy(LastTag,1,CopyCnt)+S;
  515. Move(PByteArray(@Entries)^[CurPtr+1+AddLen],HelpCtx,2);
  516. IndexEntries^.Insert(NewIndexEntry(LastTag,ID,HelpCtx));
  517. Inc(CurPtr,1+AddLen+2);
  518. end;
  519. DisposeRecord(R);
  520. IndexTableRead:=OK;
  521. end;
  522. ReadIndexTable:=OK;
  523. end;
  524. function TOAHelpFile.ReadCompression: boolean;
  525. var OK: boolean;
  526. R: TRecord;
  527. begin
  528. OK:=ReadRecord(R, true);
  529. OK:=OK and (R.Size=SizeOf(THLPCompression));
  530. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  531. DisposeRecord(R);
  532. CompressionRead:=OK;
  533. ReadCompression:=OK;
  534. end;
  535. function TOAHelpFile.ReadIndexTags: boolean;
  536. var OK: boolean;
  537. begin
  538. OK:={ReadRecord(R, true)}true;
  539. IndexTagsRead:=OK;
  540. ReadIndexTags:=OK;
  541. end;
  542. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  543. var OK: boolean;
  544. H: THLPRecordHeader;
  545. begin
  546. FillChar(R, SizeOf(R), 0);
  547. F^.Read(H,SizeOf(H));
  548. OK:=F^.Status=stOK;
  549. if OK then
  550. begin
  551. R.SClass:=H.RecType; R.Size:=H.RecLength;
  552. if (R.Size>0) and ReadData then
  553. begin
  554. GetMem(R.Data,R.Size);
  555. F^.Read(R.Data^,R.Size);
  556. OK:=F^.Status=stOK;
  557. end;
  558. if OK=false then DisposeRecord(R);
  559. end;
  560. ReadRecord:=OK;
  561. end;
  562. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  563. var SrcPtr,DestPtr: word;
  564. NewR: TRecord;
  565. function ExtractTextRec(var R: TRecord): boolean;
  566. function GetNextNibble: byte;
  567. var B,N: byte;
  568. begin
  569. B:=PByteArray(R.Data)^[SrcPtr div 2];
  570. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  571. Inc(SrcPtr);
  572. GetNextNibble:=N;
  573. end;
  574. procedure AddChar(C: char);
  575. begin
  576. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  577. Inc(DestPtr);
  578. end;
  579. var OK: boolean;
  580. C: char;
  581. P: pointer;
  582. function GetNextChar: char;
  583. var C: char;
  584. I,N,Cnt: byte;
  585. begin
  586. N:=GetNextNibble;
  587. case N of
  588. $00 : C:=#0;
  589. $01..$0D : C:=chr(Compression.CharTable[N]);
  590. ncRawChar : begin
  591. I:=GetNextNibble;
  592. C:=chr(I+GetNextNibble shl 4);
  593. end;
  594. ncRepChar : begin
  595. Cnt:=2+GetNextNibble;
  596. C:=GetNextChar{$ifdef FPC}(){$endif};
  597. for I:=1 to Cnt-1 do AddChar(C);
  598. end;
  599. end;
  600. GetNextChar:=C;
  601. end;
  602. begin
  603. OK:=Compression.CompType in[ctNone,ctNibble];
  604. if OK then
  605. case Compression.CompType of
  606. ctNone : ;
  607. ctNibble :
  608. begin
  609. NewR.SClass:=R.SClass;
  610. NewR.Size:=MaxHelpTopicSize; { R.Size*2 <- bug fixed, i didn't care of RLL codings }
  611. GetMem(NewR.Data, NewR.Size);
  612. SrcPtr:=0; DestPtr:=0;
  613. while SrcPtr<(R.Size*2) do
  614. begin
  615. C:=GetNextChar;
  616. AddChar(C);
  617. end;
  618. DisposeRecord(R); R:=NewR;
  619. if (R.Size>DestPtr) then
  620. begin
  621. P:=R.Data; GetMem(R.Data,DestPtr);
  622. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  623. end;
  624. end;
  625. else OK:=false;
  626. end;
  627. ExtractTextRec:=OK;
  628. end;
  629. var OK: boolean;
  630. TextR,KeyWR: TRecord;
  631. I: word;
  632. begin
  633. OK:=T<>nil;
  634. if OK and (T^.Text=nil) then
  635. begin
  636. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  637. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  638. if OK then OK:=ReadRecord(TextR,true);
  639. OK:=OK and (TextR.SClass=rtText);
  640. if OK then OK:=ReadRecord(KeyWR,true);
  641. OK:=OK and (KeyWR.SClass=rtKeyword);
  642. if OK then OK:=ExtractTextRec(TextR);
  643. if OK then
  644. begin
  645. if TextR.Size>0 then
  646. begin
  647. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  648. TextR.Data:=nil; TextR.Size:=0;
  649. end;
  650. with THLPKeywordRecord(KeyWR.Data^) do
  651. begin
  652. T^.LinkCount:=KeywordCount;
  653. GetMem(T^.Links,T^.LinkSize);
  654. if KeywordCount>0 then
  655. for I:=0 to KeywordCount-1 do
  656. begin
  657. T^.Links^[I].Context:=Keywords[I].KwContext;
  658. T^.Links^[I].FileID:=ID;
  659. end;
  660. end;
  661. end;
  662. DisposeRecord(TextR); DisposeRecord(KeyWR);
  663. end;
  664. ReadTopic:=OK;
  665. end;
  666. destructor TOAHelpFile.Done;
  667. begin
  668. if F<>nil then Dispose(F, Done);
  669. inherited Done;
  670. end;
  671. constructor THelpFacility.Init;
  672. begin
  673. inherited Init;
  674. New(HelpFiles, Init(10,10));
  675. IndexTabSize:=40;
  676. end;
  677. function THelpFacility.AddOAHelpFile(FileName: string): boolean;
  678. var H: PHelpFile;
  679. begin
  680. H:=New(POAHelpFile, Init(FileName, LastID+1));
  681. AddOAHelpFile:=AddFile(H);
  682. end;
  683. function THelpFacility.AddHTMLHelpFile(FileName, TOCEntry: string): boolean;
  684. var H: PHelpFile;
  685. begin
  686. H:=New(PHTMLHelpFile, Init(FileName, LastID+1, TOCEntry));
  687. AddHTMLHelpFile:=AddFile(H);;
  688. end;
  689. function THelpFacility.AddFile(H: PHelpFile): boolean;
  690. begin
  691. if H<>nil then
  692. begin
  693. HelpFiles^.Insert(H);
  694. Inc(LastID);
  695. end;
  696. AddFile:=H<>nil;
  697. end;
  698. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  699. var P: PTopic;
  700. HelpFile: PHelpFile;
  701. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  702. begin
  703. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  704. Search:=P<>nil;
  705. end;
  706. begin
  707. HelpFile:=nil;
  708. if SourceFileID=0 then P:=nil else
  709. begin
  710. HelpFile:=SearchFile(SourceFileID);
  711. P:=SearchTopicInHelpFile(HelpFile,Context);
  712. end;
  713. if P=nil then HelpFiles^.FirstThat(@Search);
  714. if P=nil then HelpFile:=nil;
  715. SearchTopicOwner:=HelpFile;
  716. end;
  717. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  718. var P: PTopic;
  719. H: PHelpFile;
  720. begin
  721. if (SourceFileID=0) and (Context=0) then
  722. P:=BuildIndexTopic else
  723. begin
  724. H:=SearchTopicOwner(SourceFileID,Context);
  725. if (H=nil) then P:=nil else
  726. P:=H^.LoadTopic(Context);
  727. end;
  728. LoadTopic:=P;
  729. end;
  730. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  731. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  732. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  733. begin
  734. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  735. end;
  736. var P: PIndexEntry;
  737. begin
  738. H^.LoadIndex;
  739. P:=H^.IndexEntries^.FirstThat(@Search);
  740. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  741. ScanHelpFile:=P<>nil;
  742. end;
  743. begin
  744. Keyword:=UpcaseStr(Keyword);
  745. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  746. end;
  747. function THelpFacility.BuildIndexTopic: PTopic;
  748. var T: PTopic;
  749. Keywords: PIndexEntryCollection;
  750. Lines: PUnsortedStringCollection;
  751. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  752. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  753. begin
  754. Keywords^.Insert(P);
  755. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  756. end;
  757. begin
  758. H^.LoadIndex;
  759. if Keywords^.Count<MaxCollectionSize then
  760. H^.IndexEntries^.FirstThat(@InsertKeywords);
  761. end;
  762. procedure AddLine(S: string);
  763. begin
  764. if S='' then S:=' ';
  765. Lines^.Insert(NewStr(S));
  766. end;
  767. procedure RenderTopic;
  768. var Size,CurPtr,I: word;
  769. S: string;
  770. function CountSize(P: PString): boolean; {$ifndef FPC}far;{$endif} begin Inc(Size, length(P^)+1); CountSize:=Size>65200; end;
  771. begin
  772. Size:=0; Lines^.FirstThat(@CountSize);
  773. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  774. CurPtr:=0;
  775. for I:=0 to Lines^.Count-1 do
  776. begin
  777. S:=Lines^.At(I)^;
  778. Size:=length(S)+1; S[Size]:=hscLineBreak;
  779. Move(S[1],PByteArray(T^.Text)^[CurPtr],Size);
  780. Inc(CurPtr,Size);
  781. if CurPtr>=T^.TextSize then Break;
  782. end;
  783. end;
  784. var Line: string;
  785. procedure FlushLine;
  786. begin
  787. if Line<>'' then AddLine(Line); Line:='';
  788. end;
  789. var KWCount,NLFlag: sw_integer;
  790. LastFirstChar: char;
  791. procedure NewSection(FirstChar: char);
  792. begin
  793. if FirstChar<=#64 then FirstChar:=#32;
  794. FlushLine;
  795. AddLine('');
  796. AddLine(FirstChar);
  797. AddLine('');
  798. LastFirstChar:=FirstChar;
  799. NLFlag:=0;
  800. end;
  801. procedure AddKeyword(KWS: string);
  802. begin
  803. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  804. if (KWCount=1) or
  805. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  806. NewSection(Upcase(KWS[1]));
  807. if (NLFlag mod 2)=0
  808. then Line:=' '+#2+KWS+#2
  809. else begin
  810. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  811. FlushLine;
  812. end;
  813. Inc(NLFlag);
  814. end;
  815. var KW: PIndexEntry;
  816. I: sw_integer;
  817. begin
  818. New(Keywords, Init(5000,1000));
  819. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  820. New(Lines, Init((Keywords^.Count div 2)+100,100));
  821. T:=NewTopic(0,0,0,'');
  822. if HelpFiles^.Count=0 then
  823. begin
  824. AddLine('');
  825. AddLine(' No help files installed.')
  826. end else
  827. begin
  828. AddLine(' Help index');
  829. KWCount:=0; Line:='';
  830. T^.LinkCount:=Keywords^.Count;
  831. GetMem(T^.Links,T^.LinkSize);
  832. for I:=0 to Keywords^.Count-1 do
  833. begin
  834. KW:=Keywords^.At(I);
  835. AddKeyword(KW^.Tag^);
  836. T^.Links^[I].Context:=KW^.HelpCtx; T^.Links^[I].FileID:=KW^.FileID;
  837. end;
  838. FlushLine;
  839. AddLine('');
  840. end;
  841. RenderTopic;
  842. Dispose(Lines, Done);
  843. Keywords^.DeleteAll; Dispose(Keywords, Done);
  844. BuildIndexTopic:=T;
  845. end;
  846. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  847. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  848. begin
  849. Match:=(P^.ID=ID);
  850. end;
  851. begin
  852. SearchFile:=HelpFiles^.FirstThat(@Match);
  853. end;
  854. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  855. var P: PTopic;
  856. begin
  857. if F=nil then P:=nil else
  858. P:=F^.SearchTopic(Context);
  859. SearchTopicInHelpFile:=P;
  860. end;
  861. destructor THelpFacility.Done;
  862. begin
  863. inherited Done;
  864. Dispose(HelpFiles, Done);
  865. end;
  866. END.
  867. {
  868. $Log$
  869. Revision 1.16 2000-01-03 14:59:03 marco
  870. * Fixed Linux code that got time of day. Removed Timezone parameter
  871. Revision 1.15 1999/08/16 18:25:29 peter
  872. * Adjusting the selection when the editor didn't contain any line.
  873. * Reserved word recognition redesigned, but this didn't affect the overall
  874. syntax highlight speed remarkably (at least not on my Amd-K6/350).
  875. The syntax scanner loop is a bit slow but the main problem is the
  876. recognition of special symbols. Switching off symbol processing boosts
  877. the performance up to ca. 200%...
  878. * The editor didn't allow copying (for ex to clipboard) of a single character
  879. * 'File|Save as' caused permanently run-time error 3. Not any more now...
  880. * Compiler Messages window (actually the whole desktop) did not act on any
  881. keypress when compilation failed and thus the window remained visible
  882. + Message windows are now closed upon pressing Esc
  883. + At 'Run' the IDE checks whether any sources are modified, and recompiles
  884. only when neccessary
  885. + BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
  886. + LineSelect (Ctrl+K+L) implemented
  887. * The IDE had problems closing help windows before saving the desktop
  888. Revision 1.14 1999/07/18 16:26:42 florian
  889. * IDE compiles with for Win32 and basic things are working
  890. Revision 1.13 1999/04/13 10:47:51 daniel
  891. * Fixed for Linux
  892. Revision 1.12 1999/04/07 21:56:00 peter
  893. + object support for browser
  894. * html help fixes
  895. * more desktop saving things
  896. * NODEBUG directive to exclude debugger
  897. Revision 1.11 1999/03/16 12:38:16 peter
  898. * tools macro fixes
  899. + tph writer
  900. + first things for resource files
  901. Revision 1.10 1999/03/08 14:58:19 peter
  902. + prompt with dialogs for tools
  903. Revision 1.9 1999/03/03 16:44:05 pierre
  904. * TPH reader fix from Peter
  905. Revision 1.8 1999/03/01 15:42:11 peter
  906. + Added dummy entries for functions not yet implemented
  907. * MenuBar didn't update itself automatically on command-set changes
  908. * Fixed Debugging/Profiling options dialog
  909. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  910. set
  911. * efBackSpaceUnindents works correctly
  912. + 'Messages' window implemented
  913. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  914. + Added TP message-filter support (for ex. you can call GREP thru
  915. GREP2MSG and view the result in the messages window - just like in TP)
  916. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  917. so topic search didn't work...
  918. * In FPHELP.PAS there were still context-variables defined as word instead
  919. of THelpCtx
  920. * StdStatusKeys() was missing from the statusdef for help windows
  921. + Topic-title for index-table can be specified when adding a HTML-files
  922. Revision 1.6 1999/02/20 15:18:35 peter
  923. + ctrl-c capture with confirm dialog
  924. + ascii table in the tools menu
  925. + heapviewer
  926. * empty file fixed
  927. * fixed callback routines in fpdebug to have far for tp7
  928. Revision 1.5 1999/02/19 15:43:22 peter
  929. * compatibility fixes for FV
  930. Revision 1.4 1999/02/18 13:44:37 peter
  931. * search fixed
  932. + backward search
  933. * help fixes
  934. * browser updates
  935. Revision 1.3 1999/02/08 10:37:46 peter
  936. + html helpviewer
  937. Revision 1.2 1998/12/28 15:47:56 peter
  938. + Added user screen support, display & window
  939. + Implemented Editor,Mouse Options dialog
  940. + Added location of .INI and .CFG file
  941. + Option (INI) file managment implemented (see bottom of Options Menu)
  942. + Switches updated
  943. + Run program
  944. Revision 1.4 1998/12/22 10:39:55 peter
  945. + options are now written/read
  946. + find and replace routines
  947. }