whelp.pas 25 KB

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