wos2help.pas 18 KB

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