mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-10 02:28:22 +02:00
325 lines
9.5 KiB
ObjectPascal
325 lines
9.5 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal Integrated Development Environment
|
|
Copyright (c) 2008 by Marco van de Voort
|
|
|
|
Wrapper for CHM reading to avoid having to import Delphi units into whtmlhlp,
|
|
which can cause all kinds of namespace conflicts.
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
unit wchmhwrap;
|
|
|
|
interface
|
|
{$Mode Delphi}
|
|
|
|
Uses wutils,whelp,whtml,SysUtils,ChmReader,ChmSiteMap,Classes;
|
|
|
|
Type
|
|
// TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;
|
|
|
|
TChmWrapper = Class
|
|
private
|
|
ffs : Classes.TFileStream;
|
|
fchmr : TChmReader;
|
|
findex : TChmSiteMap;
|
|
ftopic : TChmSiteMap;
|
|
floaded : boolean;
|
|
fileid : integer;
|
|
fshortname : string;
|
|
flongname : string;
|
|
fTopicLinks : PTopicLinkCollection;
|
|
public
|
|
constructor Create(name:String;aid:integer;TopicLinks:PTopicLinkCollection);
|
|
function LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
|
|
function GetTopic(name:string):PMemoryTextFile;
|
|
destructor Destroy;override;
|
|
end;
|
|
|
|
function combinepaths(relpath,basepath:String):String;
|
|
function CHMResolve( href: ansistring; var AFileId,ALinkId : longint):boolean;
|
|
|
|
function stringreplace(const s:ansistring;const oldstr:ansistring; const newstr:ansistring):ansistring;
|
|
implementation
|
|
|
|
var CHMIndex : TStringList; // list to register open CHMs.
|
|
|
|
|
|
function combinepaths(relpath,basepath:String):String;
|
|
|
|
begin
|
|
{$ifdef combinedebug}
|
|
debugmessageS({$i %file%},'combine in "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
|
|
if relpath='' then exit;
|
|
if relpath[length(relpath)]<>'/' Then
|
|
basepath:=extractfiledir(basepath);
|
|
while (length(relpath)>0) and (copy(relpath,1,3)='../') do
|
|
begin
|
|
basepath:=extractfiledir(basepath);
|
|
delete(relpath,1,3);
|
|
end;
|
|
|
|
{$ifdef combinedebug}
|
|
debugmessageS({$i %file%},'combine out "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
if (length(basepath)>0) and (length(relpath)>0) then
|
|
begin
|
|
if (relpath[1]<>'/') and (basepath[length(basepath)]<>'/') then
|
|
basepath:=basepath+'/';
|
|
{$ifdef combinedebug}
|
|
debugmessageS({$i %file%},'combine out2 "'+relpath+'" and "'+basepath+'"',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
end;
|
|
|
|
result:=basepath+relpath;
|
|
end;
|
|
|
|
Constructor TChmWrapper.Create(name:string;aid:integer;TopicLinks:PTopicLinkCollection);
|
|
|
|
begin
|
|
ffs:=Classes.TFileStream.create(name,fmOpenRead or fmsharedenynone);
|
|
fchmr:=TChmReader.Create(ffs,True); // owns ffs
|
|
findex:=nil;
|
|
FTopicLinks:=TopicLinks;
|
|
if not fchmr.isvalidfile then
|
|
begin
|
|
freeandnil(fchmr);
|
|
freeandnil(ffs);
|
|
exit;
|
|
end;
|
|
fileid:=aid;
|
|
flongname:=name;
|
|
fshortname:=lowercase(extractfilename(name)); // We assume ms-its: urls are case insensitive wrt filename.
|
|
chmindex.addobject(fshortname,self);
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper.Create: before sitemap creation '+fshortname+' id='+inttostr(aid),{$i %line%},'1',0,0);
|
|
{$endif}
|
|
findex:=TChmSiteMap.create(stindex);
|
|
ftopic:=TChmSiteMap.create(sttoc);
|
|
floaded:=false;
|
|
end;
|
|
|
|
function TChmWrapper.LoadIndex(id:integer;TopicLinks: PTopicLinkCollection;IndexEntries : PUnsortedIndexEntryCollection;helpfacility:PHelpFacility):boolean;
|
|
function FormatAlias(Alias: string): string;
|
|
begin
|
|
if Assigned(HelpFacility) then
|
|
if length(Alias)>HelpFacility^.IndexTabSize-4 then
|
|
Alias:=Trim(copy(Alias,1,HelpFacility^.IndexTabSize-4-2))+'..';
|
|
// if (length(alias)>0) and (alias[1]<>'/') then Alias:='/'+alias;
|
|
FormatAlias:=Alias;
|
|
end;
|
|
|
|
var
|
|
m : Classes.TMemoryStream;
|
|
i,j : integer;
|
|
item : TChmSiteMapItem;
|
|
tli: integer;
|
|
s : String;
|
|
begin
|
|
result:=false;
|
|
if floaded then exit;
|
|
if not assigned (fchmr) then exit;
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: indexfilename:'+fchmr.indexfile,{$i %line%},'1',0,0);
|
|
{$endif}
|
|
|
|
|
|
findex:=fchmr.GetIndexSitemap(false);
|
|
(* m:=fchmr.getobject(fchmr.indexfile);
|
|
try
|
|
if assigned(m) then
|
|
begin
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: stream size loaded :'+inttostr(m.size),{$i %line%},'1',0,0);
|
|
{$endif}
|
|
findex.loadfromStream(m);
|
|
end;
|
|
finally
|
|
freeandnil(m);
|
|
end;
|
|
*)
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: loadindex after final ',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
|
|
tli:=TopicLinks^.AddItem(fchmr.defaultpage);
|
|
TLI:=EncodeHTMLCtx(ID,TLI+1);
|
|
IndexEntries^.Insert(NewIndexEntry( FormatAlias('Table of contents'),ID,TLI));
|
|
if findex= Nil Then
|
|
begin
|
|
floaded:=true;
|
|
exit(true);
|
|
end;
|
|
if assigned(findex.items) and (findex.items.count>0) Then
|
|
for i:=0 to findex.items.count-1 do
|
|
begin
|
|
item:=findex.items.item[i];
|
|
s:=formatalias(item.text);
|
|
if s<>'' then
|
|
begin
|
|
if (length(item.local)>0) and (item.local[1]<>'/') then
|
|
tli:=TopicLinks^.AddItem('/'+item.local)
|
|
else
|
|
tli:=TopicLinks^.AddItem(item.local);
|
|
TLI:=EncodeHTMLCtx(ID,TLI+1);
|
|
IndexEntries^.Insert(NewIndexEntry( FormatAlias(item.text),ID,TLI));
|
|
end;
|
|
end;
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: endloadindex ',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
floaded:=true;
|
|
result:=true;
|
|
end;
|
|
|
|
procedure splitline(idestream:PMemoryTextFile;s:ansistring);
|
|
|
|
function scanvalue:integer; // searches for a possible breaking point left of char 255.
|
|
var n,i : integer;
|
|
lastpoint:integer;
|
|
inquote : boolean;
|
|
begin
|
|
lastpoint:=-1;
|
|
n:=length(s);
|
|
if n>250 then n:=250;
|
|
i:=1; inquote:=false;
|
|
while (i<=n) do
|
|
begin
|
|
while (s[i]<>' ') and (s[i]<>'"') and (i<=n) do inc(i);
|
|
if (s[i]=' ') and not inquote then lastpoint:=i;
|
|
if (s[i]='"') then inquote:=not inquote;
|
|
inc(i);
|
|
end;
|
|
scanvalue:=lastpoint;
|
|
end;
|
|
|
|
var position : longint;
|
|
|
|
begin
|
|
position:=0;
|
|
while (length(s)>250) and (position<>-1) do
|
|
begin
|
|
position:=scanvalue;
|
|
if position<>-1 then
|
|
begin
|
|
idestream.addline(copy(s,1,position-1));
|
|
delete(s,1,position);
|
|
end;
|
|
end;
|
|
if length(s)<>0 then
|
|
idestream.addline(s);
|
|
end;
|
|
|
|
function TChmWrapper.GetTopic(name:string):PMemoryTextFile;
|
|
|
|
var
|
|
m : Classes.TMemorystream;
|
|
linedata:Classes.TStringList;
|
|
i : integer;
|
|
begin
|
|
result:=nil;
|
|
if not assigned(fchmr) or (name='') then exit;
|
|
|
|
If (name[1]<>'/') and (copy(name,1,7)<>'ms-its:') Then
|
|
name:='/'+name;
|
|
linedata:=Classes.TStringList.create;
|
|
try
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: Getting file '+name,{$i %line%},'1',0,0);
|
|
{$endif}
|
|
// if uppercase(name)='TABLE OF CONTENTS' Then
|
|
// m:=fchmr.getobject(fchmr.tocfile)
|
|
// else
|
|
m:=fchmr.getobject(name);
|
|
|
|
if not assigned(m) then exit;
|
|
linedata.loadfromstream(m);
|
|
result:=new(PMemoryTextFile,Init);
|
|
for i:=0 to linedata.count-1 do
|
|
begin
|
|
if length(linedata[i])>250 Then
|
|
splitline(result,linedata[i])
|
|
else
|
|
result.addline(linedata[i]);
|
|
end;
|
|
finally
|
|
m.free;
|
|
linedata.free;
|
|
end;
|
|
end;
|
|
|
|
destructor TChmWrapper.Destroy;
|
|
|
|
var i : integer;
|
|
begin
|
|
i:=chmindex.indexof(fshortname);
|
|
if i<>-1 then
|
|
begin
|
|
chmindex.delete(i);
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: deregistering '+fshortname,{$i %line%},'1',0,0);
|
|
{$endif}
|
|
end;
|
|
freeandnil(ftopic);
|
|
freeandnil(findex);
|
|
freeandnil(fchmr);
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: destroying ',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
function CHMResolve( href: ansistring; var AFileId,ALinkId : longint):boolean;
|
|
|
|
var filename, restlink : ansistring;
|
|
I :integer;
|
|
chmw: TCHMWrapper;
|
|
begin
|
|
result:=false;
|
|
if copy(href,1,7)='ms-its:' then
|
|
begin
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: resolving '+href,{$i %line%},'1',0,0);
|
|
{$endif}
|
|
|
|
delete(href,1,7);
|
|
i:=pos('::',href);
|
|
if i<>0 then
|
|
begin
|
|
filename:=lowercase(copy(href,1,i-1));
|
|
restlink:=lowercase(copy(href,i+2,length(href)-(I+2)+1));
|
|
i:=chmindex.indexof(filename);
|
|
if i<>-1 then
|
|
begin
|
|
{$ifdef wdebug}
|
|
debugmessageS({$i %file%},'TCHMWrapper: resolving '+filename+' '+inttostr(i),{$i %line%},'1',0,0);
|
|
debugmessageS({$i %file%},'TCHMWrapper: resolving '+restlink+' ',{$i %line%},'1',0,0);
|
|
{$endif}
|
|
chmw:=TCHMWrapper(chmindex.objects[i]);
|
|
Afileid:=chmw.fileid;
|
|
alinkid:=chmw.fTopicLinks.additem(restlink);
|
|
result:=true;
|
|
end;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
function stringreplace(const s:ansistring;const oldstr:ansistring; const newstr:ansistring):ansistring;
|
|
|
|
begin
|
|
result:=sysutils.stringreplace(s,oldstr,newstr,[rfreplaceall]);
|
|
end;
|
|
initialization
|
|
ChmIndex:=TStringlist.create;
|
|
ChmIndex.sorted:=true;
|
|
finalization
|
|
ChmIndex.Free;
|
|
end.
|