wos2help.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. {
  2. This file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 2000 by Berczi Gabor
  4. Help support for OS/2 (.INF) help files
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$R-}
  12. {$H-}
  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 AnsiChar;{ 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 AnsiChar;{ 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. function DefINFGetAttrColor(TextStyle, TextColor: byte; var Color: byte): boolean;
  114. {
  115. style; // 1,2,3: same as :hp#.
  116. // 4,5,6: same as :hp5,6,7.
  117. // 0 returns to plain text
  118. color; // 1,2,3: same as :hp4,8,9.
  119. // 0: default color
  120. :hp4 text is light blue
  121. :hp8 text is red
  122. :hp9 text is magenta
  123. :hp1 is italic font
  124. :hp2 is bold font
  125. :hp3 is bold italic font
  126. :hp5 is normal underlined font
  127. :hp6 is italic underlined font
  128. :hp7 is bold underlined font
  129. }
  130. begin
  131. DefINFGetAttrColor:=false;
  132. end;
  133. function KillNonASCII(S: string): string;
  134. var I: sw_integer;
  135. begin
  136. for I:=1 to length(S) do
  137. if S[I]<#32 then
  138. S[I]:='.';
  139. KillNonASCII:=S;
  140. end;
  141. function ContainsNonASCIIChar(const S: string): boolean;
  142. var I: sw_integer;
  143. begin
  144. ContainsNonASCIIChar:=false;
  145. for I:=1 to length(S) do
  146. if S[I]<#32 then
  147. begin
  148. ContainsNonASCIIChar:=true;
  149. Break;
  150. end;
  151. end;
  152. constructor TOS2HelpFile.Init(AFileName: string; AID: word);
  153. var OK: boolean;
  154. begin
  155. if inherited Init(AID)=false then Fail;
  156. New(Dictionary, Init(100,1000));
  157. F:=New(PFastBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  158. OK:=F<>nil;
  159. if OK then OK:=(F^.Status=stOK);
  160. if OK then OK:=ReadHeader;
  161. if OK=false then
  162. begin
  163. Done;
  164. Fail;
  165. end;
  166. end;
  167. function TOS2HelpFile.ReadHeader: boolean;
  168. var OK: boolean;
  169. begin
  170. F^.Read(Header,sizeof(Header));
  171. OK:=(F^.Status=stOK);
  172. OK:=OK and (Header.Signature=INFFileSignature);
  173. ReadHeader:=OK;
  174. end;
  175. function TOS2HelpFile.LoadIndex: boolean;
  176. var OK: boolean;
  177. begin
  178. OK:=ReadDictionary;
  179. if OK then OK:=ReadSlots;
  180. if OK then OK:=ReadTOC;
  181. LoadIndex:=OK;
  182. end;
  183. function TOS2HelpFile.ReadDictionary: boolean;
  184. var OK: boolean;
  185. I: longint;
  186. C: array[0..255] of AnsiChar;
  187. B: byte;
  188. begin
  189. F^.Seek(Header.DictOfs);
  190. OK:=(F^.Status=stOK);
  191. I:=0;
  192. while OK and (I<Header.NumDictEntries) do
  193. begin
  194. FillChar(C,sizeof(C),0);
  195. F^.Read(B,sizeof(B)); F^.Read(C,B-1);
  196. OK:=(F^.Status=stOK);
  197. if OK then
  198. Dictionary^.InsertStr(StrPas(@C));
  199. Inc(I);
  200. end;
  201. ReadDictionary:=OK;
  202. end;
  203. function TOS2HelpFile.ReadSlots: boolean;
  204. var OK: boolean;
  205. begin
  206. SlotsSize:=Header.NumSlots*sizeof(Slots^[0]);
  207. GetMem(Slots,SlotsSize);
  208. F^.Seek(Header.SlotTabOfs);
  209. OK:=(F^.Status=stOK);
  210. if OK then
  211. begin
  212. F^.Read(Slots^,SlotsSize);
  213. OK:=(F^.Status=stOK);
  214. end;
  215. ReadSlots:=OK;
  216. end;
  217. function TOS2HelpFile.ReadTOC: boolean;
  218. var OK: boolean;
  219. I,Count: longint;
  220. TE: TINFTOCEntry;
  221. W: word;
  222. C: array[0..255] of AnsiChar;
  223. StartOfs: longint;
  224. S: string;
  225. SubSlots: PWordArray;
  226. SubSlotsSize: longint;
  227. TOC: PINFTOCArray;
  228. TOCSize: longint;
  229. begin
  230. F^.Seek(Header.TOCArrayOfs); TOCSize:=Header.TOCStrTabSize;
  231. OK:=(F^.Status=stOK);
  232. if OK then
  233. begin
  234. GetMem(TOC,TOCSize);
  235. F^.Read(TOC^,TOCSize);
  236. OK:=(F^.Status=stOK);
  237. I:=0;
  238. while OK and (I<Header.NumTOC) do
  239. begin
  240. F^.Seek(TOC^[I]);
  241. OK:=(F^.Status=stOK);
  242. if OK then
  243. begin
  244. StartOfs:=F^.GetPos;
  245. F^.Read(TE,sizeof(TE));
  246. OK:=(F^.Status=stOK);
  247. end;
  248. if OK and ((TE.Flags and inf_tef_Extended)<>0) then
  249. begin
  250. F^.Read(W,sizeof(W));
  251. Count:=0;
  252. if (W and 1)<>0 then Inc(Count,5);
  253. if (W and 2)<>0 then Inc(Count,5);
  254. if (W and 4)<>0 then Inc(Count,2);
  255. if (W and 8)<>0 then Inc(Count,2);
  256. F^.Seek(F^.GetPos+Count);
  257. OK:=(F^.Status=stOK);
  258. end;
  259. if OK then
  260. begin
  261. SubSlotsSize:=sizeof(Word)*TE.NumSlots;
  262. GetMem(SubSlots,SubSlotsSize);
  263. F^.Read(SubSlots^,SubSlotsSize);
  264. FillChar(C,sizeof(C),0);
  265. F^.Read(C,Max(0,TE.Size-(F^.GetPos-StartOfs)));
  266. OK:=(F^.Status=stOK);
  267. if OK then
  268. begin
  269. S:=StrPas(@C);
  270. AddTopic(I,StartOfs,S,SubSlots,SubSlotsSize);
  271. if (S<>'') and (not ContainsNonASCIIChar(S)) then
  272. AddIndexEntry(S,I);
  273. { !}
  274. end;
  275. FreeMem(SubSlots,SubSlotsSize);
  276. end;
  277. Inc(I);
  278. end;
  279. FreeMem(TOC,TOCSize); TOC:=nil;
  280. end;
  281. ReadTOC:=OK;
  282. end;
  283. function TOS2HelpFile.ReadTopicRec(FileOfs: longint; Topic: PTopic; Lines: PUnsortedStringCollection): boolean;
  284. var Line: string;
  285. CharsInLine: sw_integer;
  286. LeftMargin: byte;
  287. TextStyle,TextColor: byte;
  288. InMonospace: boolean;
  289. Align: (alLeft,alRight,alCenter);
  290. LineNo: longint;
  291. procedure FlushLine;
  292. begin
  293. if Line<>'' then Lines^.InsertStr(Line);
  294. Line:='';
  295. end;
  296. procedure AddChar(C: AnsiChar);
  297. begin
  298. if length(Line)>=255 then FlushLine;
  299. Line:=Line+C;
  300. end;
  301. procedure AddString(const S: string);
  302. var I: sw_integer;
  303. begin
  304. for I:=1 to length(S) do
  305. AddChar(S[I]);
  306. end;
  307. procedure AddTextChar(C: AnsiChar);
  308. begin
  309. if (C=hscLineBreak) then
  310. begin
  311. case Align of
  312. alRight : AddChar(hscRight);
  313. alCenter: AddChar(hscCenter);
  314. end;
  315. end;
  316. if (CharsInLine=0) and (C<>hscLineBreak) then
  317. begin
  318. if (LeftMargin>0) then
  319. begin
  320. AddString(CharStr(#255,LeftMargin)+hscLineStart);
  321. Inc(CharsInLine,LeftMargin);
  322. end else
  323. if InMonospace then
  324. begin
  325. AddChar(' ');
  326. Inc(CharsInLine);
  327. end;
  328. end;
  329. AddChar(C);
  330. if C=hscLineBreak then
  331. begin
  332. CharsInLine:=0;
  333. Inc(LineNo);
  334. end
  335. else
  336. Inc(CharsInLine);
  337. end;
  338. procedure AddText(const S: string);
  339. var I: sw_integer;
  340. begin
  341. for I:=1 to length(S) do
  342. AddTextChar(S[I]);
  343. end;
  344. var H: TINFTopicHeader;
  345. Text: PByteArray;
  346. TextOfs: longint;
  347. Dict: PWordArray;
  348. Spacing: boolean;
  349. function NextByte: byte;
  350. begin
  351. NextByte:=Text^[TextOfs];
  352. Inc(TextOfs);
  353. end;
  354. procedure ReadBytes(DataPtr: pointer; NumBytes: sw_integer);
  355. var I: sw_integer;
  356. begin
  357. for I:=1 to NumBytes do
  358. if Assigned(DataPtr) then
  359. PByteArray(DataPtr)^[I-1]:=NextByte
  360. else
  361. NextByte;
  362. end;
  363. procedure AddWord(LocalIndex: word);
  364. begin
  365. AddText(GetStr(Dictionary^.At(Dict^[LocalIndex])));
  366. if Spacing and not InMonospace then AddTextChar(' ');
  367. end;
  368. var
  369. DictSize,EscStartOfs: longint;
  370. OK: boolean;
  371. EH: TINFEscHeader;
  372. B,Color: byte;
  373. W: word;
  374. CurLinkCtx: longint;
  375. InTempMargin: boolean;
  376. begin
  377. F^.Reset;
  378. F^.Seek(FileOfs);
  379. OK:=(F^.Status=stOK);
  380. if OK then
  381. begin
  382. F^.Read(H,sizeof(H));
  383. OK:=(F^.Status=stOK);
  384. end;
  385. if OK then
  386. begin
  387. LineNo:=0;
  388. Line:=''; LeftMargin:=0;
  389. InTempMargin:=false;
  390. CharsInLine:=0; TextStyle:=0; TextColor:=0; Align:=alLeft;
  391. CurLinkCtx:=-1; InMonospace:=false;
  392. DictSize:=H.NumLocalDict*sizeof(Dict^[0]);
  393. GetMem(Text,H.TextSize);
  394. GetMem(Dict,DictSize);
  395. F^.Read(Text^,H.TextSize);
  396. F^.Seek(H.LocalDictPos);
  397. F^.Read(Dict^,DictSize);
  398. OK:=(F^.Status=stOK);
  399. TextOfs:=0; Spacing:=true;
  400. while OK and (TextOfs<H.TextSize) do
  401. begin
  402. B:=NextByte;
  403. if (B<H.NumLocalDict) then
  404. begin
  405. AddWord(B);
  406. end else
  407. case B of
  408. $fa : begin
  409. if (LineNo>0) then
  410. AddTextChar(hscLineBreak);
  411. if InTempMargin then
  412. LeftMargin:=0;
  413. AddTextChar(hscLineBreak);
  414. Spacing:=true;
  415. end;
  416. $fb : { ??? };
  417. $fc : Spacing:=not Spacing;
  418. $fd : begin
  419. AddTextChar(hscLineBreak);
  420. Spacing:=true;
  421. end;
  422. $fe : AddChar(' ');
  423. $ff : begin
  424. EscStartOfs:=TextOfs;
  425. ReadBytes(@EH,sizeof(EH));
  426. case EH.EscCode of
  427. $02,$11,$12 :
  428. begin
  429. { set left margin }
  430. if EH.EscCode=$11 then
  431. AddTextChar(hscLineBreak);
  432. LeftMargin:=NextByte;
  433. end;
  434. $03 :
  435. { right margin, not used }
  436. NextByte;
  437. $04 :
  438. begin
  439. TextStyle:=NextByte;
  440. if (TextStyle=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  441. AddChar(hscNormText)
  442. else
  443. AddText(hscTextColor+chr(Color));
  444. end;
  445. $05 :
  446. begin
  447. W:=word(NextByte)*256+NextByte;
  448. AddChar(hscLink);
  449. CurLinkCtx:=W;
  450. end;
  451. $08 :
  452. if CurLinkCtx<>-1 then
  453. begin
  454. AddChar(hscLink);
  455. AddLinkToTopic(Topic,ID,CurLinkCtx);
  456. end;
  457. $0b :
  458. begin
  459. if CharsInLine>0 then
  460. AddTextChar(hscLineBreak);
  461. AddTextChar(hscLineBreak);
  462. AddChar(hscCode);
  463. InMonospace:=true;
  464. end;
  465. $0c :
  466. begin
  467. AddChar(hscCode);
  468. InMonospace:=false;
  469. end;
  470. $0d :
  471. begin
  472. TextColor:=NextByte;
  473. if (TextColor=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  474. AddChar(hscNormText)
  475. else
  476. AddText(hscTextColor+chr(Color));
  477. end;
  478. $0e :
  479. begin
  480. AddText(hscLineBreak+'[img]'+hscLineBreak);
  481. end;
  482. $1a :
  483. begin
  484. if CharsInLine>0 then AddText(hscLineBreak);
  485. case NextByte of
  486. 1 : Align:=alLeft;
  487. 2 : Align:=alRight;
  488. 4 : Align:=alCenter;
  489. end;
  490. Spacing:=false;
  491. end;
  492. $1b :
  493. begin
  494. Spacing:=true;
  495. end;
  496. $1c :
  497. begin
  498. LeftMargin:=CharsInLine;
  499. InTempMargin:=true;
  500. end;
  501. end;
  502. TextOfs:=EscStartOfs+EH.EscLen;
  503. end;
  504. end;
  505. end;
  506. if CharsInLine>0 then
  507. AddTextChar(hscLineBreak);
  508. AddTextChar(hscLineBreak);
  509. FlushLine;
  510. FreeMem(Dict,DictSize);
  511. FreeMem(Text,H.TextSize);
  512. end;
  513. F^.Reset;
  514. ReadTopicRec:=OK;
  515. end;
  516. function TOS2HelpFile.ReadTopic(T: PTopic): boolean;
  517. var OK: boolean;
  518. I,NumSlots,Idx: sw_integer;
  519. TopicOfs: longint;
  520. L: PUnsortedStringCollection;
  521. Title: string;
  522. begin
  523. OK:=false;
  524. NumSlots:=T^.ExtDataSize div 2; { extdata is array of word }
  525. New(L, Init(100,100));
  526. Title:=GetStr(T^.Param);
  527. if Title<>'' then
  528. begin
  529. L^.InsertStr(' '+Title+' Ü'+hscLineBreak);
  530. L^.InsertStr(' '+CharStr('ß',length(Title)+3)+hscLineBreak);
  531. end;
  532. if 0<T^.HelpCtx then
  533. begin
  534. L^.InsertStr(hscLink+'[previous topic]'+hscLink+' ');
  535. AddLinkToTopic(T,ID,T^.HelpCtx-1);
  536. end;
  537. if T^.HelpCtx<Header.NumTOC then
  538. begin
  539. L^.InsertStr(hscLink+'[next topic]'+hscLink);
  540. AddLinkToTopic(T,ID,T^.HelpCtx+1);
  541. end;
  542. L^.InsertStr(hscLineBreak);
  543. for I:=0 to NumSlots-1 do
  544. begin
  545. Idx:=PWordArray(T^.ExtData)^[I];
  546. TopicOfs:=Slots^[Idx];
  547. OK:=ReadTopicRec(TopicOfs,T,L);
  548. if not OK then
  549. Break;
  550. end;
  551. if OK then BuildTopic(L,T);
  552. Dispose(L, Done);
  553. ReadTopic:=OK;
  554. end;
  555. destructor TOS2HelpFile.Done;
  556. begin
  557. if Assigned(Slots) then FreeMem(Slots, SlotsSize); Slots:=nil;
  558. if Assigned(Dictionary) then Dispose(Dictionary, Done); Dictionary:=nil;
  559. if Assigned(F) then Dispose(F, Done); F:=nil;
  560. inherited Done;
  561. end;
  562. function CreateProc(const FileName,Param: string;Index : longint): PHelpFile;
  563. begin
  564. CreateProc:=New(POS2HelpFile, Init(FileName,Index));
  565. end;
  566. procedure RegisterHelpType;
  567. begin
  568. RegisterHelpFileType({$ifdef FPC}@{$endif}CreateProc);
  569. end;
  570. END.