wos2help.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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 = DefINFGetAttrColor;
  111. implementation
  112. uses CallSpec;
  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 char;
  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,J,L,Count: longint;
  220. TE: TINFTOCEntry;
  221. W: word;
  222. C: array[0..255] of char;
  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(SubSlots^[0])*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. LastTextChar: char;
  286. CharsInLine: sw_integer;
  287. LeftMargin,RightMargin: byte;
  288. TextStyle,TextColor: byte;
  289. InMonospace: boolean;
  290. Align: (alLeft,alRight,alCenter);
  291. LineNo: longint;
  292. procedure FlushLine;
  293. begin
  294. if Line<>'' then Lines^.InsertStr(Line);
  295. Line:='';
  296. end;
  297. procedure AddChar(C: char);
  298. begin
  299. if length(Line)>=255 then FlushLine;
  300. Line:=Line+C;
  301. end;
  302. procedure AddString(const S: string);
  303. var I: sw_integer;
  304. begin
  305. for I:=1 to length(S) do
  306. AddChar(S[I]);
  307. end;
  308. procedure AddTextChar(C: char);
  309. begin
  310. if (C=hscLineBreak) then
  311. begin
  312. case Align of
  313. alRight : AddChar(hscRight);
  314. alCenter: AddChar(hscCenter);
  315. end;
  316. end;
  317. if (CharsInLine=0) and (C<>hscLineBreak) then
  318. begin
  319. if (LeftMargin>0) then
  320. begin
  321. AddString(CharStr(#255,LeftMargin)+hscLineStart);
  322. Inc(CharsInLine,LeftMargin);
  323. end else
  324. if InMonospace then
  325. begin
  326. AddChar(' ');
  327. Inc(CharsInLine);
  328. end;
  329. end;
  330. AddChar(C);
  331. LastTextChar:=C;
  332. if C=hscLineBreak then
  333. begin
  334. CharsInLine:=0;
  335. Inc(LineNo);
  336. end
  337. else
  338. Inc(CharsInLine);
  339. end;
  340. procedure AddText(const S: string);
  341. var I: sw_integer;
  342. begin
  343. for I:=1 to length(S) do
  344. AddTextChar(S[I]);
  345. end;
  346. var H: TINFTopicHeader;
  347. Text: PByteArray;
  348. TextOfs: longint;
  349. Dict: PWordArray;
  350. Spacing: boolean;
  351. function NextByte: byte;
  352. var B: byte;
  353. begin
  354. NextByte:=Text^[TextOfs];
  355. Inc(TextOfs);
  356. end;
  357. procedure ReadBytes(DataPtr: pointer; NumBytes: sw_integer);
  358. var I: sw_integer;
  359. begin
  360. for I:=1 to NumBytes do
  361. if Assigned(DataPtr) then
  362. PByteArray(DataPtr)^[I-1]:=NextByte
  363. else
  364. NextByte;
  365. end;
  366. procedure AddWord(LocalIndex: word);
  367. begin
  368. AddText(GetStr(Dictionary^.At(Dict^[LocalIndex])));
  369. if Spacing and not InMonospace then AddTextChar(' ');
  370. end;
  371. var
  372. DictSize,EscStartOfs: longint;
  373. OK: boolean;
  374. EH: TINFEscHeader;
  375. B,Color: byte;
  376. W: word;
  377. CurLinkCtx: longint;
  378. InTempMargin: boolean;
  379. begin
  380. F^.Reset;
  381. F^.Seek(FileOfs);
  382. OK:=(F^.Status=stOK);
  383. if OK then
  384. begin
  385. F^.Read(H,sizeof(H));
  386. OK:=(F^.Status=stOK);
  387. end;
  388. if OK then
  389. begin
  390. LineNo:=0;
  391. Line:=''; LeftMargin:=0; RightMargin:=0; LastTextChar:=hscLineBreak;
  392. CharsInLine:=0; TextStyle:=0; TextColor:=0; Align:=alLeft;
  393. CurLinkCtx:=-1; InMonospace:=false;
  394. DictSize:=H.NumLocalDict*sizeof(Dict^[0]);
  395. GetMem(Text,H.TextSize);
  396. GetMem(Dict,DictSize);
  397. F^.Read(Text^,H.TextSize);
  398. F^.Seek(H.LocalDictPos);
  399. F^.Read(Dict^,DictSize);
  400. OK:=(F^.Status=stOK);
  401. TextOfs:=0; Spacing:=true;
  402. while OK and (TextOfs<H.TextSize) do
  403. begin
  404. B:=NextByte;
  405. if (B<H.NumLocalDict) then
  406. begin
  407. AddWord(B);
  408. end else
  409. case B of
  410. $fa : begin
  411. if (LineNo>0) then
  412. AddTextChar(hscLineBreak);
  413. if InTempMargin then
  414. LeftMargin:=0;
  415. AddTextChar(hscLineBreak);
  416. Spacing:=true;
  417. end;
  418. $fb : { ??? };
  419. $fc : Spacing:=not Spacing;
  420. $fd : begin
  421. AddTextChar(hscLineBreak);
  422. Spacing:=true;
  423. end;
  424. $fe : AddChar(' ');
  425. $ff : begin
  426. EscStartOfs:=TextOfs;
  427. ReadBytes(@EH,sizeof(EH));
  428. case EH.EscCode of
  429. $02,$11,$12 :
  430. begin
  431. { set left margin }
  432. if EH.EscCode=$11 then
  433. AddTextChar(hscLineBreak);
  434. LeftMargin:=NextByte;
  435. end;
  436. $03 :
  437. RightMargin:=NextByte;
  438. $04 :
  439. begin
  440. TextStyle:=NextByte;
  441. if (TextStyle=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  442. AddChar(hscNormText)
  443. else
  444. AddText(hscTextColor+chr(Color));
  445. end;
  446. $05 :
  447. begin
  448. W:=word(NextByte)*256+NextByte;
  449. AddChar(hscLink);
  450. CurLinkCtx:=W;
  451. end;
  452. $08 :
  453. if CurLinkCtx<>-1 then
  454. begin
  455. AddChar(hscLink);
  456. AddLinkToTopic(Topic,ID,CurLinkCtx);
  457. end;
  458. $0b :
  459. begin
  460. if CharsInLine>0 then
  461. AddTextChar(hscLineBreak);
  462. AddTextChar(hscLineBreak);
  463. AddChar(hscCode);
  464. InMonospace:=true;
  465. end;
  466. $0c :
  467. begin
  468. AddChar(hscCode);
  469. InMonospace:=false;
  470. end;
  471. $0d :
  472. begin
  473. TextColor:=NextByte;
  474. if (TextColor=0) or (not INFGetAttrColor(TextStyle,TextColor,Color)) then
  475. AddChar(hscNormText)
  476. else
  477. AddText(hscTextColor+chr(Color));
  478. end;
  479. $0e :
  480. begin
  481. AddText(hscLineBreak+'[img]'+hscLineBreak);
  482. end;
  483. $1a :
  484. begin
  485. if CharsInLine>0 then AddText(hscLineBreak);
  486. case NextByte of
  487. 1 : Align:=alLeft;
  488. 2 : Align:=alRight;
  489. 4 : Align:=alCenter;
  490. end;
  491. Spacing:=false;
  492. end;
  493. $1b :
  494. begin
  495. Spacing:=true;
  496. end;
  497. $1c :
  498. begin
  499. LeftMargin:=CharsInLine;
  500. InTempMargin:=true;
  501. end;
  502. end;
  503. TextOfs:=EscStartOfs+EH.EscLen;
  504. end;
  505. end;
  506. end;
  507. if CharsInLine>0 then
  508. AddTextChar(hscLineBreak);
  509. AddTextChar(hscLineBreak);
  510. FlushLine;
  511. FreeMem(Dict,DictSize);
  512. FreeMem(Text,H.TextSize);
  513. end;
  514. F^.Reset;
  515. ReadTopicRec:=OK;
  516. end;
  517. function TOS2HelpFile.ReadTopic(T: PTopic): boolean;
  518. var OK: boolean;
  519. I,NumSlots,Idx: sw_integer;
  520. TopicOfs: longint;
  521. L: PUnsortedStringCollection;
  522. Title: string;
  523. begin
  524. OK:=false;
  525. NumSlots:=T^.ExtDataSize div 2; { extdata is array of word }
  526. New(L, Init(100,100));
  527. Title:=GetStr(T^.Param);
  528. if Title<>'' then
  529. begin
  530. L^.InsertStr(' '+Title+' Ü'+hscLineBreak);
  531. L^.InsertStr(' '+CharStr('ß',length(Title)+3)+hscLineBreak);
  532. end;
  533. if 0<T^.HelpCtx then
  534. begin
  535. L^.InsertStr(hscLink+'[previous topic]'+hscLink+' ');
  536. AddLinkToTopic(T,ID,T^.HelpCtx-1);
  537. end;
  538. if T^.HelpCtx<Header.NumTOC then
  539. begin
  540. L^.InsertStr(hscLink+'[next topic]'+hscLink);
  541. AddLinkToTopic(T,ID,T^.HelpCtx+1);
  542. end;
  543. L^.InsertStr(hscLineBreak);
  544. for I:=0 to NumSlots-1 do
  545. begin
  546. Idx:=PWordArray(T^.ExtData)^[I];
  547. TopicOfs:=Slots^[Idx];
  548. OK:=ReadTopicRec(TopicOfs,T,L);
  549. if not OK then
  550. Break;
  551. end;
  552. if OK then BuildTopic(L,T);
  553. Dispose(L, Done);
  554. ReadTopic:=OK;
  555. end;
  556. destructor TOS2HelpFile.Done;
  557. begin
  558. if Assigned(Slots) then FreeMem(Slots, SlotsSize); Slots:=nil;
  559. if Assigned(Dictionary) then Dispose(Dictionary, Done); Dictionary:=nil;
  560. if Assigned(F) then Dispose(F, Done); F:=nil;
  561. inherited Done;
  562. end;
  563. END.
  564. {
  565. $Log$
  566. Revision 1.2 2000-10-31 22:35:56 pierre
  567. * New big merge from fixes branch
  568. Revision 1.1.2.1 2000/09/18 19:50:37 pierre
  569. New file from Gabor
  570. }