whelp.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991
  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
  16. {$ifdef Win32}
  17. { placed here to avoid TRect to be found in windows unit
  18. for win32 target whereas its found in objects unit for other targets PM }
  19. windows,
  20. {$endif Win32}
  21. Objects,
  22. WUtils;
  23. const
  24. hscLineBreak = #0;
  25. hscLink = #2;
  26. hscLineStart = #3;
  27. hscCode = #5;
  28. hscDirect = #6; { add the next char directly }
  29. hscCenter = #10;
  30. hscRight = #11;
  31. hscNamedMark = #12;
  32. hscTextAttr = #13;
  33. hscTextColor = #14;
  34. hscNormText = #15;
  35. hscInImage = #16;
  36. type
  37. THelpCtx = longint;
  38. TRecord = packed record
  39. SClass : word;
  40. Size : word;
  41. Data : pointer;
  42. end;
  43. PIndexEntry = ^TIndexEntry;
  44. TIndexEntry = packed record
  45. Tag : PString;
  46. HelpCtx : THelpCtx;
  47. FileID : word;
  48. end;
  49. PKeywordDescriptor = ^TKeywordDescriptor;
  50. TKeywordDescriptor = packed record
  51. FileID : word;
  52. Context : THelpCtx;
  53. end;
  54. PKeywordDescriptors = ^TKeywordDescriptors;
  55. TKeywordDescriptors = array[0..MaxBytes div sizeof(TKeywordDescriptor)-1] of TKeywordDescriptor;
  56. PTopic = ^TTopic;
  57. TTopic = object
  58. HelpCtx : THelpCtx;
  59. FileOfs : longint;
  60. TextSize : sw_word;
  61. Text : PByteArray;
  62. LinkCount : sw_word;
  63. Links : PKeywordDescriptors;
  64. LastAccess : longint;
  65. FileID : word;
  66. Param : PString;
  67. StartNamedMark: integer;
  68. NamedMarks : PUnsortedStringCollection;
  69. ExtData : pointer;
  70. ExtDataSize : longint;
  71. function LinkSize: sw_word;
  72. function GetNamedMarkIndex(const MarkName: string): sw_integer;
  73. end;
  74. PTopicCollection = ^TTopicCollection;
  75. TTopicCollection = object(TSortedCollection)
  76. function At(Index: sw_Integer): PTopic;
  77. procedure FreeItem(Item: Pointer); virtual;
  78. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  79. function SearchTopic(AHelpCtx: THelpCtx): PTopic;
  80. end;
  81. PIndexEntryCollection = ^TIndexEntryCollection;
  82. TIndexEntryCollection = object(TSortedCollection)
  83. function At(Index: Sw_Integer): PIndexEntry;
  84. procedure FreeItem(Item: Pointer); virtual;
  85. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  86. end;
  87. PUnsortedIndexEntryCollection = ^TUnsortedIndexEntryCollection;
  88. TUnsortedIndexEntryCollection = object(TCollection)
  89. function At(Index: Sw_Integer): PIndexEntry;
  90. procedure FreeItem(Item: Pointer); virtual;
  91. end;
  92. PHelpFile = ^THelpFile;
  93. THelpFile = object(TObject)
  94. ID : word;
  95. Topics : PTopicCollection;
  96. IndexEntries : PUnsortedIndexEntryCollection;
  97. constructor Init(AID: word);
  98. function LoadTopic(HelpCtx: THelpCtx): PTopic; virtual;
  99. procedure AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string;
  100. ExtData: pointer; ExtDataSize: longint);
  101. procedure AddIndexEntry(const Text: string; AHelpCtx: THelpCtx);
  102. destructor Done; virtual;
  103. public
  104. function LoadIndex: boolean; virtual;
  105. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  106. function ReadTopic(T: PTopic): boolean; virtual;
  107. private
  108. procedure MaintainTopicCache;
  109. end;
  110. PHelpFileCollection = PCollection;
  111. PHelpFacility = ^THelpFacility;
  112. THelpFacility = object(TObject)
  113. HelpFiles: PHelpFileCollection;
  114. IndexTabSize: sw_integer;
  115. constructor Init;
  116. function AddFile(const FileName, Param: string): PHelpFile;
  117. function AddHelpFile(H: PHelpFile): boolean;
  118. function LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
  119. function TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
  120. function BuildIndexTopic: PTopic; virtual;
  121. destructor Done; virtual;
  122. private
  123. LastID: word;
  124. function SearchFile(ID: byte): PHelpFile;
  125. function SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  126. function SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  127. end;
  128. THelpFileOpenProc = function(const FileName,Param: string;Index : longint): PHelpFile;
  129. PHelpFileType = ^THelpFileType;
  130. THelpFileType = record
  131. OpenProc : THelpFileOpenProc;
  132. end;
  133. const TopicCacheSize : sw_integer = 10;
  134. HelpStreamBufSize : sw_integer = 4096;
  135. HelpFacility : PHelpFacility = nil;
  136. MaxHelpTopicSize : sw_word = {$ifdef FPC}3*65520{$else}65520{$endif};
  137. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  138. ExtData: pointer; ExtDataSize: longint): PTopic;
  139. procedure DisposeTopic(P: PTopic);
  140. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  141. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  142. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  143. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  144. procedure DisposeIndexEntry(P: PIndexEntry);
  145. procedure DisposeRecord(var R: TRecord);
  146. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  147. function GetHelpFileTypeCount: integer;
  148. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  149. procedure DoneHelpFilesTypes;
  150. {$ifdef ENDIAN_BIG}
  151. Procedure SwapLong(var x : longint);
  152. Procedure SwapWord(var x : word);
  153. {$endif ENDIAN_BIG}
  154. implementation
  155. uses
  156. {$ifdef Unix}
  157. {$ifdef VER1_0}
  158. linux,
  159. {$else}
  160. baseunix,
  161. unix,
  162. {$endif}
  163. {$endif Unix}
  164. {$IFDEF OS2}
  165. DosCalls,
  166. {$ENDIF OS2}
  167. {$ifdef netwlibc}
  168. Libc,
  169. {$endif}
  170. Strings,
  171. WConsts;
  172. type
  173. PHelpFileTypeCollection = ^THelpFileTypeCollection;
  174. THelpFileTypeCollection = object(TCollection)
  175. function At(Index: sw_Integer): PHelpFileType;
  176. procedure FreeItem(Item: Pointer); virtual;
  177. end;
  178. const
  179. HelpFileTypes : PHelpFileTypeCollection = nil;
  180. {$ifdef ENDIAN_BIG}
  181. Procedure SwapLong(var x : longint);
  182. var
  183. y : word;
  184. z : word;
  185. Begin
  186. y := (x shr 16) and $FFFF;
  187. y := ((y shl 8) and $FFFF) or ((y shr 8) and $ff);
  188. z := x and $FFFF;
  189. z := ((z shl 8) and $FFFF) or ((z shr 8) and $ff);
  190. x := (longint(z) shl 16) or longint(y);
  191. End;
  192. Procedure SwapWord(var x : word);
  193. var
  194. z : byte;
  195. Begin
  196. z := (x shr 8) and $ff;
  197. x := x and $ff;
  198. x := (x shl 8);
  199. x := x or z;
  200. End;
  201. {$endif ENDIAN_BIG}
  202. function NewHelpFileType(AOpenProc: THelpFileOpenProc): PHelpFileType;
  203. var P: PHelpFileType;
  204. begin
  205. New(P);
  206. with P^ do begin OpenProc:=AOpenProc; end;
  207. NewHelpFileType:=P;
  208. end;
  209. procedure DisposeHelpFileType(P: PHelpFileType);
  210. begin
  211. if Assigned(P) then
  212. Dispose(P);
  213. end;
  214. procedure DoneHelpFilesTypes;
  215. begin
  216. if Assigned(HelpFileTypes) then
  217. Dispose(HelpFileTypes, Done);
  218. end;
  219. function THelpFileTypeCollection.At(Index: sw_Integer): PHelpFileType;
  220. begin
  221. At:=inherited At(Index);
  222. end;
  223. procedure THelpFileTypeCollection.FreeItem(Item: Pointer);
  224. begin
  225. if Assigned(Item) then
  226. DisposeHelpFileType(Item);
  227. end;
  228. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  229. begin
  230. if not Assigned(HelpFileTypes) then
  231. New(HelpFileTypes, Init(10,10));
  232. HelpFileTypes^.Insert(NewHelpFileType(AOpenProc));
  233. end;
  234. function GetHelpFileTypeCount: integer;
  235. var Count: integer;
  236. begin
  237. if not Assigned(HelpFileTypes) then
  238. Count:=0
  239. else
  240. Count:=HelpFileTypes^.Count;
  241. GetHelpFileTypeCount:=Count;
  242. end;
  243. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  244. begin
  245. HT:=HelpFileTypes^.At(Index)^;
  246. end;
  247. Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
  248. {$IFDEF OS2}
  249. const
  250. QSV_MS_COUNT = 14;
  251. var
  252. L: longint;
  253. begin
  254. DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, L, 4);
  255. GetDosTicks := L div 55;
  256. end;
  257. {$ENDIF}
  258. {$IFDEF Unix}
  259. var
  260. tv : TimeVal;
  261. tz : TimeZone;
  262. begin
  263. {$ifdef ver1_0}
  264. GetTimeOfDay(tv); {Timezone no longer used?}
  265. GetDosTicks:=((tv.Sec mod 86400) div 60)*1092+((tv.Sec mod 60)*1000000+tv.USec) div 54945;
  266. {$else}
  267. fpGetTimeOfDay(@tv,@tz);
  268. GetDosTicks:=((tv.tv_Sec mod 86400) div 60)*1092+((tv.tv_Sec mod 60)*1000000+tv.tv_USec) div 54945;
  269. {$endif}
  270. end;
  271. {$endif Unix}
  272. {$ifdef Win32}
  273. begin
  274. GetDosTicks:=(Windows.GetTickCount*5484) div 100;
  275. end;
  276. {$endif Win32}
  277. {$ifdef go32v2}
  278. begin
  279. GetDosTicks:=MemL[$40:$6c];
  280. end;
  281. {$endif go32v2}
  282. {$ifdef TP}
  283. begin
  284. GetDosTicks:=MemL[$40:$6c];
  285. end;
  286. {$endif go32v2}
  287. {$ifdef netwlibc}
  288. var
  289. tv : TTimeVal;
  290. tz : TTimeZone;
  291. begin
  292. fpGetTimeOfDay(tv,tz);
  293. GetDosTicks:=((tv.tv_sec mod 86400) div 60)*1092+((tv.tv_Sec mod 60)*1000000+tv.tv_USec) div 549
  294. end;
  295. {$endif}
  296. procedure DisposeRecord(var R: TRecord);
  297. begin
  298. with R do
  299. if (Size>0) and (Data<>nil) then FreeMem(Data, Size);
  300. FillChar(R, SizeOf(R), 0);
  301. end;
  302. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  303. ExtData: pointer; ExtDataSize: longint): PTopic;
  304. var P: PTopic;
  305. begin
  306. New(P); FillChar(P^,SizeOf(P^), 0);
  307. P^.HelpCtx:=HelpCtx; P^.FileOfs:=Pos; P^.FileID:=FileID;
  308. P^.Param:=NewStr(Param);
  309. if Assigned(ExtData) and (ExtDataSize>0) then
  310. begin
  311. P^.ExtDataSize:=ExtDataSize;
  312. GetMem(P^.ExtData,ExtDataSize);
  313. Move(ExtData^,P^.ExtData^,ExtDataSize);
  314. end;
  315. New(P^.NamedMarks, Init(100,100));
  316. NewTopic:=P;
  317. end;
  318. procedure DisposeTopic(P: PTopic);
  319. begin
  320. if P<>nil then
  321. begin
  322. if (P^.TextSize>0) and (P^.Text<>nil) then
  323. FreeMem(P^.Text,P^.TextSize);
  324. P^.Text:=nil;
  325. if {(P^.LinkCount>0) and }(P^.Links<>nil) then
  326. FreeMem(P^.Links,P^.LinkSize);
  327. P^.Links:=nil;
  328. if P^.Param<>nil then DisposeStr(P^.Param); P^.Param:=nil;
  329. if Assigned(P^.ExtData) then
  330. FreeMem(P^.ExtData{$ifndef FPC},P^.ExtDataSize{$endif});
  331. if Assigned(P^.NamedMarks) then Dispose(P^.NamedMarks, Done); P^.NamedMarks:=nil;
  332. Dispose(P);
  333. end;
  334. end;
  335. function CloneTopic(T: PTopic): PTopic;
  336. var NT: PTopic;
  337. procedure CloneMark(P: PString); {$ifndef FPC}far;{$endif}
  338. begin
  339. NT^.NamedMarks^.InsertStr(GetStr(P));
  340. end;
  341. begin
  342. New(NT);
  343. Move(T^,NT^,SizeOf(NT^));
  344. if NT^.Text<>nil then
  345. begin GetMem(NT^.Text,NT^.TextSize); Move(T^.Text^,NT^.Text^,NT^.TextSize); end;
  346. if NT^.Links<>nil then
  347. begin
  348. GetMem(NT^.Links,NT^.LinkSize);
  349. Move(T^.Links^,NT^.Links^,NT^.LinkSize);
  350. end;
  351. if NT^.Param<>nil then
  352. NT^.Param:=NewStr(T^.Param^);
  353. if Assigned(T^.NamedMarks) then
  354. begin
  355. New(NT^.NamedMarks, Init(T^.NamedMarks^.Count,10));
  356. T^.NamedMarks^.ForEach(@CloneMark);
  357. end;
  358. NT^.ExtDataSize:=T^.ExtDataSize;
  359. if Assigned(T^.ExtData) and (T^.ExtDataSize>0) then
  360. begin
  361. GetMem(NT^.ExtData,NT^.ExtDataSize);
  362. Move(T^.ExtData^,NT^.ExtData^,NT^.ExtDataSize);
  363. end;
  364. CloneTopic:=NT;
  365. end;
  366. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  367. var Size,CurPtr,I,MSize: sw_word;
  368. S: string;
  369. begin
  370. CurPtr:=0;
  371. for I:=0 to Lines^.Count-1 do
  372. begin
  373. S:=GetStr(Lines^.At(I));
  374. Size:=length(S)+1;
  375. Inc(CurPtr,Size);
  376. end;
  377. Size:=CurPtr;
  378. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  379. CurPtr:=0;
  380. for I:=0 to Lines^.Count-1 do
  381. begin
  382. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  383. if CurPtr+Size>=T^.TextSize then
  384. MSize:=T^.TextSize-CurPtr;
  385. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  386. if MSize<>Size then
  387. Break;
  388. Inc(CurPtr,Size);
  389. PByteArray(T^.Text)^[CurPtr]:=ord(hscLineBreak);
  390. Inc(CurPtr);
  391. if CurPtr>=T^.TextSize then Break;
  392. end;
  393. end;
  394. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  395. var Size,CurPtr,MSize: sw_word;
  396. I: sw_integer;
  397. S: string;
  398. begin
  399. CurPtr:=0;
  400. for I:=0 to Lines^.Count-1 do
  401. begin
  402. S:=GetStr(Lines^.At(I));
  403. Size:=length(S);
  404. Inc(CurPtr,Size);
  405. end;
  406. Size:=CurPtr;
  407. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  408. CurPtr:=0;
  409. for I:=0 to Lines^.Count-1 do
  410. begin
  411. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  412. if Size>0 then
  413. begin
  414. if CurPtr+Size>=T^.TextSize then
  415. MSize:=T^.TextSize-CurPtr;
  416. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  417. if MSize<>Size then
  418. Break;
  419. Inc(CurPtr,Size);
  420. end;
  421. if CurPtr>=T^.TextSize then Break;
  422. end;
  423. end;
  424. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  425. var NewSize: word;
  426. NewPtr: pointer;
  427. begin
  428. NewSize:=longint(T^.LinkCount+1)*sizeof(T^.Links^[0]);
  429. GetMem(NewPtr,NewSize);
  430. if Assigned(T^.Links) then
  431. begin
  432. Move(T^.Links^,NewPtr^,T^.LinkSize);
  433. FreeMem(T^.Links,T^.LinkSize);
  434. end;
  435. T^.Links:=NewPtr;
  436. with T^.Links^[T^.LinkCount] do
  437. begin
  438. FileID:=AFileID;
  439. Context:=ACtx;
  440. end;
  441. Inc(T^.LinkCount);
  442. end;
  443. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  444. var P: PIndexEntry;
  445. begin
  446. New(P); FillChar(P^,SizeOf(P^), 0);
  447. P^.Tag:=NewStr(Tag); P^.FileID:=FileID; P^.HelpCtx:=HelpCtx;
  448. NewIndexEntry:=P;
  449. end;
  450. procedure DisposeIndexEntry(P: PIndexEntry);
  451. begin
  452. if P<>nil then
  453. begin
  454. if P^.Tag<>nil then DisposeStr(P^.Tag);
  455. Dispose(P);
  456. end;
  457. end;
  458. function TTopic.LinkSize: sw_word;
  459. begin
  460. LinkSize:=LinkCount*SizeOf(Links^[0]);
  461. end;
  462. function TTopic.GetNamedMarkIndex(const MarkName: string): sw_integer;
  463. var I,Index: sw_integer;
  464. begin
  465. Index:=-1;
  466. if Assigned(NamedMarks) then
  467. for I:=0 to NamedMarks^.Count-1 do
  468. if CompareText(GetStr(NamedMarks^.At(I)),MarkName)=0 then
  469. begin
  470. Index:=I;
  471. Break;
  472. end;
  473. GetNamedMarkIndex:=Index;
  474. end;
  475. function TTopicCollection.At(Index: sw_Integer): PTopic;
  476. begin
  477. At:=inherited At(Index);
  478. end;
  479. procedure TTopicCollection.FreeItem(Item: Pointer);
  480. begin
  481. if Item<>nil then DisposeTopic(Item);
  482. end;
  483. function TTopicCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  484. var K1: PTopic absolute Key1;
  485. K2: PTopic absolute Key2;
  486. R: Sw_integer;
  487. begin
  488. if K1^.HelpCtx<K2^.HelpCtx then R:=-1 else
  489. if K1^.HelpCtx>K2^.HelpCtx then R:= 1 else
  490. R:=0;
  491. Compare:=R;
  492. end;
  493. function TTopicCollection.SearchTopic(AHelpCtx: THelpCtx): PTopic;
  494. var T: TTopic;
  495. P: PTopic;
  496. Index: sw_integer;
  497. begin
  498. T.HelpCtx:=AHelpCtx;
  499. if Search(@T,Index) then
  500. P:=At(Index)
  501. else
  502. P:=nil;
  503. SearchTopic:=P;
  504. end;
  505. function TIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  506. begin
  507. At:=inherited At(Index);
  508. end;
  509. procedure TIndexEntryCollection.FreeItem(Item: Pointer);
  510. begin
  511. if Item<>nil then DisposeIndexEntry(Item);
  512. end;
  513. function TUnsortedIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  514. begin
  515. At:=inherited At(Index);
  516. end;
  517. procedure TUnsortedIndexEntryCollection.FreeItem(Item: Pointer);
  518. begin
  519. if Item<>nil then DisposeIndexEntry(Item);
  520. end;
  521. function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  522. var K1: PIndexEntry absolute Key1;
  523. K2: PIndexEntry absolute Key2;
  524. R: Sw_integer;
  525. S1,S2: string;
  526. T1,T2 : PTopic;
  527. begin
  528. S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
  529. if S1<S2 then
  530. begin
  531. Compare:=-1;
  532. exit;
  533. end;
  534. if S1>S2 then
  535. begin
  536. Compare:=1;
  537. exit;
  538. end;
  539. (* if assigned(HelpFacility) then
  540. begin
  541. { Try to read the title of the topic }
  542. T1:=HelpFacility^.LoadTopic(K1^.FileID,K1^.HelpCtx);
  543. T2:=HelpFacility^.LoadTopic(K2^.FileID,K2^.HelpCtx);
  544. if assigned(T1^.Text) and assigned(T2^.Text) then
  545. r:=strcomp(pchar(T1^.Text),pchar(T2^.Text))
  546. else
  547. r:=0;
  548. if r>0 then
  549. begin
  550. Compare:=1;
  551. exit;
  552. end;
  553. if r<0 then
  554. begin
  555. Compare:=-1;
  556. exit;
  557. end;
  558. end; *)
  559. if K1^.FileID<K2^.FileID then R:=-1
  560. else if K1^.FileID>K2^.FileID then R:= 1
  561. else if K1^.HelpCtx<K2^.HelpCtx then
  562. r:=-1
  563. else if K1^.HelpCtx>K2^.HelpCtx then
  564. r:=1
  565. else
  566. R:=0;
  567. Compare:=R;
  568. end;
  569. constructor THelpFile.Init(AID: word);
  570. begin
  571. inherited Init;
  572. ID:=AID;
  573. New(Topics, Init(2000,1000));
  574. New(IndexEntries, Init(2000,1000));
  575. end;
  576. procedure THelpFile.AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string; ExtData: pointer; ExtDataSize: longint);
  577. begin
  578. Topics^.Insert(NewTopic(ID,HelpCtx,Pos,Param,ExtData,ExtDataSize));
  579. end;
  580. procedure THelpFile.AddIndexEntry(const Text: string; AHelpCtx: THelpCtx);
  581. begin
  582. IndexEntries^.Insert(NewIndexEntry(Text,ID,AHelpCtx));
  583. end;
  584. function THelpFile.LoadTopic(HelpCtx: THelpCtx): PTopic;
  585. var T: PTopic;
  586. begin
  587. T:=SearchTopic(HelpCtx);
  588. if (T<>nil) then
  589. if T^.Text=nil then
  590. begin
  591. MaintainTopicCache;
  592. if ReadTopic(T)=false then
  593. T:=nil;
  594. if (T<>nil) and (T^.Text=nil) then T:=nil;
  595. end;
  596. if T<>nil then
  597. begin
  598. T^.LastAccess:=GetDosTicks;
  599. T:=CloneTopic(T);
  600. end;
  601. LoadTopic:=T;
  602. end;
  603. function THelpFile.LoadIndex: boolean;
  604. begin
  605. Abstract;
  606. LoadIndex:=false; { remove warning }
  607. end;
  608. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  609. var T: PTopic;
  610. begin
  611. T:=Topics^.SearchTopic(HelpCtx);
  612. SearchTopic:=T;
  613. end;
  614. function THelpFile.ReadTopic(T: PTopic): boolean;
  615. begin
  616. Abstract;
  617. ReadTopic:=false; { remove warning }
  618. end;
  619. procedure THelpFile.MaintainTopicCache;
  620. var Count: sw_integer;
  621. MinLRU: longint;
  622. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  623. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  624. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  625. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; end; end;
  626. var P: PTopic;
  627. begin
  628. Count:=0; Topics^.ForEach(@CountThem);
  629. if (Count>=TopicCacheSize) then
  630. begin
  631. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  632. if P<>nil then
  633. begin
  634. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  635. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  636. end;
  637. end;
  638. end;
  639. destructor THelpFile.Done;
  640. begin
  641. if Topics<>nil then Dispose(Topics, Done);
  642. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  643. inherited Done;
  644. end;
  645. constructor THelpFacility.Init;
  646. begin
  647. inherited Init;
  648. New(HelpFiles, Init(10,10));
  649. IndexTabSize:=40;
  650. end;
  651. function THelpFacility.AddFile(const FileName, Param: string): PHelpFile;
  652. var H: PHelpFile;
  653. OK: boolean;
  654. I: integer;
  655. HT: THelpFileType;
  656. begin
  657. OK:=false; H:=nil;
  658. for I:=0 to GetHelpFileTypeCount-1 do
  659. begin
  660. GetHelpFileType(I,HT);
  661. H:=HT.OpenProc(FileName,Param,LastID+1);
  662. if Assigned(H) then
  663. Break;
  664. end;
  665. if Assigned(H) then
  666. OK:=AddHelpFile(H);
  667. if (not OK) and Assigned(H) then begin Dispose(H, Done); H:=nil; end;
  668. AddFile:=H;
  669. end;
  670. function THelpFacility.AddHelpFile(H: PHelpFile): boolean;
  671. begin
  672. if H<>nil then
  673. begin
  674. HelpFiles^.Insert(H);
  675. Inc(LastID);
  676. { H^.ID:=LastID; now already set by OpenProc PM }
  677. end;
  678. AddHelpFile:=H<>nil;
  679. end;
  680. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  681. var P: PTopic;
  682. HelpFile: PHelpFile;
  683. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  684. begin
  685. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  686. Search:=P<>nil;
  687. end;
  688. begin
  689. HelpFile:=nil;
  690. if SourceFileID=0 then P:=nil else
  691. begin
  692. HelpFile:=SearchFile(SourceFileID);
  693. P:=SearchTopicInHelpFile(HelpFile,Context);
  694. end;
  695. if P=nil then HelpFiles^.FirstThat(@Search);
  696. if P=nil then HelpFile:=nil;
  697. SearchTopicOwner:=HelpFile;
  698. end;
  699. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  700. var P: PTopic;
  701. H: PHelpFile;
  702. begin
  703. if (SourceFileID=0) and (Context=0) then
  704. P:=BuildIndexTopic else
  705. begin
  706. H:=SearchTopicOwner(SourceFileID,Context);
  707. if (H=nil) then P:=nil else
  708. P:=H^.LoadTopic(Context);
  709. end;
  710. LoadTopic:=P;
  711. end;
  712. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  713. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  714. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  715. begin
  716. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  717. end;
  718. var P: PIndexEntry;
  719. begin
  720. H^.LoadIndex;
  721. P:=H^.IndexEntries^.FirstThat(@Search);
  722. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  723. ScanHelpFile:=P<>nil;
  724. end;
  725. begin
  726. Keyword:=UpcaseStr(Keyword);
  727. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  728. end;
  729. function THelpFacility.BuildIndexTopic: PTopic;
  730. var T: PTopic;
  731. Keywords: PIndexEntryCollection;
  732. Lines: PUnsortedStringCollection;
  733. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  734. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  735. begin
  736. Keywords^.Insert(P);
  737. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  738. end;
  739. begin
  740. H^.LoadIndex;
  741. if Keywords^.Count<MaxCollectionSize then
  742. H^.IndexEntries^.FirstThat(@InsertKeywords);
  743. end;
  744. procedure AddLine(S: string);
  745. begin
  746. if S='' then S:=' ';
  747. Lines^.Insert(NewStr(S));
  748. end;
  749. var Line: string;
  750. procedure FlushLine;
  751. begin
  752. if Line<>'' then AddLine(Line); Line:='';
  753. end;
  754. var KWCount,NLFlag: sw_integer;
  755. LastFirstChar: char;
  756. procedure NewSection(FirstChar: char);
  757. begin
  758. if FirstChar<=#64 then FirstChar:=#32;
  759. FlushLine;
  760. AddLine('');
  761. AddLine(FirstChar);
  762. AddLine('');
  763. LastFirstChar:=FirstChar;
  764. NLFlag:=0;
  765. end;
  766. function FormatAlias(Alias: string): string;
  767. var StartP,EndP: sw_integer;
  768. begin
  769. repeat
  770. StartP:=Pos(' ',Alias);
  771. if StartP>0 then
  772. begin
  773. EndP:=StartP;
  774. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  775. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  776. end;
  777. until StartP=0;
  778. if Assigned(HelpFacility) then
  779. if length(Alias)>IndexTabSize-4 then
  780. Alias:=Trim(copy(Alias,1,IndexTabSize-4-2))+'..';
  781. FormatAlias:=Alias;
  782. end;
  783. procedure AddKeyword(KWS: string);
  784. begin
  785. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  786. if (KWCount=1) or
  787. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  788. NewSection(Upcase(KWS[1]));
  789. KWS:=FormatAlias(KWS);
  790. if (NLFlag mod 2)=0
  791. then Line:=' '+#2+KWS+#2
  792. else begin
  793. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  794. FlushLine;
  795. end;
  796. Inc(NLFlag);
  797. end;
  798. var KW: PIndexEntry;
  799. I,p : sw_integer;
  800. IsMultiple : boolean;
  801. MultiCount : longint;
  802. St,LastTag : String;
  803. begin
  804. New(Keywords, Init(5000,5000));
  805. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  806. New(Lines, Init((Keywords^.Count div 2)+100,1000));
  807. T:=NewTopic(0,0,0,'',nil,0);
  808. if HelpFiles^.Count=0 then
  809. begin
  810. AddLine('');
  811. AddLine(' '+msg_nohelpfilesinstalled)
  812. end else
  813. begin
  814. AddLine(' '+msg_helpindex);
  815. KWCount:=0; Line:='';
  816. T^.LinkCount:=Min(Keywords^.Count,MaxBytes div sizeof(T^.Links^[0])-1);
  817. GetMem(T^.Links,T^.LinkSize);
  818. MultiCount:=0;
  819. LastTag:='';
  820. for I:=0 to T^.LinkCount-1 do
  821. begin
  822. KW:=Keywords^.At(I);
  823. if (LastTag<>KW^.Tag^) then
  824. Begin
  825. MultiCount:=0;
  826. IsMultiple:=(I<T^.LinkCount-1) and (KW^.Tag^=Keywords^.At(I+1)^.Tag^);
  827. End
  828. else
  829. IsMultiple:=true;
  830. if IsMultiple then
  831. Begin
  832. Inc(MultiCount);
  833. (* St:=Trim(strpas(pchar(HelpFacility^.LoadTopic(KW^.FileID,KW^.HelpCtx)^.Text))); *)
  834. St:=KW^.Tag^+' ['+IntToStr(MultiCount)+']';
  835. (* { Remove all special chars }
  836. for p:=1 to Length(st) do
  837. if ord(st[p])<=16 then
  838. st[p]:=' ';
  839. p:=pos(KW^.Tag^,St);
  840. if (p=1) then
  841. AddKeyword(St)
  842. else
  843. AddKeyword(KW^.Tag^+' '+St); *)
  844. AddKeyWord(St);
  845. End
  846. else
  847. AddKeyword(KW^.Tag^);
  848. LastTag:=KW^.Tag^;
  849. T^.Links^[I].Context:=longint(KW^.HelpCtx);
  850. T^.Links^[I].FileID:=KW^.FileID;
  851. end;
  852. FlushLine;
  853. AddLine('');
  854. end;
  855. RenderTopic(Lines,T);
  856. Dispose(Lines, Done);
  857. Keywords^.DeleteAll; Dispose(Keywords, Done);
  858. BuildIndexTopic:=T;
  859. end;
  860. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  861. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  862. begin
  863. Match:=(P^.ID=ID);
  864. end;
  865. begin
  866. SearchFile:=HelpFiles^.FirstThat(@Match);
  867. end;
  868. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  869. var P: PTopic;
  870. begin
  871. if F=nil then P:=nil else
  872. P:=F^.SearchTopic(Context);
  873. SearchTopicInHelpFile:=P;
  874. end;
  875. destructor THelpFacility.Done;
  876. begin
  877. inherited Done;
  878. Dispose(HelpFiles, Done);
  879. end;
  880. END.
  881. {
  882. $Log$
  883. Revision 1.11 2004-09-16 22:08:13 armin
  884. * added target netwlibc
  885. Revision 1.10 2003/09/27 14:03:45 peter
  886. * fixed for unix
  887. Revision 1.9 2002/11/22 15:18:24 pierre
  888. * fix SwapWord, arg must be of var type
  889. Revision 1.8 2002/11/22 12:21:16 pierre
  890. + SwapLongint and SwapWord added for big endian machines
  891. Revision 1.7 2002/09/07 15:40:49 peter
  892. * old logs removed and tabs fixed
  893. Revision 1.6 2002/03/25 14:37:03 pierre
  894. + hscDirect added
  895. }