mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 10:39:53 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1930 lines
		
	
	
		
			62 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1930 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));
 | 
						|
    Assert(Assigned(FListing), 'TCTDirectoryCache.UpdateListing: FListing=Nil.');
 | 
						|
    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.
 | 
						|
 |