wos2help.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 2000 by Berczi Gabor
  5. Help support for OS/2 (.INF) help files
  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 WOS2Help;
  14. interface
  15. uses Objects,
  16. WUtils,WHelp;
  17. const
  18. INFFileSignature = 'HS';
  19. { TOCEntry flags }
  20. inf_tef_HasChildren = $80; { following nodes are a higher level }
  21. inf_tef_Hidden = $40; { this entry doesn't appear in VIEW.EXE's presentation of the toc }
  22. inf_tef_Extended = $20; { extended entry format }
  23. inf_tef_LevelMask = $0f;
  24. type
  25. TINFFileHeader = packed record
  26. Signature : array[1..2] of char;{ ID magic word (5348h = "HS") }
  27. Unknown1 : byte; { unknown purpose, could be third letter of ID }
  28. Flags : byte; { probably a flag word... }
  29. { bit 0: set if INF style file }
  30. { bit 4: set if HLP style file }
  31. { patching this byte allows reading HLP files }
  32. { using the VIEW command, while help files }
  33. { seem to work with INF settings here as well. }
  34. HeaderSize : word; { total size of header }
  35. Unknown2 : word; { unknown purpose }
  36. NumTOC : word; { 16 bit number of entries in the tocarray }
  37. TOCStrTabOfs: longint; { 32 bit file offset of the start of the }
  38. { strings for the table-of-contents }
  39. TOCStrTabSize: longint; { number of bytes in file occupied by the }
  40. { table-of-contents strings }
  41. TOCArrayOfs : longint; { 32 bit file offset of the start of tocarray }
  42. NumResPanels: word; { number of panels with ressource numbers }
  43. ResTabOfs : longint; { 32 bit file offset of ressource number table }
  44. NumNames : word; { number of panels with textual name }
  45. NameTabOfs : longint; { 32 bit file offset to panel name table }
  46. NumIndexes : word; { number of index entries }
  47. IndexTabOfs : longint; { 32 bit file offset to index table }
  48. IndexTabSize: longint; { size of index table }
  49. Unknown3: array[1..10] of byte; { unknown purpose }
  50. SearchTabOfs: longint; { 32 bit file offset of full text search table }
  51. SearchTabSize:longint; { size of full text search table }
  52. NumSlots : word; { number of "slots" }
  53. SlotTabOfs : longint; { file offset of the slots array }
  54. DictSize : longint; { number of bytes occupied by the "dictionary" }
  55. NumDictEntries: word; { number of entries in the dictionary }
  56. DictOfs : longint; { file offset of the start of the dictionary }
  57. ImageOfs : longint; { file offset of image data }
  58. Unknown4 : byte; { unknown purpose }
  59. NLSTabOfs : longint; { 32 bit file offset of NLS table }
  60. NLSTabSize : longint; { size of NLS table }
  61. ExtBlockOfs : longint; { 32 bit file offset of extended data block }
  62. Unknown5: array[1..12] of byte;{ unknown purpose }
  63. Title : array[1..48] of char;{ ASCII title of database }
  64. end;
  65. PINFTOCArray = ^TINFTOCArray;
  66. TINFTOCArray = packed array[0..16382] of longint;
  67. PINFSlotArray = ^TINFSlotArray;
  68. TINFSlotArray = packed array[0..32766] of word;
  69. PSlotArray = ^TSlotArray;
  70. TSlotArray = packed array[0..16382] of longint;
  71. TINFTOCEntry = packed record
  72. Size : byte; { length of the entry including this byte }
  73. Flags : byte; { flag byte, description folows (MSB first) }
  74. NumSlots : byte; { number of "slots" occupied by the text for }
  75. { this toc entry }
  76. Slots : record end;
  77. end;
  78. TINFTopicHeader = packed record
  79. Stuff : byte; { ?? }
  80. LocalDictPos : longint; { file offset of the local dictionary }
  81. NumLocalDict : byte; { number of entries in the local dict }
  82. TextSize : word; { number of bytes in the text }
  83. Text : record end;{ encoded text of the article }
  84. end;
  85. TINFEscHeader = packed record
  86. EscLen : byte; { length of the sequence (including esclen but excluding FF) }
  87. EscCode: byte; { which escape function }
  88. end;
  89. POS2HelpFile = ^TOS2HelpFile;
  90. TOS2HelpFile = object(THelpFile)
  91. constructor Init(AFileName: string; AID: word);
  92. destructor Done; virtual;
  93. public
  94. function LoadIndex: boolean; virtual;
  95. function ReadTopic(T: PTopic): boolean; virtual;
  96. private
  97. F: PStream;
  98. Header: TINFFileHeader;
  99. Dictionary: PUnsortedStringCollection;
  100. Slots: PSlotArray;
  101. SlotsSize: longint;
  102. function ReadHeader: boolean;
  103. function ReadSlots: boolean;
  104. function ReadDictionary: boolean;
  105. function ReadTOC: boolean;
  106. function ReadTopicRec(FileOfs: longint; Topic: PTopic; Lines: PUnsortedStringCollection): boolean;
  107. end;
  108. TINFGetAttrColorProc = function(TextStyle, TextColor: byte; var Color: byte): boolean;
  109. function DefINFGetAttrColor(TextStyle, TextColor: byte; var Color: byte): boolean;
  110. const INFGetAttrColor : TINFGetAttrColorProc = {$ifdef fpc}@{$endif}DefINFGetAttrColor;
  111. procedure RegisterHelpType;
  112. implementation
  113. uses CallSpec;
  114. function DefINFGetAttrColor(TextStyle, TextColor: byte; var Color: byte): boolean;
  115. {
  116. style; // 1,2,3: same as :hp#.
  117. // 4,5,6: same as :hp5,6,7.
  118. // 0 returns to plain text
  119. color; // 1,2,3: same as :hp4,8,9.
  120. // 0: default color
  121. :hp4 text is light blue
  122. :hp8 text is red
  123. :hp9 text is magenta
  124. :hp1 is italic font
  125. :hp2 is bold font
  126. :hp3 is bold italic font
  127. :hp5 is normal underlined font
  128. :hp6 is italic underlined font
  129. :hp7 is bold underlined font
  130. }
  131. begin
  132. DefINFGetAttrColor:=false;
  133. end;
  134. function KillNonASCII(S: string): string;
  135. var I: sw_integer;
  136. begin
  137. for I:=1 to length(S) do
  138. if S[I]<#32 then
  139. S[I]:='.';
  140. KillNonASCII:=S;
  141. end;
  142. function ContainsNonASCIIChar(const S: string): boolean;
  143. var I: sw_integer;
  144. begin
  145. ContainsNonASCIIChar:=false;
  146. for I:=1 to length(S) do
  147. if S[I]<#32 then
  148. begin
  149. ContainsNonASCIIChar:=true;
  150. Break;
  151. end;
  152. end;
  153. constructor TOS2HelpFile.Init(AFileName: string; AID: word);
  154. var OK: boolean;
  155. begin
  156. if inherited Init(AID)=false then Fail;
  157. New(Dictionary, Init(100,1000));
  158. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  159. OK:=F<>nil;
  160. if OK then OK:=(F^.Status=stOK);
  161. if OK then OK:=ReadHeader;
  162. if OK=false then
  163. begin
  164. Done;
  165. Fail;
  166. end;
  167. end;
  168. function TOS2HelpFile.ReadHeader: boolean;
  169. var OK: boolean;
  170. begin
  171. F^.Read(Header,sizeof(Header));
  172. OK:=(F^.Status=stOK);
  173. OK:=OK and (Header.Signature=INFFileSignature);
  174. ReadHeader:=OK;
  175. end;
  176. function TOS2HelpFile.LoadIndex: boolean;
  177. var OK: boolean;
  178. begin
  179. OK:=ReadDictionary;
  180. if OK then OK:=ReadSlots;
  181. if OK then OK:=ReadTOC;
  182. LoadIndex:=OK;
  183. end;
  184. function TOS2HelpFile.ReadDictionary: boolean;
  185. var OK: boolean;
  186. I: longint;
  187. C: array[0..255] of char;
  188. B: byte;
  189. begin
  190. F^.Seek(Header.DictOfs);
  191. OK:=(F^.Status=stOK);
  192. I:=0;
  193. while OK and (I<Header.NumDictEntries) do
  194. begin
  195. FillChar(C,sizeof(C),0);
  196. F^.Read(B,sizeof(B)); F^.Read(C,B-1);
  197. OK:=(F^.Status=stOK);
  198. if OK then
  199. Dictionary^.InsertStr(StrPas(@C));
  200. Inc(I);
  201. end;
  202. ReadDictionary:=OK;
  203. end;
  204. function TOS2HelpFile.ReadSlots: boolean;
  205. var OK: boolean;
  206. begin
  207. SlotsSize:=Header.NumSlots*sizeof(Slots^[0]);
  208. GetMem(Slots,SlotsSize);
  209. F^.Seek(Header.SlotTabOfs);
  210. OK:=(F^.Status=stOK);
  211. if OK then
  212. begin
  213. F^.Read(Slots^,SlotsSize);
  214. OK:=(F^.Status=stOK);
  215. end;
  216. ReadSlots:=OK;
  217. end;
  218. function TOS2HelpFile.ReadTOC: boolean;
  219. var OK: boolean;
  220. I,J,L,Count: longint;
  221. TE: TINFTOCEntry;
  222. W: word;
  223. C: array[0..255] of char;
  224. StartOfs: longint;
  225. S: string;
  226. SubSlots: PWordArray;
  227. SubSlotsSize: longint;
  228. TOC: PINFTOCArray;
  229. TOCSize: longint;
  230. begin
  231. F^.Seek(Header.TOCArrayOfs); TOCSize:=Header.TOCStrTabSize;
  232. OK:=(F^.Status=stOK);
  233. if OK then
  234. begin
  235. GetMem(TOC,TOCSize);
  236. F^.Read(TOC^,TOCSize);
  237. OK:=(F^.Status=stOK);
  238. I:=0;
  239. while OK and (I<Header.NumTOC) do
  240. begin
  241. F^.Seek(TOC^[I]);
  242. OK:=(F^.Status=stOK);
  243. if OK then
  244. begin
  245. StartOfs:=F^.GetPos;
  246. F^.Read(TE,sizeof(TE));
  247. OK:=(F^.Status=stOK);
  248. end;
  249. if OK and ((TE.Flags and inf_tef_Extended)<>0) then
  250. begin
  251. F^.Read(W,sizeof(W));
  252. Count:=0;
  253. if (W and 1)<>0 then Inc(Count,5);
  254. if (W and 2)<>0 then Inc(Count,5);
  255. if (W and 4)<>0 then Inc(Count,2);
  256. if (W and 8)<>0 then Inc(Count,2);
  257. F^.Seek(F^.GetPos+Count);
  258. OK:=(F^.Status=stOK);
  259. end;
  260. if OK then
  261. begin
  262. SubSlotsSize:=sizeof(Word)*TE.NumSlots;
  263. GetMem(SubSlots,SubSlotsSize);
  264. F^.Read(SubSlots^,SubSlotsSize);
  265. FillChar(C,sizeof(C),0);
  266. F^.Read(C,Max(0,TE.Size-(F^.GetPos-StartOfs)));
  267. OK:=(F^.Status=stOK);
  268. if OK then
  269. begin
  270. S:=StrPas(@C);
  271. AddTopic(I,StartOfs,S,SubSlots,SubSlotsSize);
  272. if (S<>'') and (not ContainsNonASCIIChar(S)) then
  273. AddIndexEntry(S,I);
  274. { !}
  275. end;
  276. FreeMem(SubSlots,SubSlotsSize);
  277. end;
  278. Inc(I);
  279. end;
  280. FreeMem(TOC,TOCSize); TOC:=nil;
  281. end;
  282. ReadTOC:=OK;
  283. end;
  284. function TOS2HelpFile.ReadTopicRec(FileOfs: longint; Topic: PTopic; Lines: PUnsortedStringCollection): boolean;
  285. var Line: string;
  286. LastTextChar: char;
  287. CharsInLine: sw_integer;
  288. LeftMargin,RightMargin: byte;
  289. TextStyle,TextColor: byte;
  290. InMonospace: boolean;
  291. Align: (alLeft,alRight,alCenter);
  292. LineNo: longint;
  293. procedure FlushLine;
  294. begin
  295. if Line<>'' then Lines^.InsertStr(Line);
  296. Line:='';
  297. end;
  298. procedure AddChar(C: char);
  299. begin
  300. if length(Line)>=255 then FlushLine;
  301. Line:=Line+C;
  302. end;
  303. procedure AddString(const S: string);
  304. var I: sw_integer;
  305. begin
  306. for I:=1 to length(S) do
  307. AddChar(S[I]);
  308. end;
  309. procedure AddTextChar(C: char);
  310. begin
  311. if (C=hscLineBreak) then
  312. begin
  313. case Align of
  314. alRight : AddChar(hscRight);
  315. alCenter: AddChar(hscCenter);
  316. end;
  317. end;
  318. if (CharsInLine=0) and (C<>hscLineBreak) then
  319. begin
  320. if (LeftMargin>0) then
  321. begin
  322. AddString(CharStr(#255,LeftMargin)+hscLineStart);
  323. Inc(CharsInLine,LeftMargin);
  324. end else
  325. if InMonospace then
  326. begin
  327. AddChar(' ');
  328. Inc(CharsInLine);
  329. end;
  330. end;
  331. AddChar(C);
  332. LastTextChar:=C;
  333. if C=hscLineBreak then
  334. begin
  335. CharsInLine:=0;
  336. Inc(LineNo);
  337. end
  338. else
  339. Inc(CharsInLine);
  340. end;
  341. procedure AddText(const S: string);
  342. var I: sw_integer;
  343. begin
  344. for I:=1 to length(S) do
  345. AddTextChar(S[I]);
  346. end;
  347. var H: TINFTopicHeader;
  348. Text: PByteArray;
  349. TextOfs: longint;
  350. Dict: PWordArray;
  351. Spacing: boolean;
  352. function NextByte: byte;
  353. var B: byte;
  354. begin
  355. NextByte:=Text^[TextOfs];
  356. Inc(TextOfs);
  357. end;
  358. procedure ReadBytes(DataPtr: pointer; NumBytes: sw_integer);
  359. var I: sw_integer;
  360. begin
  361. for I:=1 to NumBytes do
  362. if Assigned(DataPtr) then
  363. PByteArray(DataPtr)^[I-1]:=NextByte
  364. else
  365. NextByte;
  366. end;
  367. procedure AddWord(LocalIndex: word);
  368. begin
  369. AddText(GetStr(Dictionary^.At(Dict^[LocalIndex])));
  370. if Spacing and not InMonospace then AddTextChar(' ');
  371. end;
  372. var
  373. DictSize,EscStartOfs: longint;
  374. OK: boolean;
  375. EH: TINFEscHeader;
  376. B,Color: byte;
  377. W: word;
  378. CurLinkCtx: longint;
  379. InTempMargin: boolean;
  380. begin
  381. F^.Reset;
  382. F^.Seek(FileOfs);
  383. OK:=(F^.Status=stOK);
  384. if OK then
  385. begin
  386. F^.Read(H,sizeof(H));
  387. OK:=(F^.Status=stOK);
  388. end;
  389. if OK then
  390. begin
  391. LineNo:=0;
  392. Line:=''; LeftMargin:=0; RightMargin:=0; LastTextChar:=hscLineBreak;
  393. InTempMargin:=false;
  394. CharsInLine:=0; TextStyle:=0; TextColor:=0; Align:=alLeft;
  395. CurLinkCtx:=-1; InMonospace:=false;
  396. DictSize:=H.NumLocalDict*sizeof(Dict^[0]);
  397. GetMem(Text,H.TextSize);
  398. GetMem(Dict,DictSize);
  399. F^.Read(Text^,H.TextSize);
  400. F^.Seek(H.LocalDictPos);
  401. F^.Read(Dict^,DictSize);
  402. OK:=(F^.Status=stOK);
  403. TextOfs:=0; Spacing:=true;
  404. while OK and (TextOfs<H.TextSize) do
  405. begin
  406. B:=NextByte;
  407. if (B<H.NumLocalDict) then
  408. begin
  409. AddWord(B);
  410. end else
  411. case B of
  412. $fa : begin
  413. if (LineNo>0) then
  414. AddTextChar(hscLineBreak);
  415. if InTempMargin then
  416. LeftMargin:=0;
  417. AddTextChar(hscLineBreak);
  418. Spacing:=true;
  419. end;
  420. $fb : { ??? };
  421. $fc : Spacing:=not Spacing;
  422. $fd : begin
  423. AddTextChar(hscLineBreak);
  424. Spacing:=true;
  425. end;
  426. $fe : AddChar(' ');
  427. $ff : begin
  428. EscStartOfs:=TextOfs;
  429. ReadBytes(@EH,sizeof(EH));
  430. case EH.EscCode of
  431. $02,$11,$12 :
  432. begin
  433. { set left margin }
  434. if EH.EscCode=$11 then
  435. AddTextChar(hscLineBreak);
  436. LeftMargin:=NextByte;
  437. end;
  438. $03 :
  439. RightMargin:=NextByte;
  440. $04 :
  441. begin
  442. TextStyle:=NextByte;
  443. if (TextStyle=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  444. AddChar(hscNormText)
  445. else
  446. AddText(hscTextColor+chr(Color));
  447. end;
  448. $05 :
  449. begin
  450. W:=word(NextByte)*256+NextByte;
  451. AddChar(hscLink);
  452. CurLinkCtx:=W;
  453. end;
  454. $08 :
  455. if CurLinkCtx<>-1 then
  456. begin
  457. AddChar(hscLink);
  458. AddLinkToTopic(Topic,ID,CurLinkCtx);
  459. end;
  460. $0b :
  461. begin
  462. if CharsInLine>0 then
  463. AddTextChar(hscLineBreak);
  464. AddTextChar(hscLineBreak);
  465. AddChar(hscCode);
  466. InMonospace:=true;
  467. end;
  468. $0c :
  469. begin
  470. AddChar(hscCode);
  471. InMonospace:=false;
  472. end;
  473. $0d :
  474. begin
  475. TextColor:=NextByte;
  476. if (TextColor=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  477. AddChar(hscNormText)
  478. else
  479. AddText(hscTextColor+chr(Color));
  480. end;
  481. $0e :
  482. begin
  483. AddText(hscLineBreak+'[img]'+hscLineBreak);
  484. end;
  485. $1a :
  486. begin
  487. if CharsInLine>0 then AddText(hscLineBreak);
  488. case NextByte of
  489. 1 : Align:=alLeft;
  490. 2 : Align:=alRight;
  491. 4 : Align:=alCenter;
  492. end;
  493. Spacing:=false;
  494. end;
  495. $1b :
  496. begin
  497. Spacing:=true;
  498. end;
  499. $1c :
  500. begin
  501. LeftMargin:=CharsInLine;
  502. InTempMargin:=true;
  503. end;
  504. end;
  505. TextOfs:=EscStartOfs+EH.EscLen;
  506. end;
  507. end;
  508. end;
  509. if CharsInLine>0 then
  510. AddTextChar(hscLineBreak);
  511. AddTextChar(hscLineBreak);
  512. FlushLine;
  513. FreeMem(Dict,DictSize);
  514. FreeMem(Text,H.TextSize);
  515. end;
  516. F^.Reset;
  517. ReadTopicRec:=OK;
  518. end;
  519. function TOS2HelpFile.ReadTopic(T: PTopic): boolean;
  520. var OK: boolean;
  521. I,NumSlots,Idx: sw_integer;
  522. TopicOfs: longint;
  523. L: PUnsortedStringCollection;
  524. Title: string;
  525. begin
  526. OK:=false;
  527. NumSlots:=T^.ExtDataSize div 2; { extdata is array of word }
  528. New(L, Init(100,100));
  529. Title:=GetStr(T^.Param);
  530. if Title<>'' then
  531. begin
  532. L^.InsertStr(' '+Title+' Ü'+hscLineBreak);
  533. L^.InsertStr(' '+CharStr('ß',length(Title)+3)+hscLineBreak);
  534. end;
  535. if 0<T^.HelpCtx then
  536. begin
  537. L^.InsertStr(hscLink+'[previous topic]'+hscLink+' ');
  538. AddLinkToTopic(T,ID,T^.HelpCtx-1);
  539. end;
  540. if T^.HelpCtx<Header.NumTOC then
  541. begin
  542. L^.InsertStr(hscLink+'[next topic]'+hscLink);
  543. AddLinkToTopic(T,ID,T^.HelpCtx+1);
  544. end;
  545. L^.InsertStr(hscLineBreak);
  546. for I:=0 to NumSlots-1 do
  547. begin
  548. Idx:=PWordArray(T^.ExtData)^[I];
  549. TopicOfs:=Slots^[Idx];
  550. OK:=ReadTopicRec(TopicOfs,T,L);
  551. if not OK then
  552. Break;
  553. end;
  554. if OK then BuildTopic(L,T);
  555. Dispose(L, Done);
  556. ReadTopic:=OK;
  557. end;
  558. destructor TOS2HelpFile.Done;
  559. begin
  560. if Assigned(Slots) then FreeMem(Slots, SlotsSize); Slots:=nil;
  561. if Assigned(Dictionary) then Dispose(Dictionary, Done); Dictionary:=nil;
  562. if Assigned(F) then Dispose(F, Done); F:=nil;
  563. inherited Done;
  564. end;
  565. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile; {$ifndef FPC}far;{$endif}
  566. begin
  567. CreateProc:=New(POS2HelpFile, Init(FileName,Index));
  568. end;
  569. procedure RegisterHelpType;
  570. begin
  571. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  572. end;
  573. END.
  574. {
  575. $Log$
  576. Revision 1.3 2002-09-07 15:40:50 peter
  577. * old logs removed and tabs fixed
  578. }