mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-07 13:59:06 +02:00
1942 lines
62 KiB
ObjectPascal
1942 lines
62 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code 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. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Caches for directories.
|
|
The codetools work directory based, that means all define templates are the
|
|
same for all files in a directory.
|
|
That's why all the units in a directory use the same search paths and find
|
|
the same files.
|
|
|
|
}
|
|
unit DirectoryCacher;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL + FCL
|
|
Classes, SysUtils, Laz_AVL_Tree,
|
|
// CodeTools
|
|
FileProcs,
|
|
// LazUtils
|
|
LazUTF8, LazFileCache, LazFileUtils, LazUtilities, AvgLvlTree, LazDbgLog;
|
|
|
|
// verbosity
|
|
{ $DEFINE CTDEBUG}
|
|
{ $DEFINE ShowTriedFiles}
|
|
{ $DEFINE ShowTriedUnits}
|
|
{ $DEFINE DebugDirCacheFindUnitSource}
|
|
|
|
{$ifdef Windows}
|
|
{$define CaseInsensitiveFilenames}
|
|
{$endif}
|
|
{$IF defined(CaseInsensitiveFilenames) or defined(darwin)}
|
|
{$DEFINE NotLiteralFilenames}
|
|
{$ENDIF}
|
|
|
|
type
|
|
TCTDirCacheString = (
|
|
ctdcsUnitPath,
|
|
ctdcsSrcPath,
|
|
ctdcsIncludePath,
|
|
ctdcsCompleteSrcPath, // including unit path, src path and compiled src paths
|
|
ctdcsUnitLinks,
|
|
ctdcsUnitSet,
|
|
ctdcsFPCUnitPath, // unit paths reported by FPC
|
|
ctdcsNamespaces
|
|
);
|
|
|
|
TCTDirCacheStringRecord = record
|
|
Value: string;
|
|
ConfigTimeStamp: integer;
|
|
end;
|
|
|
|
TCTDirectoryUnitSources = (
|
|
ctdusUnitNormal, // e.g. AUnitName (case depends on OS) -> filename
|
|
ctdusUnitCaseInsensitive, // AUnitName case insensitive -> filename
|
|
ctdusInFilenameNormal, // unit 'in' filename -> filename
|
|
ctdusInFilenameCaseInsensitive, // unit 'in' filename case insensitive -> filename
|
|
ctdusUnitFileNormal, // AUnitName.ext (case depends on OS) -> filename
|
|
ctdusUnitFileCaseInsensitive, // AUnitName.ext case insensitive -> filename
|
|
ctdusPPUNormal, // UnitName (case depends on OS) => filename
|
|
ctdusPPUCaseInsensitive // UnitName case insensitive => filename
|
|
);
|
|
|
|
const
|
|
ctdusCaseNormal = [ctdusUnitNormal,
|
|
ctdusInFilenameNormal,
|
|
ctdusUnitFileNormal,
|
|
ctdusPPUNormal];
|
|
ctdusCaseInsensitive = [ctdusUnitCaseInsensitive,
|
|
ctdusInFilenameCaseInsensitive,
|
|
ctdusUnitFileCaseInsensitive,
|
|
ctdusPPUCaseInsensitive];
|
|
|
|
type
|
|
|
|
{ TUnitFileNameLink }
|
|
|
|
TUnitFileNameLink = class
|
|
public
|
|
Unit_Name: string;
|
|
Filename: string;
|
|
function CalcMemSize: PtrUInt;
|
|
end;
|
|
|
|
TCTDirCacheUnitSrcRecord = record
|
|
Files: TStringToStringTree;
|
|
ConfigTimeStamp: integer;
|
|
FileTimeStamp: integer;
|
|
end;
|
|
|
|
TCTDirectoryListingAttr = longint;
|
|
PCTDirectoryListingAttr = ^TCTDirectoryListingAttr;
|
|
TCTDirectoryListingSize = int64;
|
|
PCTDirectoryListingSize = ^TCTDirectoryListingSize;
|
|
|
|
TCTDirectoryListingHeader = packed record
|
|
Time: TCTFileAgeTime;
|
|
Attr: TCTDirectoryListingAttr;
|
|
Size: TCTDirectoryListingSize;
|
|
end;
|
|
PCTDirectoryListingHeader = ^TCTDirectoryListingHeader;
|
|
|
|
{ TCTDirectoryListing }
|
|
|
|
TCTDirectoryListing = class
|
|
public
|
|
FileTimeStamp: integer;
|
|
Files: PChar; { each file: TCTDirectoryListingHeader+filename+#0
|
|
sorted: first case insensitive then sensitive }
|
|
Count: integer; // number of filenames
|
|
Size: PtrInt; // length of Files in bytes
|
|
Starts: PInteger; // offsets of each file in Files
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
function CalcMemSize: PtrUInt;
|
|
function GetFilename(Index: integer): PChar;
|
|
function GetTime(Index: integer): TCTFileAgeTime;
|
|
function GetAttr(Index: integer): TCTDirectoryListingAttr;
|
|
function GetSize(Index: integer): TCTDirectoryListingSize;
|
|
end;
|
|
|
|
TCTOnIterateFile = procedure(const Filename: string) of object;
|
|
TCTDirectoryCachePool = class;
|
|
|
|
|
|
{ TCTDirectoryCache }
|
|
|
|
TCTDirectoryCache = class
|
|
private
|
|
FDirectory: string;
|
|
FPool: TCTDirectoryCachePool;
|
|
FRefCount: integer;
|
|
FStrings: array[TCTDirCacheString] of TCTDirCacheStringRecord;
|
|
FUnitLinksTree: TAVLTree; // tree of TUnitFileNameLink
|
|
FUnitLinksTreeTimeStamp: integer;
|
|
FListing: TCTDirectoryListing;
|
|
FUnitSources: array[TCTDirectoryUnitSources] of TCTDirCacheUnitSrcRecord;
|
|
function GetStrings(const AStringType: TCTDirCacheString): string;
|
|
procedure SetStrings(const AStringType: TCTDirCacheString;
|
|
const AValue: string);
|
|
procedure ClearUnitLinks;
|
|
function GetUnitSourceCacheValue(const UnitSrc: TCTDirectoryUnitSources;
|
|
const Search: string; var Filename: string): boolean;
|
|
procedure AddToCache(const UnitSrc: TCTDirectoryUnitSources;
|
|
const Search, Filename: string);
|
|
public
|
|
constructor Create(const TheDirectory: string;
|
|
ThePool: TCTDirectoryCachePool);
|
|
destructor Destroy; override;
|
|
procedure CalcMemSize(Stats: TCTMemStats);
|
|
procedure Reference;
|
|
procedure Release;
|
|
function IndexOfFileCaseInsensitive(ShortFilename: PChar): integer;
|
|
function IndexOfFileCaseSensitive(ShortFilename: PChar): integer;
|
|
function FindUnitLink(const AUnitName: string): string;
|
|
function FindUnitInUnitSet(const AUnitName: string;
|
|
SrcSearchRequiresPPU: boolean = true): string;
|
|
function FindCompiledUnitInUnitSet(const AUnitName: string): string;
|
|
function FindFile(const ShortFilename: string;
|
|
const FileCase: TCTSearchFileCase): string;
|
|
function FileAge(const ShortFilename: string): TCTFileAgeTime;
|
|
function FileAttr(const ShortFilename: string): TCTDirectoryListingAttr;
|
|
function FileSize(const ShortFilename: string): TCTDirectoryListingSize;
|
|
function FindUnitSource(const AUnitName: string; AnyCase: boolean): string;
|
|
function FindUnitSourceInCleanSearchPath(const AUnitName,
|
|
SearchPath: string; AnyCase: boolean): string;
|
|
function FindUnitSourceInCompletePath(var AUnitName, InFilename: string;
|
|
AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean = false;
|
|
const AddNameSpaces: string = ''): string;
|
|
function FindCompiledUnitInCompletePath(const AnUnitname: string;
|
|
AnyCase: boolean): string;
|
|
procedure IterateFPCUnitsInSet(const Iterate: TCTOnIterateFile);
|
|
procedure UpdateListing;
|
|
procedure WriteListing;
|
|
procedure Invalidate; inline;
|
|
procedure GetFiles(var Files: TStrings; IncludeDirs: boolean = true); // relative to Directory
|
|
public
|
|
property Directory: string read FDirectory;
|
|
property RefCount: integer read FRefCount;
|
|
property Pool: TCTDirectoryCachePool read FPool;
|
|
property Strings[const AStringType: TCTDirCacheString]: string read GetStrings write SetStrings;
|
|
property Listing: TCTDirectoryListing read FListing;
|
|
end;
|
|
|
|
{ TCTDirectoryCachePool }
|
|
|
|
TCTDirCacheGetString = function(const ADirectory: string;
|
|
const AStringType: TCTDirCacheString
|
|
): string of object;
|
|
TCTDirCacheFindVirtualFile = function(const Filename: string): string of object;
|
|
TCTGetUnitFromSet = function(const UnitSet, AnUnitName: string;
|
|
SrcSearchRequiresPPU: boolean): string of object;
|
|
TCTGetCompiledUnitFromSet = function(const UnitSet, AnUnitName: string): string of object;
|
|
TCTIterateFPCUnitsFromSet = procedure(const UnitSet: string;
|
|
const Iterate: TCTOnIterateFile) of object;
|
|
|
|
TCTDirectoryCachePool = class
|
|
private
|
|
FConfigTimeStamp: integer;
|
|
FFileTimeStamp: integer;
|
|
FDirectories: TAVLTree;// tree of TCTDirectoryCache
|
|
FOnFindVirtualFile: TCTDirCacheFindVirtualFile;
|
|
FOnGetCompiledUnitFromSet: TCTGetCompiledUnitFromSet;
|
|
FOnGetString: TCTDirCacheGetString;
|
|
FOnGetUnitFromSet: TCTGetUnitFromSet;
|
|
FOnIterateFPCUnitsFromSet: TCTIterateFPCUnitsFromSet;
|
|
procedure DoRemove(ACache: TCTDirectoryCache);
|
|
procedure OnFileStateCacheChangeTimeStamp(Sender: TObject;
|
|
const AFilename: string);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure CalcMemSize(Stats: TCTMemStats);
|
|
procedure GetListing(const aDirectory: string; var Files: TStrings;
|
|
IncludeDirs: boolean = true); // relative to Directory
|
|
function GetCache(const Directory: string;
|
|
CreateIfNotExists: boolean = true;
|
|
DoReference: boolean = true): TCTDirectoryCache;
|
|
function GetString(const Directory: string; AStringType: TCTDirCacheString;
|
|
UseCache: boolean = true): string;
|
|
procedure IncreaseFileTimeStamp; inline;
|
|
procedure IncreaseConfigTimeStamp; inline;
|
|
function FileExists(Filename: string): boolean;
|
|
function FileAge(Filename: string): TCTFileAgeTime;
|
|
function FileAttr(Filename: string): TCTDirectoryListingAttr;
|
|
function FileSize(Filename: string): TCTDirectoryListingSize;
|
|
function FindUnitInUnitLinks(const Directory, AUnitName: string): string;
|
|
function FindUnitInUnitSet(const Directory, AUnitName: string): string;
|
|
function FindCompiledUnitInUnitSet(const Directory, AUnitName: string): string;
|
|
procedure IterateFPCUnitsInSet(const Directory: string;
|
|
const Iterate: TCTOnIterateFile);
|
|
function FindDiskFilename(const Filename: string;
|
|
{%H-}SearchCaseInsensitive: boolean = false): string; // using Pascal case insensitivity, not UTF-8
|
|
function FindUnitInDirectory(const Directory, AUnitName: string;
|
|
AnyCase: boolean = false): string;
|
|
function FindVirtualFile(const Filename: string): string;
|
|
function FindVirtualUnit(const AUnitName: string): string;
|
|
function FindUnitSourceInCompletePath(const Directory: string;
|
|
var AUnitName, InFilename: string;
|
|
AnyCase: boolean = false): string;
|
|
function FindCompiledUnitInCompletePath(const Directory: string;
|
|
var AnUnitname: string;
|
|
AnyCase: boolean = false): string;
|
|
function FindCompiledUnitInPath(const BaseDirectory, UnitPath, AnUnitname: string;
|
|
AnyCase: boolean = false): string; // result is not cached!
|
|
property FileTimeStamp: integer read FFileTimeStamp;
|
|
property ConfigTimeStamp: integer read FConfigTimeStamp;
|
|
property OnGetString: TCTDirCacheGetString read FOnGetString write FOnGetString;
|
|
property OnFindVirtualFile: TCTDirCacheFindVirtualFile read FOnFindVirtualFile
|
|
write FOnFindVirtualFile;
|
|
property OnGetUnitFromSet: TCTGetUnitFromSet read FOnGetUnitFromSet
|
|
write FOnGetUnitFromSet;
|
|
property OnGetCompiledUnitFromSet: TCTGetCompiledUnitFromSet
|
|
read FOnGetCompiledUnitFromSet write FOnGetCompiledUnitFromSet;
|
|
property OnIterateFPCUnitsFromSet: TCTIterateFPCUnitsFromSet
|
|
read FOnIterateFPCUnitsFromSet write FOnIterateFPCUnitsFromSet;
|
|
end;
|
|
|
|
function CompareCTDirectoryCaches(Data1, Data2: Pointer): integer;
|
|
function CompareAnsiStringAndDirectoryCache(Dir, Cache: Pointer): integer;
|
|
|
|
function ComparePCharFirstCaseInsAThenCase(Data1, Data2: Pointer): integer; // insensitive ASCII then byte wise
|
|
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer): integer; // insensitive ASCII
|
|
function ComparePCharCaseSensitive(Data1, Data2: Pointer): integer; // byte wise
|
|
|
|
// unit links
|
|
function SearchUnitInUnitLinks(const UnitLinks, TheUnitName: string;
|
|
var UnitLinkStart, UnitLinkEnd: integer; out Filename: string): boolean;
|
|
function CreateUnitLinksTree(const UnitLinks: string): TAVLTree; // tree of TUnitFileNameLink
|
|
function CompareUnitLinkNodes(NodeData1, NodeData2: Pointer): integer;
|
|
function CompareUnitNameWithUnitLinkNode(AUnitName: Pointer;
|
|
NodeData: pointer): integer;
|
|
|
|
implementation
|
|
|
|
const
|
|
NameOffset = SizeOf(TCTDirectoryListingHeader);
|
|
type
|
|
TWorkFileInfo = record
|
|
Header: TCTDirectoryListingHeader;
|
|
FileName: string;
|
|
end;
|
|
PWorkFileInfo = ^TWorkFileInfo;
|
|
PPWorkFileInfo = ^PWorkFileInfo;
|
|
|
|
function CompareWorkFileInfos(Data1, Data2: Pointer): integer;
|
|
var
|
|
Info1: PWorkFileInfo absolute Data1;
|
|
Info2: PWorkFileInfo absolute Data2;
|
|
begin
|
|
Result:=ComparePCharFirstCaseInsAThenCase(PChar(Info1^.Filename),PChar(Info2^.Filename));
|
|
end;
|
|
|
|
function CompareCTDirectoryCaches(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(TCTDirectoryCache(Data1).FDirectory,
|
|
TCTDirectoryCache(Data2).FDirectory);
|
|
end;
|
|
|
|
function CompareAnsiStringAndDirectoryCache(Dir, Cache: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(AnsiString(Dir),TCTDirectoryCache(Cache).FDirectory);
|
|
end;
|
|
|
|
function ComparePCharFirstCaseInsAThenCase(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=ComparePCharCaseInsensitiveA(Data1,Data2);
|
|
if Result=0 then
|
|
Result:=ComparePCharCaseSensitive(Data1,Data2);
|
|
end;
|
|
|
|
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer): integer;
|
|
var
|
|
p1: PChar absolute Data1;
|
|
p2: PChar absolute Data2;
|
|
begin
|
|
while (FPUpChars[p1^]=FPUpChars[p2^]) and (p1^<>#0) do begin
|
|
inc(p1);
|
|
inc(p2);
|
|
end;
|
|
Result:=ord(FPUpChars[p1^])-ord(FPUpChars[p2^]);
|
|
end;
|
|
|
|
function ComparePCharCaseInsensitiveA(Data1, Data2: Pointer;
|
|
MaxCount: PtrInt): integer;
|
|
var
|
|
p1: PChar absolute Data1;
|
|
p2: PChar absolute Data2;
|
|
begin
|
|
while (MaxCount>0) and (FPUpChars[p1^]=FPUpChars[p2^]) and (p1^<>#0) do begin
|
|
inc(p1);
|
|
inc(p2);
|
|
dec(MaxCount);
|
|
end;
|
|
if MaxCount=0 then
|
|
Result:=0
|
|
else
|
|
Result:=ord(FPUpChars[p1^])-ord(FPUpChars[p2^]);
|
|
end;
|
|
|
|
function ComparePCharCaseSensitive(Data1, Data2: Pointer): integer;
|
|
var
|
|
p1: PChar absolute Data1;
|
|
p2: PChar absolute Data2;
|
|
begin
|
|
while (p1^=p2^) and (p1^<>#0) do begin
|
|
inc(p1);
|
|
inc(p2);
|
|
end;
|
|
Result:=ord(p1^)-ord(p2^);
|
|
end;
|
|
|
|
function ComparePCharUnitNameWithFilename(UnitNameP, FilenameP: Pointer): integer;
|
|
{ Checks if UnitNameP is a dotted prefix of FilenameP.
|
|
For example:
|
|
a.b is prefix of a.b.c.d, A.b.c, a.b.c
|
|
but not of a.bc
|
|
}
|
|
var
|
|
AUnitName: PChar absolute UnitNameP;
|
|
Filename: PChar absolute FilenameP;
|
|
cu: Char;
|
|
cf: Char;
|
|
begin
|
|
repeat
|
|
cu:=FPUpChars[AUnitName^];
|
|
cf:=FPUpChars[Filename^];
|
|
if cu=#0 then begin
|
|
// the unit name fits the start of the file name
|
|
if (cf<>'.') then
|
|
Result:=ord('.')-ord(cf)
|
|
else
|
|
Result:=0;
|
|
exit;
|
|
end;
|
|
if cu=cf then begin
|
|
inc(AUnitName);
|
|
inc(Filename);
|
|
end else begin
|
|
Result:=ord(cu)-ord(cf);
|
|
exit;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
function SearchUnitInUnitLinks(const UnitLinks, TheUnitName: string;
|
|
var UnitLinkStart, UnitLinkEnd: integer; out Filename: string): boolean;
|
|
var
|
|
UnitLinkLen: integer;
|
|
pe: TCTPascalExtType;
|
|
AliasFilename: String;
|
|
begin
|
|
Result:=false;
|
|
Filename:='';
|
|
if TheUnitName='' then exit;
|
|
{$IFDEF ShowTriedFiles}
|
|
DebugLn(['SearchUnitInUnitLinks length(UnitLinks)=',length(UnitLinks)]);
|
|
{$ENDIF}
|
|
if UnitLinkStart<1 then
|
|
UnitLinkStart:=1;
|
|
while UnitLinkStart<=length(UnitLinks) do begin
|
|
while (UnitLinkStart<=length(UnitLinks))
|
|
and (UnitLinks[UnitLinkStart] in [#10,#13]) do
|
|
inc(UnitLinkStart);
|
|
UnitLinkEnd:=UnitLinkStart;
|
|
while (UnitLinkEnd<=length(UnitLinks)) and (UnitLinks[UnitLinkEnd]<>' ')
|
|
do
|
|
inc(UnitLinkEnd);
|
|
UnitLinkLen:=UnitLinkEnd-UnitLinkStart;
|
|
if UnitLinkLen>0 then begin
|
|
{$IFDEF ShowTriedFiles}
|
|
DebugLn([' unit "',copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart),'" ',
|
|
ComparePCharCaseInsensitiveA(Pointer(TheUnitName),@UnitLinks[UnitLinkStart],UnitLinkLen)]);
|
|
{$ENDIF}
|
|
if (UnitLinkLen=length(TheUnitName))
|
|
and (ComparePCharCaseInsensitiveA(Pointer(TheUnitName),@UnitLinks[UnitLinkStart],
|
|
UnitLinkLen)=0)
|
|
then begin
|
|
// unit found -> parse filename
|
|
UnitLinkStart:=UnitLinkEnd+1;
|
|
UnitLinkEnd:=UnitLinkStart;
|
|
while (UnitLinkEnd<=length(UnitLinks))
|
|
and (not (UnitLinks[UnitLinkEnd] in [#10,#13])) do
|
|
inc(UnitLinkEnd);
|
|
if UnitLinkEnd>UnitLinkStart then begin
|
|
Filename:=copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart);
|
|
if FileExistsCached(Filename) then begin
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
// try also different extensions
|
|
for pe:=Low(TCTPascalExtType) to High(TCTPascalExtType) do begin
|
|
if CompareFileExt(Filename,CTPascalExtension[pe],false)<>0 then
|
|
begin
|
|
AliasFilename:=ChangeFileExt(Filename,'.pas');
|
|
if FileExistsCached(AliasFilename) then begin
|
|
Filename:=AliasFilename;
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
UnitLinkStart:=UnitLinkEnd;
|
|
end else begin
|
|
UnitLinkStart:=UnitLinkEnd+1;
|
|
while (UnitLinkStart<=length(UnitLinks))
|
|
and (not (UnitLinks[UnitLinkStart] in [#10,#13])) do
|
|
inc(UnitLinkStart);
|
|
end;
|
|
end else
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function CreateUnitLinksTree(const UnitLinks: string): TAVLTree;
|
|
var
|
|
UnitLinksTree: TAVLTree;
|
|
UnitLinkLen: integer;
|
|
UnitLinkStart: Integer;
|
|
UnitLinkEnd: Integer;
|
|
TheUnitName: String;
|
|
Filename: String;
|
|
NewNode: TUnitFileNameLink;
|
|
begin
|
|
UnitLinksTree:=TAVLTree.Create(@CompareUnitLinkNodes);
|
|
UnitLinkStart:=1;
|
|
while UnitLinkStart<=length(UnitLinks) do begin
|
|
while (UnitLinkStart<=length(UnitLinks))
|
|
and (UnitLinks[UnitLinkStart] in [#10,#13]) do
|
|
inc(UnitLinkStart);
|
|
UnitLinkEnd:=UnitLinkStart;
|
|
while (UnitLinkEnd<=length(UnitLinks)) and (UnitLinks[UnitLinkEnd]<>' ')
|
|
do
|
|
inc(UnitLinkEnd);
|
|
UnitLinkLen:=UnitLinkEnd-UnitLinkStart;
|
|
if UnitLinkLen>0 then begin
|
|
TheUnitName:=copy(UnitLinks,UnitLinkStart,UnitLinkLen);
|
|
if IsValidIdent(TheUnitName) then begin
|
|
UnitLinkStart:=UnitLinkEnd+1;
|
|
UnitLinkEnd:=UnitLinkStart;
|
|
while (UnitLinkEnd<=length(UnitLinks))
|
|
and (not (UnitLinks[UnitLinkEnd] in [#10,#13])) do
|
|
inc(UnitLinkEnd);
|
|
if UnitLinkEnd>UnitLinkStart then begin
|
|
Filename:=copy(UnitLinks,UnitLinkStart,UnitLinkEnd-UnitLinkStart);
|
|
NewNode:=TUnitFileNameLink.Create;
|
|
NewNode.Unit_Name:=TheUnitName;
|
|
NewNode.Filename:=Filename;
|
|
UnitLinksTree.Add(NewNode);
|
|
end;
|
|
UnitLinkStart:=UnitLinkEnd;
|
|
end else begin
|
|
UnitLinkStart:=UnitLinkEnd+1;
|
|
while (UnitLinkStart<=length(UnitLinks))
|
|
and (not (UnitLinks[UnitLinkStart] in [#10,#13])) do
|
|
inc(UnitLinkStart);
|
|
end;
|
|
end else
|
|
break;
|
|
end;
|
|
Result:=UnitLinksTree;
|
|
end;
|
|
|
|
function CompareUnitLinkNodes(NodeData1, NodeData2: pointer): integer;
|
|
var Link1, Link2: TUnitFileNameLink;
|
|
begin
|
|
Link1:=TUnitFileNameLink(NodeData1);
|
|
Link2:=TUnitFileNameLink(NodeData2);
|
|
Result:=CompareText(Link1.Unit_Name,Link2.Unit_Name);
|
|
end;
|
|
|
|
function CompareUnitNameWithUnitLinkNode(AUnitName: Pointer;
|
|
NodeData: pointer): integer;
|
|
begin
|
|
Result:=CompareText(String(AUnitName),TUnitFileNameLink(NodeData).Unit_Name);
|
|
end;
|
|
|
|
{$IF FPC_FULLVERSION<30101}
|
|
function IsValidIdent(const Ident: string; AllowDots: Boolean = False; StrictDots: Boolean = False): Boolean;
|
|
const
|
|
Alpha = ['A'..'Z', 'a'..'z', '_'];
|
|
AlphaNum = Alpha + ['0'..'9'];
|
|
Dot = '.';
|
|
var
|
|
First: Boolean;
|
|
I, Len: Integer;
|
|
begin
|
|
Len := Length(Ident);
|
|
if Len < 1 then
|
|
Exit(False);
|
|
First := True;
|
|
for I := 1 to Len do
|
|
begin
|
|
if First then
|
|
begin
|
|
Result := Ident[I] in Alpha;
|
|
First := False;
|
|
end
|
|
else if AllowDots and (Ident[I] = Dot) then
|
|
begin
|
|
if StrictDots then
|
|
begin
|
|
Result := I < Len;
|
|
First := True;
|
|
end;
|
|
end
|
|
else
|
|
Result := Ident[I] in AlphaNum;
|
|
if not Result then
|
|
Break;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ TCTDirectoryCache }
|
|
|
|
function TCTDirectoryCache.GetStrings(const AStringType: TCTDirCacheString
|
|
): string;
|
|
begin
|
|
//if AStringType=ctdcsUnitPath then DebugLn(['TCTDirectoryCache.GetStrings ctdcsUnitPath ',Directory,' ',FStrings[AStringType].ConfigTimeStamp,' ',Pool.ConfigTimeStamp]);
|
|
if FStrings[AStringType].ConfigTimeStamp<>Pool.ConfigTimeStamp then begin
|
|
Strings[AStringType]:=Pool.GetString(Directory,AStringType,false);
|
|
end;
|
|
Result:=FStrings[AStringType].Value;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.SetStrings(const AStringType: TCTDirCacheString;
|
|
const AValue: string);
|
|
begin
|
|
FStrings[AStringType].Value:=AValue;
|
|
FStrings[AStringType].ConfigTimeStamp:=Pool.ConfigTimeStamp;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.ClearUnitLinks;
|
|
begin
|
|
if FUnitLinksTree=nil then exit;
|
|
FUnitLinksTree.FreeAndClear;
|
|
FUnitLinksTree.Free;
|
|
FUnitLinksTree:=nil
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.UpdateListing;
|
|
var
|
|
WorkingListing: PWorkFileInfo;
|
|
WorkingListingCapacity, WorkingListingCount: integer;
|
|
WorkingItem: PWorkFileInfo;
|
|
FileInfo: TSearchRec;
|
|
TotalLen: Integer;
|
|
i: Integer;
|
|
p: PChar;
|
|
CurFilenameLen: Integer;
|
|
NewCapacity: Integer;
|
|
SortMap: PPWorkFileInfo;
|
|
begin
|
|
if FListing.FileTimeStamp=Pool.FileTimeStamp then exit;
|
|
FListing.Clear;
|
|
FListing.FileTimeStamp:=Pool.FileTimeStamp;
|
|
if Directory='' then exit;// virtual directory
|
|
|
|
// Note: do not add a 'if not DirectoryExistsUTF8 then exit'.
|
|
// This will not work on automounted directories. You must use FindFirstUTF8.
|
|
|
|
// read the directory
|
|
WorkingListing:=nil;
|
|
WorkingListingCapacity:=0;
|
|
WorkingListingCount:=0;
|
|
SortMap:=nil;
|
|
try
|
|
if FindFirstUTF8(Directory+FileMask,faAnyFile,FileInfo)=0 then begin
|
|
repeat
|
|
// check if special file
|
|
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
|
|
then
|
|
continue;
|
|
// add file
|
|
if WorkingListingCount=WorkingListingCapacity then begin
|
|
// grow WorkingListing
|
|
if WorkingListingCapacity>0 then
|
|
NewCapacity:=WorkingListingCapacity*2
|
|
else
|
|
NewCapacity:=64;
|
|
ReAllocMem(WorkingListing,SizeOf(TWorkFileInfo)*NewCapacity);
|
|
FillChar(WorkingListing[WorkingListingCount],
|
|
SizeOf(TWorkFileInfo)*(NewCapacity-WorkingListingCapacity),0);
|
|
WorkingListingCapacity:=NewCapacity;
|
|
end;
|
|
WorkingItem:=@WorkingListing[WorkingListingCount];
|
|
WorkingItem^.Header.Time:=FileInfo.Time;
|
|
WorkingItem^.Header.Attr:=FileInfo.Attr;
|
|
WorkingItem^.Header.Size:=FileInfo.Size;
|
|
WorkingItem^.FileName:=FileInfo.Name;
|
|
inc(WorkingListingCount);
|
|
until FindNextUTF8(FileInfo)<>0;
|
|
end;
|
|
FindCloseUTF8(FileInfo);
|
|
|
|
if WorkingListingCount=0 then exit;
|
|
|
|
// sort the files
|
|
GetMem(SortMap,WorkingListingCount*SizeOf(Pointer));
|
|
for i:=0 to WorkingListingCount-1 do
|
|
SortMap[i]:=@WorkingListing[i];
|
|
MergeSort(PPointer(SortMap),WorkingListingCount,@CompareWorkFileInfos);
|
|
|
|
// create listing
|
|
TotalLen:=0;
|
|
for i:=0 to WorkingListingCount-1 do
|
|
inc(TotalLen,length(WorkingListing[i].FileName)+1+SizeOf(TCTDirectoryListingHeader));
|
|
GetMem(FListing.Files,TotalLen);
|
|
FListing.Size:=TotalLen;
|
|
FListing.Count:=WorkingListingCount;
|
|
GetMem(FListing.Starts,SizeOf(PChar)*WorkingListingCount);
|
|
p:=FListing.Files;
|
|
for i:=0 to WorkingListingCount-1 do begin
|
|
FListing.Starts[i]:=p-FListing.Files;
|
|
WorkingItem:=SortMap[i];
|
|
PCTDirectoryListingHeader(p)^:=WorkingItem^.Header;
|
|
inc(p,SizeOf(TCTDirectoryListingHeader));
|
|
// filename
|
|
CurFilenameLen:=length(WorkingItem^.FileName);
|
|
if CurFilenameLen>0 then begin
|
|
System.Move(WorkingItem^.FileName[1],p^,CurFilenameLen);
|
|
inc(p,CurFilenameLen);
|
|
end;
|
|
p^:=#0;
|
|
inc(p);
|
|
end;
|
|
finally
|
|
ReAllocMem(SortMap,0);
|
|
for i:=0 to WorkingListingCount-1 do
|
|
WorkingListing[i].FileName:='';
|
|
ReAllocMem(WorkingListing,0);
|
|
end;
|
|
end;
|
|
|
|
function TCTDirectoryCache.GetUnitSourceCacheValue(
|
|
const UnitSrc: TCTDirectoryUnitSources; const Search: string;
|
|
var Filename: string): boolean;
|
|
var
|
|
Files: TStringToStringTree;
|
|
begin
|
|
//debugln(['TCTDirectoryCache.GetUnitSourceCacheValue START ',UnitSrc,' Search=',Search]);
|
|
Files:=FUnitSources[UnitSrc].Files;
|
|
if (FUnitSources[UnitSrc].FileTimeStamp<>Pool.FileTimeStamp)
|
|
or (FUnitSources[UnitSrc].ConfigTimeStamp<>Pool.ConfigTimeStamp) then begin
|
|
// cache is invalid -> clear to make it valid
|
|
if Files<>nil then
|
|
Files.Clear;
|
|
FUnitSources[UnitSrc].FileTimeStamp:=Pool.FileTimeStamp;
|
|
FUnitSources[UnitSrc].ConfigTimeStamp:=Pool.ConfigTimeStamp;
|
|
Result:=false;
|
|
end else begin
|
|
// cache is valid
|
|
if Files<>nil then begin
|
|
Result:=Files.GetString(Search,Filename);
|
|
end else begin
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
//debugln(['TCTDirectoryCache.GetUnitSourceCacheValue END ',UnitSrc,' Search=',Search,' Result=',Result,' Filename=',Filename]);
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.AddToCache(const UnitSrc: TCTDirectoryUnitSources;
|
|
const Search, Filename: string);
|
|
var
|
|
Files: TStringToStringTree;
|
|
CaseSensitive: Boolean;
|
|
begin
|
|
Files:=FUnitSources[UnitSrc].Files;
|
|
if Files=nil then begin
|
|
if UnitSrc in [ctdusUnitNormal,ctdusPPUNormal] then
|
|
CaseSensitive:=FilenamesCaseSensitive
|
|
else
|
|
CaseSensitive:=UnitSrc in ctdusCaseNormal;
|
|
Files:=TFilenameToStringTree.Create(CaseSensitive);
|
|
FUnitSources[UnitSrc].Files:=Files;
|
|
end;
|
|
Files[Search]:=Filename;
|
|
end;
|
|
|
|
constructor TCTDirectoryCache.Create(const TheDirectory: string;
|
|
ThePool: TCTDirectoryCachePool);
|
|
|
|
procedure RaiseDirNotAbsolute;
|
|
begin
|
|
raise Exception.Create('directory not absolute "'+FDirectory+'"');
|
|
end;
|
|
|
|
begin
|
|
FDirectory:=AppendPathDelim(TrimFilename(TheDirectory));
|
|
if FDirectory='.' then FDirectory:='';
|
|
if (FDirectory<>'') and not FilenameIsAbsolute(FDirectory) then
|
|
RaiseDirNotAbsolute;
|
|
FListing:=TCTDirectoryListing.Create;
|
|
FPool:=ThePool;
|
|
FRefCount:=1;
|
|
end;
|
|
|
|
destructor TCTDirectoryCache.Destroy;
|
|
var
|
|
UnitSrc: TCTDirectoryUnitSources;
|
|
begin
|
|
ClearUnitLinks;
|
|
if Pool<>nil then Pool.DoRemove(Self);
|
|
FreeAndNil(FListing);
|
|
for UnitSrc:=Low(TCTDirectoryUnitSources) to High(TCTDirectoryUnitSources) do
|
|
FreeAndNil(FUnitSources[UnitSrc].Files);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.CalcMemSize(Stats: TCTMemStats);
|
|
var
|
|
cs: TCTDirCacheString;
|
|
us: TCTDirectoryUnitSources;
|
|
Node: TAVLTreeNode;
|
|
m: PtrUInt;
|
|
begin
|
|
Stats.Add('TCTDirectoryCache',PtrUInt(InstanceSize)
|
|
+MemSizeString(FDirectory));
|
|
|
|
m:=0;
|
|
for cs:=Low(FStrings) to high(FStrings) do begin
|
|
inc(m,SizeOf(TCTDirCacheStringRecord));
|
|
inc(m,MemSizeString(FStrings[cs].Value));
|
|
end;
|
|
Stats.Add('TCTDirectoryCache.FStrings',m);
|
|
|
|
m:=0;
|
|
for us:=Low(FUnitSources) to high(FUnitSources) do begin
|
|
inc(m,SizeOf(TCTDirectoryUnitSources));
|
|
if FUnitSources[us].Files<>nil then
|
|
inc(m,FUnitSources[us].Files.CalcMemSize);
|
|
end;
|
|
Stats.Add('TCTDirectoryCache.FUnitSources',m);
|
|
|
|
if FUnitLinksTree<>nil then begin
|
|
m:=PtrUInt(FUnitLinksTree.InstanceSize)
|
|
+SizeOf(TAVLTreeNode)*PtrUInt(FUnitLinksTree.Count);
|
|
Node:=FUnitLinksTree.FindLowest;
|
|
while Node<>nil do begin
|
|
inc(m,TUnitFileNameLink(Node.Data).CalcMemSize);
|
|
Node:=FUnitLinksTree.FindSuccessor(Node);
|
|
end;
|
|
Stats.Add('TCTDirectoryCache.FUnitLinksTree',m);
|
|
end;
|
|
|
|
if FListing<>nil then
|
|
Stats.Add('TCTDirectoryCache.FListing',FListing.CalcMemSize);
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.Reference;
|
|
begin
|
|
inc(FRefCount);
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.Release;
|
|
begin
|
|
if FRefCount<=0 then
|
|
raise Exception.Create('TCTDirectoryCache.Release');
|
|
dec(FRefCount);
|
|
if FRefCount=0 then Free;
|
|
end;
|
|
|
|
function TCTDirectoryCache.IndexOfFileCaseInsensitive(
|
|
ShortFilename: PChar): integer;
|
|
var
|
|
Files: PChar;
|
|
l: Integer;
|
|
r: Integer;
|
|
m: Integer;
|
|
CurFilename: PChar;
|
|
cmp: Integer;
|
|
begin
|
|
UpdateListing;
|
|
Files:=FListing.Files;
|
|
if Files=nil then exit(-1);
|
|
l:=0;
|
|
r:=FListing.Count-1;
|
|
while r>=l do begin
|
|
m:=(l+r) shr 1;
|
|
CurFilename:=@Files[FListing.Starts[m]+NameOffset];
|
|
cmp:=ComparePCharCaseInsensitiveA(ShortFilename,CurFilename);
|
|
if cmp>0 then
|
|
l:=m+1
|
|
else if cmp<0 then
|
|
r:=m-1
|
|
else begin
|
|
Result:=m;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=-1;
|
|
end;
|
|
|
|
function TCTDirectoryCache.IndexOfFileCaseSensitive(ShortFilename: PChar
|
|
): integer;
|
|
var
|
|
Files: PChar;
|
|
l: Integer;
|
|
r: Integer;
|
|
m: Integer;
|
|
CurFilename: PChar;
|
|
cmp: Integer;
|
|
begin
|
|
UpdateListing;
|
|
Files:=FListing.Files;
|
|
if Files=nil then exit(-1);
|
|
l:=0;
|
|
r:=FListing.Count-1;
|
|
while r>=l do begin
|
|
m:=(l+r) shr 1;
|
|
CurFilename:=@Files[FListing.Starts[m]+NameOffset];
|
|
cmp:=ComparePCharFirstCaseInsAThenCase(ShortFilename,CurFilename);// pointer type cast avoids #0 check
|
|
if cmp>0 then
|
|
l:=m+1
|
|
else if cmp<0 then
|
|
r:=m-1
|
|
else begin
|
|
Result:=m;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result:=-1;
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindUnitLink(const AUnitName: string): string;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Link: TUnitFileNameLink;
|
|
AliasFilename: String;
|
|
pe: TCTPascalExtType;
|
|
begin
|
|
if (FUnitLinksTree=nil) or (FUnitLinksTreeTimeStamp<>Pool.FileTimeStamp) then
|
|
begin
|
|
ClearUnitLinks;
|
|
FUnitLinksTreeTimeStamp:=Pool.FileTimeStamp;
|
|
FUnitLinksTree:=CreateUnitLinksTree(Strings[ctdcsUnitLinks]);
|
|
end;
|
|
Node:=FUnitLinksTree.FindKey(Pointer(AUnitName),
|
|
@CompareUnitNameWithUnitLinkNode);
|
|
if Node<>nil then begin
|
|
Link:=TUnitFileNameLink(Node.Data);
|
|
Result:=Link.Filename;
|
|
if FileExistsCached(Result) then begin
|
|
exit;
|
|
end;
|
|
// try different extensions too
|
|
for pe:=Low(TCTPascalExtType) to High(TCTPascalExtType) do begin
|
|
if CompareFileExt(Result,CTPascalExtension[pe],false)<>0 then
|
|
begin
|
|
AliasFilename:=ChangeFileExt(Result,CTPascalExtension[pe]);
|
|
if FileExistsCached(AliasFilename) then begin
|
|
Link.Filename:=AliasFilename;
|
|
Result:=AliasFilename;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindUnitInUnitSet(const AUnitName: string;
|
|
SrcSearchRequiresPPU: boolean): string;
|
|
var
|
|
UnitSet: string;
|
|
begin
|
|
UnitSet:=Strings[ctdcsUnitSet];
|
|
//debugln(['TCTDirectoryCache.FindUnitInUnitSet Directory="',Directory,'" UnitSet="',UnitSet,'" AUnitName="',AUnitName,'"']);
|
|
Result:=Pool.OnGetUnitFromSet(UnitSet,AUnitName,SrcSearchRequiresPPU);
|
|
//debugln(['TCTDirectoryCache.FindUnitInUnitSet Directory="',Directory,'" UnitSet="',dbgstr(UnitSet),'" AUnitName="',AUnitName,'" Result="',Result,'"']);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindCompiledUnitInUnitSet(const AUnitName: string
|
|
): string;
|
|
var
|
|
UnitSet: string;
|
|
begin
|
|
UnitSet:=Strings[ctdcsUnitSet];
|
|
//debugln(['TCTDirectoryCache.FindCompiledUnitInUnitSet Directory="',Directory,'" UnitSet="',UnitSet,'" AUnitName="',AUnitName,'"']);
|
|
Result:=Pool.OnGetCompiledUnitFromSet(UnitSet,AUnitName);
|
|
//debugln(['TCTDirectoryCache.FindCompiledUnitInUnitSet Directory="',Directory,'" UnitSet="',dbgstr(UnitSet),'" AUnitName="',AUnitName,'" Result="',Result,'"']);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindFile(const ShortFilename: string;
|
|
const FileCase: TCTSearchFileCase): string;
|
|
|
|
procedure RaiseDontKnow;
|
|
begin
|
|
raise Exception.Create('do not know FileCase '+IntToStr(ord(FileCase)));
|
|
end;
|
|
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:='';
|
|
i:=0;
|
|
if ShortFilename='' then exit;
|
|
if Directory<>'' then begin
|
|
case FileCase of
|
|
ctsfcDefault:
|
|
{$IFDEF CaseInsensitiveFilenames}
|
|
i:=IndexOfFileCaseInsensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ELSE}
|
|
begin
|
|
i:=IndexOfFileCaseSensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
// just return the parameter
|
|
if i>=0 then
|
|
Result:=ShortFilename;
|
|
exit;
|
|
end;
|
|
{$ENDIF}
|
|
ctsfcAllCase,ctsfcLoUpCase:
|
|
i:=IndexOfFileCaseInsensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
else RaiseDontKnow;
|
|
end;
|
|
if i>=0 then
|
|
Result:=FListing.GetFilename(i);
|
|
end else begin
|
|
// this is a virtual directory
|
|
Result:=Pool.FindVirtualFile(ShortFilename);
|
|
end;
|
|
end;
|
|
|
|
function TCTDirectoryCache.FileAge(const ShortFilename: string
|
|
): TCTFileAgeTime;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=-1;
|
|
if ShortFilename='' then exit;
|
|
if Directory='' then begin
|
|
// this is a virtual directory
|
|
exit;
|
|
end;
|
|
{$IFDEF CaseInsensitiveFilenames}
|
|
i:=IndexOfFileCaseInsensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ELSE}
|
|
i:=IndexOfFileCaseSensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ENDIF}
|
|
if i>=0 then
|
|
Result:=FListing.GetTime(i);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FileAttr(const ShortFilename: string
|
|
): TCTDirectoryListingAttr;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=0;
|
|
if ShortFilename='' then exit;
|
|
if Directory='' then begin
|
|
// this is a virtual directory
|
|
exit;
|
|
end;
|
|
{$IFDEF CaseInsensitiveFilenames}
|
|
i:=IndexOfFileCaseInsensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ELSE}
|
|
i:=IndexOfFileCaseSensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ENDIF}
|
|
if i>=0 then
|
|
Result:=FListing.GetAttr(i);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FileSize(const ShortFilename: string
|
|
): TCTDirectoryListingSize;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=-1;
|
|
if ShortFilename='' then exit;
|
|
if Directory='' then begin
|
|
// this is a virtual directory
|
|
exit;
|
|
end;
|
|
{$IFDEF CaseInsensitiveFilenames}
|
|
i:=IndexOfFileCaseInsensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ELSE}
|
|
i:=IndexOfFileCaseSensitive(Pointer(ShortFilename));// pointer type cast avoids #0 check
|
|
{$ENDIF}
|
|
if i>=0 then
|
|
Result:=FListing.GetSize(i);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindUnitSource(const AUnitName: string;
|
|
AnyCase: boolean): string;
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
const
|
|
DebugUnitName = 'IDEDialogs';
|
|
DebugDirPart = 'ideintf';
|
|
{$ENDIF}
|
|
var
|
|
l: Integer;
|
|
r: Integer;
|
|
m: Integer;
|
|
cmp: LongInt;
|
|
CurFilename: PChar;
|
|
Files: PChar;
|
|
ExtStartPos: PChar;
|
|
begin
|
|
Result:='';
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
|
|
DebugLn('TCTDirectoryCache.FindUnitSource AUnitName="',AUnitName,'" AnyCase=',dbgs(AnyCase),' Directory=',Directory);
|
|
{$ENDIF}
|
|
if AUnitName='' then exit;
|
|
if Directory<>'' then begin
|
|
UpdateListing;
|
|
Files:=FListing.Files;
|
|
if Files=nil then exit;
|
|
// binary search the nearest filename
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
|
|
WriteListing;
|
|
{$ENDIF}
|
|
|
|
l:=0;
|
|
r:=FListing.Count-1;
|
|
while r>=l do begin
|
|
m:=(l+r) shr 1;
|
|
CurFilename:=@Files[FListing.Starts[m]+NameOffset];
|
|
cmp:=ComparePCharUnitNameWithFilename(Pointer(AUnitName),CurFilename);
|
|
if cmp>0 then
|
|
l:=m+1
|
|
else if cmp<0 then
|
|
r:=m-1
|
|
else
|
|
break;
|
|
end;
|
|
if cmp<>0 then exit;
|
|
// m is now on a filename with the right prefix
|
|
// go to the first pascal unit with the right unit name
|
|
while (m>0)
|
|
and (ComparePCharUnitNameWithFilename(Pointer(AUnitName),
|
|
@Files[FListing.Starts[m-1]+NameOffset])=0)
|
|
do
|
|
dec(m);
|
|
// -> now find a filename with correct case and extension
|
|
while m<FListing.Count do begin
|
|
CurFilename:=@Files[FListing.Starts[m]+NameOffset];
|
|
// check if filename has the right AUnitName prefix
|
|
if (ComparePCharUnitNameWithFilename(Pointer(AUnitName),CurFilename)<>0)
|
|
then
|
|
break;
|
|
|
|
// check if the filename fits
|
|
ExtStartPos:=CurFilename+length(AUnitname);
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
|
|
DebugLn('TCTDirectoryCache.FindUnitSource NEXT "',CurFilename,'" ExtStart=',dbgstr(ExtStartPos^));
|
|
{$ENDIF}
|
|
if IsPascalUnitExt(ExtStartPos) then begin
|
|
// the extension is ok
|
|
Result:=CurFilename;
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
|
|
DebugLn('TCTDirectoryCache.FindUnitSource CHECKING CASE "',CurFilename,'"');
|
|
{$ENDIF}
|
|
if AnyCase then begin
|
|
exit;
|
|
end else begin
|
|
// check case platform dependent
|
|
{$IFDEF CaseInsensitiveFilenames}
|
|
exit;
|
|
{$ELSE}
|
|
if (ExtractFileNameOnly(Result)=AUnitName)
|
|
or (Result=lowercase(Result))
|
|
or (Result=uppercase(Result)) then
|
|
exit;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
inc(m);
|
|
end;
|
|
{$IFDEF DebugDirCacheFindUnitSource}
|
|
if m<FListing.Count then
|
|
if (CompareText(AUnitName,DebugUnitName)=0) and (System.Pos(DebugDirPart,directory)>0) then
|
|
DebugLn('TCTDirectoryCache.FindUnitSource LAST ',CurFilename);
|
|
{$ENDIF}
|
|
end else begin
|
|
// this is a virtual directory
|
|
Result:=Pool.FindVirtualUnit(AUnitName);
|
|
if Result<>'' then exit;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindUnitSourceInCleanSearchPath(const AUnitName,
|
|
SearchPath: string; AnyCase: boolean): string;
|
|
var
|
|
p, StartPos, l: integer;
|
|
CurPath: string;
|
|
IsAbsolute: Boolean;
|
|
begin
|
|
//if (CompareText(AUnitName,'UnitDependencies')=0) then
|
|
// DebugLn('TCTDirectoryCache.FindUnitSourceInCleanSearchPath AUnitName="',AUnitName,'" SearchPath="',SearchPath,'"');
|
|
StartPos:=1;
|
|
l:=length(SearchPath);
|
|
while StartPos<=l do begin
|
|
p:=StartPos;
|
|
while (p<=l) and (SearchPath[p]<>';') do inc(p);
|
|
CurPath:=Trim(copy(SearchPath,StartPos,p-StartPos));
|
|
if CurPath<>'' then begin
|
|
IsAbsolute:=FilenameIsAbsolute(CurPath);
|
|
if (not IsAbsolute) and (Directory<>'') then begin
|
|
CurPath:=Directory+CurPath;
|
|
IsAbsolute:=true;
|
|
end;
|
|
//DebugLn('TCTDirectoryCache.FindUnitSourceInCleanSearchPath CurPath="',CurPath,'"');
|
|
if IsAbsolute then begin
|
|
CurPath:=AppendPathDelim(CurPath);
|
|
Result:=Pool.FindUnitInDirectory(CurPath,AUnitName,AnyCase);
|
|
end else if (CurPath='.') and (Directory='') then
|
|
Result:=Pool.FindVirtualUnit(AUnitname)
|
|
else
|
|
Result:='';
|
|
if Result<>'' then exit;
|
|
end;
|
|
StartPos:=p+1;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindUnitSourceInCompletePath(var AUnitName,
|
|
InFilename: string; AnyCase: boolean; FPCSrcSearchRequiresPPU: boolean;
|
|
const AddNameSpaces: string): string;
|
|
|
|
function FindInFilenameLowUp(aFilename: string): string;
|
|
begin
|
|
if AnyCase then
|
|
Result:=Pool.FindDiskFilename(aFilename,true)
|
|
else begin
|
|
Result:=aFilename;
|
|
if FileExistsCached(Result) then exit;
|
|
{$IFNDEF CaseInsensitiveFilenames}
|
|
Result:=ExtractFilePath(aFilename)+lowercase(ExtractFileName(aFilename));
|
|
if FileExistsCached(Result) then exit;
|
|
Result:=ExtractFilePath(aFilename)+uppercase(ExtractFileName(aFilename));
|
|
if FileExistsCached(Result) then exit;
|
|
{$ENDIF}
|
|
Result:='';
|
|
end;
|
|
end;
|
|
|
|
function FindInFilename(aFilename: string): string;
|
|
var
|
|
Ext: String;
|
|
begin
|
|
Result:='';
|
|
if not FilenameIsAbsolute(aFilename) then
|
|
exit;
|
|
Ext:=ExtractFileExt(aFilename);
|
|
if Ext='' then
|
|
aFilename:=aFilename+'.pp'; // append default extension
|
|
Result:=FindInFilenameLowUp(aFilename);
|
|
if Result='' then begin
|
|
if (Ext<>'') then exit;
|
|
// search for secondary extension
|
|
aFilename:=ChangeFileExt(aFilename,'.pas');
|
|
Result:=FindInFilenameLowUp(aFilename);
|
|
if Result='' then exit;
|
|
end;
|
|
InFilename:=CreateRelativePath(Result,Directory);
|
|
end;
|
|
|
|
var
|
|
UnitSrc: TCTDirectoryUnitSources;
|
|
CurDir: String;
|
|
SrcPath: string;
|
|
NewUnitName, aNameSpace, aName, NameSpaces: String;
|
|
p: SizeInt;
|
|
begin
|
|
Result:='';
|
|
{$IFDEF ShowTriedUnits}
|
|
DebugLn('TCTDirectoryCache.FindUnitSourceInCompletePath AUnitName="',AUnitname,'" InFilename="',InFilename,'" Directory="',Directory,'"',BoolToStr(AddNameSpaces<>'',' ExtraNameSpaces="'+AddNameSpaces+'"',''));
|
|
{$ENDIF}
|
|
if InFilename<>'' then begin
|
|
// uses IN parameter
|
|
InFilename:=TrimFilename(GetForcedPathDelims(InFilename));
|
|
if AnyCase then
|
|
UnitSrc:=ctdusInFilenameCaseInsensitive
|
|
else
|
|
UnitSrc:=ctdusInFilenameNormal;
|
|
if GetUnitSourceCacheValue(UnitSrc,InFilename,Result) then begin
|
|
// found in cache
|
|
if Result<>'' then begin
|
|
// unit found
|
|
if Directory<>'' then
|
|
InFilename:=CreateRelativePath(Result,Directory);
|
|
end else begin
|
|
// unit not found
|
|
end;
|
|
end else begin
|
|
// not found in cache -> search
|
|
if FilenameIsAbsolute(InFilename) then begin
|
|
// absolute filename
|
|
Result:=FindInFilename(InFilename);
|
|
end else begin
|
|
// 'in'-filename has no complete path
|
|
// -> search file relative to current directory
|
|
CurDir:=Directory;
|
|
if CurDir<>'' then begin
|
|
Result:=FindInFilename(TrimFilename(CurDir+InFilename));
|
|
end else begin
|
|
// this is a virtual directory -> search virtual unit
|
|
InFilename:=Pool.FindVirtualFile(InFilename);
|
|
Result:=InFilename;
|
|
end;
|
|
end;
|
|
AddToCache(UnitSrc,InFilename,Result);
|
|
end;
|
|
end else begin
|
|
// normal unit name
|
|
|
|
if Pos('.',AUnitName)<1 then begin
|
|
// generic unit -> search with namespaces
|
|
NameSpaces:=MergeWithDelimiter(Strings[ctdcsNamespaces],AddNameSpaces,';');
|
|
if NameSpaces<>'' then begin
|
|
// search with additional namespaces, separated by semicolon
|
|
//debugln(['TCTDirectoryCache.FindUnitSourceInCompletePath NameSpaces="',NameSpaces,'"']);
|
|
repeat
|
|
p:=Pos(';',NameSpaces);
|
|
if p>0 then begin
|
|
aNameSpace:=LeftStr(NameSpaces,p-1);
|
|
Delete(NameSpaces,1,p);
|
|
end else begin
|
|
aNameSpace:=NameSpaces;
|
|
NameSpaces:='';
|
|
end;
|
|
if IsValidIdent(aNameSpace,true,true) then begin
|
|
aName:=aNameSpace+'.'+AUnitName;
|
|
Result:=FindUnitSourceInCompletePath(aName,InFilename,AnyCase,
|
|
FPCSrcSearchRequiresPPU,'');
|
|
if Result<>'' then begin
|
|
AUnitName:=RightStr(aName,length(aName)-length(aNameSpace)-1);
|
|
exit;
|
|
end;
|
|
end;
|
|
until NameSpaces='';
|
|
end;
|
|
end;
|
|
|
|
if AnyCase then
|
|
UnitSrc:=ctdusUnitCaseInsensitive
|
|
else
|
|
UnitSrc:=ctdusUnitNormal;
|
|
if GetUnitSourceCacheValue(UnitSrc,AUnitName,Result) then begin
|
|
// found in cache
|
|
if Result<>'' then begin
|
|
// unit found
|
|
end else begin
|
|
// unit not found
|
|
end;
|
|
end else begin
|
|
// not found in cache -> search in complete source path
|
|
|
|
if Directory='' then begin
|
|
// virtual directory => search virtual unit
|
|
Result:=Pool.FindVirtualUnit(AUnitName);
|
|
end;
|
|
if Result='' then begin
|
|
// search in search path
|
|
SrcPath:=Strings[ctdcsCompleteSrcPath];
|
|
Result:=FindUnitSourceInCleanSearchPath(AUnitName,SrcPath,AnyCase);
|
|
end;
|
|
if Result='' then begin
|
|
// search in unit set
|
|
{$IFDEF ShowTriedUnits}
|
|
DebugLn(['TCTDirectoryCache.FindUnitSourceInCompletePath unit ',AUnitName,' not found in SrcPath="',SrcPath,'" Directory="',Directory,'" searchin in unitset ...']);
|
|
{$ENDIF}
|
|
Result:=FindUnitInUnitSet(AUnitName,FPCSrcSearchRequiresPPU);
|
|
{$IFDEF ShowTriedUnits}
|
|
if Result='' then begin
|
|
DebugLn(['TCTDirectoryCache.FindUnitSourceInCompletePath unit ',AUnitName,' not found in unitlinks. Directory="',Directory,'"']);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
AddToCache(UnitSrc,AUnitName,Result);
|
|
end;
|
|
if Result<>'' then begin
|
|
// improve unit name
|
|
NewUnitName:=ExtractFileNameOnly(Result);
|
|
if (NewUnitName<>lowercase(NewUnitName))
|
|
and (AUnitName<>NewUnitName) then
|
|
AUnitName:=NewUnitName;
|
|
end;
|
|
end;
|
|
//DebugLn('TCTDirectoryCache.FindUnitSourceInCompletePath RESULT AUnitName="',AUnitName,'" InFilename="',InFilename,'" Result=',Result);
|
|
end;
|
|
|
|
function TCTDirectoryCache.FindCompiledUnitInCompletePath(
|
|
const AnUnitname: string; AnyCase: boolean): string;
|
|
var
|
|
UnitPath: string;
|
|
UnitSrc: TCTDirectoryUnitSources;
|
|
begin
|
|
Result:='';
|
|
if AnyCase then
|
|
UnitSrc:=ctdusPPUCaseInsensitive
|
|
else
|
|
UnitSrc:=ctdusPPUNormal;
|
|
if GetUnitSourceCacheValue(UnitSrc,AnUnitname,Result) then begin
|
|
//if AnUnitName='lazmkunit.ppu' then
|
|
// debugln(['TCTDirectoryCache.FindCompiledUnitInCompletePath cached ',Result]);
|
|
// found in cache
|
|
if Result<>'' then begin
|
|
// unit found
|
|
end else begin
|
|
// unit not found
|
|
end;
|
|
//debugln(['TCTDirectoryCache.FindCompiledUnitInCompletePath Cached AnUnitname="',AnUnitname,'" Result="',Result,'"']);
|
|
end else begin
|
|
// not found in cache -> search
|
|
|
|
// search in unit path
|
|
UnitPath:=Strings[ctdcsUnitPath];
|
|
Result:=Pool.FindCompiledUnitInPath(Directory,UnitPath,AnUnitname,AnyCase);
|
|
//if AnUnitName='lazmkunit.ppu' then
|
|
// debugln(['TCTDirectoryCache.FindCompiledUnitInCompletePath CurDir="',Directory,'" UnitPath="',UnitPath,'" AnUnitname="',AnUnitname,'" Result=',Result]);
|
|
if Result='' then begin
|
|
// search in unit set
|
|
Result:=FindCompiledUnitInUnitSet(AnUnitname);
|
|
end;
|
|
//if (Result='') then debugln(['TCTDirectoryCache.FindCompiledUnitInCompletePath CurDir="',Directory,'" UnitPath="',UnitPath,'" AnUnitname="',AnUnitname,'" Result=',Result]);
|
|
|
|
AddToCache(UnitSrc,AnUnitname,Result);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.IterateFPCUnitsInSet(const Iterate: TCTOnIterateFile);
|
|
var
|
|
UnitSet: string;
|
|
begin
|
|
UnitSet:=Strings[ctdcsUnitSet];
|
|
Pool.OnIterateFPCUnitsFromSet(UnitSet,Iterate);
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.WriteListing;
|
|
var
|
|
i: Integer;
|
|
Filename: PChar;
|
|
begin
|
|
writeln('TCTDirectoryCache.WriteListing Count=',FListing.Count,' Size=',FListing.Size);
|
|
for i:=0 to FListing.Count-1 do begin
|
|
Filename:=@FListing.Files[FListing.Starts[i]+NameOffset];
|
|
writeln(i,' "',Filename,'"');
|
|
end;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.Invalidate;
|
|
begin
|
|
FListing.FileTimeStamp:=CTInvalidChangeStamp;
|
|
end;
|
|
|
|
procedure TCTDirectoryCache.GetFiles(var Files: TStrings; IncludeDirs: boolean);
|
|
var
|
|
ListedFiles: PChar;
|
|
i: Integer;
|
|
p: PChar;
|
|
begin
|
|
if Files=nil then
|
|
Files:=TStringList.Create;
|
|
if (Self=nil) or (Directory='') then exit;
|
|
UpdateListing;
|
|
ListedFiles:=FListing.Files;
|
|
for i:=0 to FListing.Count-1 do begin
|
|
p:=@ListedFiles[FListing.Starts[i]];
|
|
if IncludeDirs
|
|
or ((PCTDirectoryListingHeader(p)^.Attr and faDirectory)=0) then
|
|
Files.Add(PChar(p+NameOffset));
|
|
end;
|
|
end;
|
|
|
|
{ TCTDirectoryCachePool }
|
|
|
|
procedure TCTDirectoryCachePool.DoRemove(ACache: TCTDirectoryCache);
|
|
begin
|
|
FDirectories.Remove(ACache);
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.OnFileStateCacheChangeTimeStamp(
|
|
Sender: TObject; const AFilename: string);
|
|
var
|
|
Dir: String;
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if AFilename='' then
|
|
IncreaseFileTimeStamp
|
|
else if FilenameIsAbsolute(AFilename) then begin
|
|
Dir:=ExtractFilePath(AFilename);
|
|
Cache:=GetCache(Dir,false,false);
|
|
//debugln(['TCTDirectoryCachePool.OnFileStateCacheChangeTimeStamp Dir="',Dir,'" Cache=',Cache<>nil]);
|
|
if Cache=nil then exit;
|
|
Cache.Invalidate;
|
|
end;
|
|
end;
|
|
|
|
constructor TCTDirectoryCachePool.Create;
|
|
begin
|
|
FDirectories:=TAVLTree.Create(@CompareCTDirectoryCaches);
|
|
IncreaseFileTimeStamp;
|
|
IncreaseConfigTimeStamp;
|
|
if FileStateCache<>nil then
|
|
FileStateCache.AddChangeTimeStampHandler(@OnFileStateCacheChangeTimeStamp);
|
|
end;
|
|
|
|
destructor TCTDirectoryCachePool.Destroy;
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if FileStateCache<>nil then
|
|
FileStateCache.RemoveChangeTimeStampHandler(@OnFileStateCacheChangeTimeStamp);
|
|
while FDirectories.Root<>nil do begin
|
|
Cache:=TCTDirectoryCache(FDirectories.Root.Data);
|
|
if Cache.RefCount<>1 then
|
|
raise Exception.Create('TCTDirectoryCachePool.Destroy');
|
|
Cache.Release;
|
|
end;
|
|
FDirectories.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.CalcMemSize(Stats: TCTMemStats);
|
|
var
|
|
Node: TAVLTreeNode;
|
|
begin
|
|
Stats.Add('TCTDirectoryCachePool',PtrUInt(InstanceSize));
|
|
Stats.Add('TCTDirectoryCachePool.Count',FDirectories.Count);
|
|
Node:=FDirectories.FindLowest;
|
|
while Node<>nil do begin
|
|
TCTDirectoryCache(Node.Data).CalcMemSize(Stats);
|
|
Node:=FDirectories.FindSuccessor(Node);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.GetListing(const aDirectory: string;
|
|
var Files: TStrings; IncludeDirs: boolean);
|
|
begin
|
|
GetCache(aDirectory,true,false).GetFiles(Files,IncludeDirs);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.GetCache(const Directory: string;
|
|
CreateIfNotExists: boolean; DoReference: boolean): TCTDirectoryCache;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Dir: String;
|
|
begin
|
|
Dir:=AppendPathDelim(TrimFilename(Directory));
|
|
Node:=FDirectories.FindKey(Pointer(Dir),@CompareAnsiStringAndDirectoryCache);
|
|
if Node<>nil then begin
|
|
Result:=TCTDirectoryCache(Node.Data);
|
|
if DoReference then
|
|
Result.Reference;
|
|
end else if DoReference or CreateIfNotExists then begin
|
|
Dir:=FindDiskFilename(Directory);
|
|
Result:=TCTDirectoryCache.Create(Dir,Self);
|
|
FDirectories.Add(Result);
|
|
if DoReference then
|
|
Result.Reference;
|
|
end else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.GetString(const Directory: string;
|
|
AStringType: TCTDirCacheString; UseCache: boolean): string;
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if UseCache then begin
|
|
Cache:=GetCache(Directory,true,false);
|
|
if Cache<>nil then
|
|
Result:=Cache.Strings[AStringType]
|
|
else
|
|
Result:='';
|
|
end else begin
|
|
Result:=OnGetString(Directory,AStringType);
|
|
end;
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.IncreaseFileTimeStamp;
|
|
begin
|
|
//DebugLn(['TCTDirectoryCachePool.IncreaseTimeStamp ']);
|
|
CTIncreaseChangeStamp(FFileTimeStamp);
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.IncreaseConfigTimeStamp;
|
|
begin
|
|
//DebugLn(['TCTDirectoryCachePool.IncreaseConfigTimeStamp ']);
|
|
CTIncreaseChangeStamp(FConfigTimeStamp);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FileExists(Filename: string): boolean;
|
|
var
|
|
Directory: String;
|
|
Cache: TCTDirectoryCache;
|
|
ShortFilename: String;
|
|
begin
|
|
Filename:=TrimFilename(Filename);
|
|
if Filename='' then exit(false);
|
|
if FilenameIsAbsolute(Filename) then begin
|
|
ShortFilename:=ExtractFilename(Filename);
|
|
if (ShortFilename<>'') and (ShortFilename<>'.') and (ShortFilename<>'..')
|
|
then begin
|
|
Directory:=ExtractFilePath(Filename);
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindFile(ShortFilename,ctsfcDefault)<>'';
|
|
exit;
|
|
end;
|
|
end;
|
|
// fallback
|
|
Result:=FileStateCache.FileExistsCached(Filename);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FileAge(Filename: string
|
|
): TCTFileAgeTime;
|
|
var
|
|
Directory: String;
|
|
Cache: TCTDirectoryCache;
|
|
ShortFilename: String;
|
|
begin
|
|
Filename:=TrimFilename(Filename);
|
|
if (Filename<>'') and FilenameIsAbsolute(Filename) then begin
|
|
ShortFilename:=ExtractFilename(Filename);
|
|
if (ShortFilename<>'') and (ShortFilename<>'.') and (ShortFilename<>'..')
|
|
then begin
|
|
Directory:=ExtractFilePath(Filename);
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FileAge(ShortFilename);
|
|
exit;
|
|
end;
|
|
end;
|
|
// fallback
|
|
Result:=FileStateCache.FileAgeCached(Filename);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FileAttr(Filename: string
|
|
): TCTDirectoryListingAttr;
|
|
var
|
|
Directory: String;
|
|
Cache: TCTDirectoryCache;
|
|
ShortFilename: String;
|
|
begin
|
|
Filename:=TrimFilename(Filename);
|
|
if (Filename<>'') and FilenameIsAbsolute(Filename) then begin
|
|
ShortFilename:=ExtractFilename(Filename);
|
|
if (ShortFilename<>'') and (ShortFilename<>'.') and (ShortFilename<>'..')
|
|
then begin
|
|
Directory:=ExtractFilePath(Filename);
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FileAttr(ShortFilename);
|
|
exit;
|
|
end;
|
|
end;
|
|
// fallback
|
|
Result:=0;
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FileSize(Filename: string
|
|
): TCTDirectoryListingSize;
|
|
var
|
|
Directory: String;
|
|
Cache: TCTDirectoryCache;
|
|
ShortFilename: String;
|
|
begin
|
|
Filename:=TrimFilename(Filename);
|
|
if (Filename<>'') and FilenameIsAbsolute(Filename) then begin
|
|
ShortFilename:=ExtractFilename(Filename);
|
|
if (ShortFilename<>'') and (ShortFilename<>'.') and (ShortFilename<>'..')
|
|
then begin
|
|
Directory:=ExtractFilePath(Filename);
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FileSize(ShortFilename);
|
|
exit;
|
|
end;
|
|
end;
|
|
// fallback
|
|
Result:=-1;
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindUnitInUnitLinks(const Directory,
|
|
AUnitName: string): string;
|
|
|
|
procedure RaiseDirNotAbsolute;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryCachePool.FindUnitInUnitLinks not absolute Directory="'+Directory+'"');
|
|
end;
|
|
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseDirNotAbsolute;
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindUnitLink(AUnitName);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindUnitInUnitSet(const Directory,
|
|
AUnitName: string): string;
|
|
|
|
procedure RaiseDirNotAbsolute;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryCachePool.FindUnitInUnitSet not absolute Directory="'+Directory+'"');
|
|
end;
|
|
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseDirNotAbsolute;
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindUnitInUnitSet(AUnitName);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindCompiledUnitInUnitSet(const Directory,
|
|
AUnitName: string): string;
|
|
|
|
procedure RaiseDirNotAbsolute;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryCachePool.FindCompiledUnitInUnitSet not absolute Directory="'+Directory+'"');
|
|
end;
|
|
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseDirNotAbsolute;
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindCompiledUnitInUnitSet(AUnitName);
|
|
end;
|
|
|
|
procedure TCTDirectoryCachePool.IterateFPCUnitsInSet(const Directory: string;
|
|
const Iterate: TCTOnIterateFile);
|
|
|
|
procedure RaiseDirNotAbsolute;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryCachePool.IterateFPCUnitsInSet not absolute Directory="'+Directory+'"');
|
|
end;
|
|
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
if (Directory<>'') and not FilenameIsAbsolute(Directory) then
|
|
RaiseDirNotAbsolute;
|
|
Cache:=GetCache(Directory,true,false);
|
|
Cache.IterateFPCUnitsInSet(Iterate);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindDiskFilename(const Filename: string;
|
|
SearchCaseInsensitive: boolean): string;
|
|
var
|
|
ADirectory: String;
|
|
Cache: TCTDirectoryCache;
|
|
DiskShortFilename: String;
|
|
begin
|
|
Result:=ChompPathDelim(ResolveDots(Filename));
|
|
if Result='' then exit;
|
|
//debugln(['TCTDirectoryCachePool.FindDiskFilename Filename=',Result]);
|
|
{$IF defined(NotLiteralFilenames) or defined(CaseInsensitiveFilenames)}
|
|
{$ELSE}
|
|
if (not SearchCaseInsensitive) then exit;
|
|
{$ENDIF}
|
|
ADirectory:=ExtractFilePath(Result);
|
|
if ADirectory=Result then
|
|
exit; // e.g. / under Linux
|
|
if SearchCaseInsensitive then
|
|
// search recursively all directory parts
|
|
ADirectory:=AppendPathDelim(FindDiskFilename(ADirectory,true));
|
|
Cache:=GetCache(ADirectory,true,false);
|
|
//debugln(['TCTDirectoryCachePool.FindDiskFilename Dir=',Cache.Directory]);
|
|
Result:=ExtractFileName(Result);
|
|
DiskShortFilename:=Cache.FindFile(Result,ctsfcAllCase);
|
|
//debugln(['TCTDirectoryCachePool.FindDiskFilename DiskShortFilename=',DiskShortFilename]);
|
|
if DiskShortFilename<>'' then Result:=DiskShortFilename;
|
|
Result:=Cache.Directory+Result;
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindUnitInDirectory(const Directory,
|
|
AUnitName: string; AnyCase: boolean): string;
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindUnitSource(AUnitName,AnyCase);
|
|
if Result='' then exit;
|
|
Result:=Cache.Directory+Result;
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindVirtualFile(const Filename: string): string;
|
|
begin
|
|
if Assigned(OnFindVirtualFile) then
|
|
Result:=OnFindVirtualFile(Filename)
|
|
else
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindVirtualUnit(const AUnitName: string): string;
|
|
var
|
|
e: TCTPascalExtType;
|
|
CurUnitName:String;
|
|
begin
|
|
// search normal
|
|
for e:=Low(CTPascalExtension) to High(CTPascalExtension) do begin
|
|
if CTPascalExtension[e]='' then continue;
|
|
Result:=FindVirtualFile(AUnitName+CTPascalExtension[e]);
|
|
if Result<>'' then exit;
|
|
end;
|
|
// search lowercase
|
|
CurUnitName:=lowercase(AUnitName);
|
|
if CurUnitName<>AUnitName then begin
|
|
for e:=Low(CTPascalExtension) to High(CTPascalExtension) do begin
|
|
if CTPascalExtension[e]='' then continue;
|
|
Result:=FindVirtualFile(CurUnitName+CTPascalExtension[e]);
|
|
if Result<>'' then exit;
|
|
end;
|
|
end;
|
|
// search uppercase
|
|
CurUnitName:=uppercase(AUnitName);
|
|
for e:=Low(CTPascalExtension) to High(CTPascalExtension) do begin
|
|
if CTPascalExtension[e]='' then continue;
|
|
Result:=FindVirtualFile(CurUnitName+uppercase(CTPascalExtension[e]));
|
|
if Result<>'' then exit;
|
|
end;
|
|
Result:='';
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindUnitSourceInCompletePath(
|
|
const Directory: string; var AUnitName, InFilename: string; AnyCase: boolean
|
|
): string;
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindUnitSourceInCompletePath(AUnitName,InFilename,AnyCase);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindCompiledUnitInCompletePath(
|
|
const Directory: string; var AnUnitname: string; AnyCase: boolean
|
|
): string;
|
|
var
|
|
Cache: TCTDirectoryCache;
|
|
begin
|
|
Cache:=GetCache(Directory,true,false);
|
|
Result:=Cache.FindCompiledUnitInCompletePath(AnUnitname,AnyCase);
|
|
end;
|
|
|
|
function TCTDirectoryCachePool.FindCompiledUnitInPath(const BaseDirectory,
|
|
UnitPath, AnUnitname: string; AnyCase: boolean): string;
|
|
var
|
|
StartPos: Integer;
|
|
l: Integer;
|
|
p: Integer;
|
|
CurPath: String;
|
|
Cache: TCTDirectoryCache;
|
|
ShortFilename: String;
|
|
SearchCase: TCTSearchFileCase;
|
|
Base: String;
|
|
begin
|
|
Result:='';
|
|
Base:=AppendPathDelim(TrimFilename(BaseDirectory));
|
|
// search in search path
|
|
StartPos:=1;
|
|
l:=length(UnitPath);
|
|
ShortFilename:=AnUnitname+'.ppu';
|
|
if AnyCase then
|
|
SearchCase:=ctsfcAllCase
|
|
else
|
|
SearchCase:=ctsfcLoUpCase;
|
|
while StartPos<=l do begin
|
|
p:=StartPos;
|
|
while (p<=l) and (UnitPath[p]<>';') do inc(p);
|
|
CurPath:=TrimFilename(copy(UnitPath,StartPos,p-StartPos));
|
|
if CurPath<>'' then begin
|
|
if not FilenameIsAbsolute(CurPath) then
|
|
CurPath:=Base+CurPath;
|
|
if FilenameIsAbsolute(CurPath) then begin
|
|
Cache:=GetCache(CurPath,true,false);
|
|
Result:=Cache.FindFile(ShortFilename,SearchCase);
|
|
if Result<>'' then begin
|
|
Result:=AppendPathDelim(CurPath)+Result;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
StartPos:=p+1;
|
|
end;
|
|
end;
|
|
|
|
{ TCTDirectoryListing }
|
|
|
|
destructor TCTDirectoryListing.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCTDirectoryListing.Clear;
|
|
begin
|
|
if Starts<>nil then begin
|
|
FreeMem(Starts);
|
|
Starts:=nil;
|
|
Size:=0;
|
|
FreeMem(Files);
|
|
Files:=nil;
|
|
Count:=0;
|
|
end;
|
|
end;
|
|
|
|
function TCTDirectoryListing.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
{%H-}+SizeOf(Pointer)*Count // Starts
|
|
+PtrUInt(Size); // Files
|
|
end;
|
|
|
|
function TCTDirectoryListing.GetFilename(Index: integer): PChar;
|
|
|
|
procedure RaiseIndexOutOfBounds;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryListing.GetFilename: Index out of bounds');
|
|
end;
|
|
|
|
begin
|
|
if (Index<0) or (Index>=Count) then
|
|
RaiseIndexOutOfBounds;
|
|
Result:=@Files[Starts[Index]+NameOffset];
|
|
end;
|
|
|
|
function TCTDirectoryListing.GetTime(Index: integer): TCTFileAgeTime;
|
|
|
|
procedure RaiseIndexOutOfBounds;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryListing.GetTime: Index out of bounds');
|
|
end;
|
|
|
|
begin
|
|
if (Index<0) or (Index>=Count) then
|
|
RaiseIndexOutOfBounds;
|
|
Result:=PCTDirectoryListingHeader(@Files[Starts[Index]])^.Time;
|
|
end;
|
|
|
|
function TCTDirectoryListing.GetAttr(Index: integer): TCTDirectoryListingAttr;
|
|
|
|
procedure RaiseIndexOutOfBounds;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryListing.GetAttr: Index out of bounds');
|
|
end;
|
|
|
|
begin
|
|
if (Index<0) or (Index>=Count) then
|
|
RaiseIndexOutOfBounds;
|
|
Result:=PCTDirectoryListingHeader(@Files[Starts[Index]])^.Attr;
|
|
end;
|
|
|
|
function TCTDirectoryListing.GetSize(Index: integer): TCTDirectoryListingSize;
|
|
|
|
procedure RaiseIndexOutOfBounds;
|
|
begin
|
|
raise Exception.Create('TCTDirectoryListing.GetSize: Index out of bounds');
|
|
end;
|
|
|
|
begin
|
|
if (Index<0) or (Index>=Count) then
|
|
RaiseIndexOutOfBounds;
|
|
Result:=PCTDirectoryListingHeader(@Files[Starts[Index]])^.Size;
|
|
end;
|
|
|
|
{ TUnitFileNameLink }
|
|
|
|
function TUnitFileNameLink.CalcMemSize: PtrUInt;
|
|
begin
|
|
Result:=PtrUInt(InstanceSize)
|
|
+MemSizeString(Unit_Name)
|
|
+MemSizeString(Filename);
|
|
end;
|
|
|
|
end.
|
|
|