mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-27 09:51:58 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1302 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1302 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| {
 | |
|     Copyright (c) 1998-2002 by Florian Klaempfl and Peter Vreman
 | |
| 
 | |
|     This module provides some basic file/dir handling utils and classes
 | |
| 
 | |
|     This program 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 program is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU General Public License
 | |
|     along with this program; if not, write to the Free Software
 | |
|     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | |
| 
 | |
|  ****************************************************************************
 | |
| }
 | |
| unit cfileutl;
 | |
| 
 | |
| {$i fpcdefs.inc}
 | |
| 
 | |
| {$define usedircache}
 | |
| 
 | |
| interface
 | |
| 
 | |
|     uses
 | |
| {$ifdef hasunix}
 | |
|       Baseunix,unix,
 | |
| {$endif hasunix}
 | |
| {$ifdef win32}
 | |
|       Windows,
 | |
| {$endif win32}
 | |
| {$if defined(go32v2) or defined(watcom)}
 | |
|       Dos,
 | |
| {$endif}
 | |
| {$IFNDEF USE_FAKE_SYSUTILS}
 | |
|       SysUtils,
 | |
| {$ELSE}
 | |
|       fksysutl,
 | |
| {$ENDIF}
 | |
|       GlobType,
 | |
|       CUtils,CClasses,
 | |
|       Systems;
 | |
| 
 | |
|     type
 | |
|       TCachedDirectory = class(TFPHashObject)
 | |
|       private
 | |
|         FDirectoryEntries : TFPHashList;
 | |
|         FCached : Boolean;
 | |
|         procedure FreeDirectoryEntries;
 | |
|         function GetItemAttr(const AName: TCmdStr): byte;
 | |
|         function TryUseCache: boolean;
 | |
|         procedure ForceUseCache;
 | |
|         procedure Reload;
 | |
|       public
 | |
|         constructor Create(AList:TFPHashObjectList;const AName:TCmdStr);
 | |
|         destructor  destroy;override;
 | |
|         function FileExists(const AName:TCmdStr):boolean;
 | |
|         function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
 | |
|         function DirectoryExists(const AName:TCmdStr):boolean;
 | |
|         property DirectoryEntries:TFPHashList read FDirectoryEntries;
 | |
|       end;
 | |
| 
 | |
|       TCachedSearchRec = record
 | |
|         Name       : TCmdStr;
 | |
|         Attr       : byte;
 | |
|         Pattern    : TCmdStr;
 | |
|         CachedDir  : TCachedDirectory;
 | |
|         EntryIndex : longint;
 | |
|       end;
 | |
| 
 | |
|       PCachedDirectoryEntry =  ^TCachedDirectoryEntry;
 | |
|       TCachedDirectoryEntry = record
 | |
|         RealName: TCmdStr;
 | |
|         Attr    : longint;
 | |
|       end;
 | |
| 
 | |
|       TDirectoryCache = class
 | |
|       private
 | |
|         FDirectories : TFPHashObjectList;
 | |
|         function GetDirectory(const ADir:TCmdStr):TCachedDirectory;
 | |
|       public
 | |
|         constructor Create;
 | |
|         destructor  destroy;override;
 | |
|         function FileExists(const AName:TCmdStr):boolean;
 | |
|         function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
 | |
|         function DirectoryExists(const AName:TCmdStr):boolean;
 | |
|         function FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
 | |
|         function FindNext(var Res:TCachedSearchRec):boolean;
 | |
|         function FindClose(var Res:TCachedSearchRec):boolean;
 | |
|       end;
 | |
| 
 | |
|       TSearchPathList = class(TCmdStrList)
 | |
|         procedure AddPath(s:TCmdStr;addfirst:boolean);overload;
 | |
|         procedure AddPath(SrcPath,s:TCmdStr;addfirst:boolean);overload;
 | |
|         procedure AddList(list:TSearchPathList;addfirst:boolean);
 | |
|         function  FindFile(const f : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|       end;
 | |
| 
 | |
|     function  bstoslash(const s : TCmdStr) : TCmdStr;
 | |
|     {Gives the absolute path to the current directory}
 | |
|     function  GetCurrentDir:TCmdStr;
 | |
|     {Gives the relative path to the current directory,
 | |
|      with a trailing dir separator. E. g. on unix ./ }
 | |
|     function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
 | |
|     function  path_absolute(const s : TCmdStr) : boolean;
 | |
|     Function  PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
 | |
|     Function  FileExists (const F : TCmdStr;allowcache:boolean) : Boolean;
 | |
|     function  FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|     Function  RemoveDir(d:TCmdStr):boolean;
 | |
|     Function  FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
 | |
|     function  FixFileName(const s:TCmdStr):TCmdStr;
 | |
|     function  TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
 | |
|     function  TargetFixFileName(const s:TCmdStr):TCmdStr;
 | |
|     procedure SplitBinCmd(const s:TCmdStr;var bstr: TCmdStr;var cstr:TCmdStr);
 | |
|     function  FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
| {    function  FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;}
 | |
|     function  FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|     function  GetShortName(const n:TCmdStr):TCmdStr;
 | |
| 
 | |
|     procedure InitFileUtils;
 | |
|     procedure DoneFileUtils;
 | |
| 
 | |
| 
 | |
| { * Since native Amiga commands can't handle Unix-style relative paths used by the compiler,
 | |
|     and some GNU tools, Unix2AmigaPath is needed to handle such situations (KB) * }
 | |
| 
 | |
| {$IF DEFINED(MORPHOS) OR DEFINED(AMIGA)}
 | |
| { * PATHCONV is implemented in the Amiga/MorphOS system unit * }
 | |
| {$WARNING TODO Amiga: implement PathConv() in System unit, which works with AnsiString}
 | |
| function Unix2AmigaPath(path: ShortString): ShortString; external name 'PATHCONV';
 | |
| {$ELSE}
 | |
| function Unix2AmigaPath(path: String): String;{$IFDEF USEINLINE}inline;{$ENDIF}
 | |
| {$ENDIF}
 | |
| 
 | |
| 
 | |
| 
 | |
| implementation
 | |
| 
 | |
|     uses
 | |
|       Comphook,
 | |
|       Globals;
 | |
| 
 | |
| {$undef AllFilesMaskIsInRTL}
 | |
| 
 | |
| {$if (FPC_VERSION > 2)}
 | |
|   {$define AllFilesMaskIsInRTL}
 | |
| {$endif FPC_VERSION}
 | |
| 
 | |
| {$if (FPC_VERSION = 2) and (FPC_RELEASE > 2)}
 | |
|   {$define AllFilesMaskIsInRTL}
 | |
| {$endif}
 | |
| 
 | |
| {$if (FPC_VERSION = 2) and (FPC_RELEASE = 2) and (FPC_PATCH > 0)}
 | |
|   {$define AllFilesMaskIsInRTL}
 | |
| {$endif}
 | |
| 
 | |
| {$ifndef AllFilesMaskIsInRTL}
 | |
|   {$if defined(go32v2) or defined(watcom)}
 | |
|   const
 | |
|     AllFilesMask = '*.*';
 | |
|   {$else}
 | |
|   const
 | |
|     AllFilesMask = '*';
 | |
|   {$endif not (go32v2 or watcom)}
 | |
| {$endif not AllFilesMaskIsInRTL}
 | |
|     var
 | |
|       DirCache : TDirectoryCache;
 | |
| 
 | |
| 
 | |
| {$IF NOT (DEFINED(MORPHOS) OR DEFINED(AMIGA))}
 | |
| { Stub function for Unix2Amiga Path conversion functionality, only available in
 | |
|   Amiga/MorphOS RTL. I'm open for better solutions. (KB) }
 | |
| function Unix2AmigaPath(path: String): String;{$IFDEF USEINLINE}inline;{$ENDIF}
 | |
| begin
 | |
|   Unix2AmigaPath:=path;
 | |
| end;
 | |
| {$ENDIF}
 | |
| 
 | |
| 
 | |
| 
 | |
| {****************************************************************************
 | |
|                            TCachedDirectory
 | |
| ****************************************************************************}
 | |
| 
 | |
|     constructor TCachedDirectory.create(AList:TFPHashObjectList;const AName:TCmdStr);
 | |
|       begin
 | |
|         inherited create(AList,AName);
 | |
|         FDirectoryEntries:=TFPHashList.Create;
 | |
|         FCached:=False;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     destructor TCachedDirectory.destroy;
 | |
|       begin
 | |
|         FreeDirectoryEntries;
 | |
|         FDirectoryEntries.Free;
 | |
|         inherited destroy;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TCachedDirectory.TryUseCache:boolean;
 | |
|       begin
 | |
|         Result:=True;
 | |
|         if FCached then
 | |
|           exit;
 | |
|         if not current_settings.disabledircache then
 | |
|           ForceUseCache
 | |
|         else
 | |
|           Result:=False;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure TCachedDirectory.ForceUseCache;
 | |
|       begin
 | |
|         if not FCached then
 | |
|           begin
 | |
|             FCached:=True;
 | |
|             Reload;
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure TCachedDirectory.FreeDirectoryEntries;
 | |
|       var
 | |
|         i: Integer;
 | |
|       begin
 | |
|         if not(tf_files_case_aware in source_info.flags) then
 | |
|           exit;
 | |
|         for i := 0 to DirectoryEntries.Count-1 do
 | |
|           dispose(PCachedDirectoryEntry(DirectoryEntries[i]));
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TCachedDirectory.GetItemAttr(const AName: TCmdStr): byte;
 | |
|       var
 | |
|         entry: PCachedDirectoryEntry;
 | |
|       begin
 | |
|         if not(tf_files_case_sensitive in source_info.flags) then
 | |
|           if (tf_files_case_aware in source_info.flags) then
 | |
|             begin
 | |
|               entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(AName)));
 | |
|               if assigned(entry) then
 | |
|                 Result:=entry^.Attr
 | |
|               else
 | |
|                 Result:=0;
 | |
|             end
 | |
|           else
 | |
|             Result:=PtrUInt(DirectoryEntries.Find(Lower(AName)))
 | |
|         else
 | |
|           Result:=PtrUInt(DirectoryEntries.Find(AName));
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure TCachedDirectory.Reload;
 | |
|       var
 | |
|         dir   : TSearchRec;
 | |
|         entry : PCachedDirectoryEntry;
 | |
|       begin
 | |
|         FreeDirectoryEntries;
 | |
|         DirectoryEntries.Clear;
 | |
|         if findfirst(IncludeTrailingPathDelimiter(Name)+AllFilesMask,faAnyFile or faDirectory,dir) = 0 then
 | |
|           begin
 | |
|             repeat
 | |
|               if ((dir.attr and faDirectory)<>faDirectory) or
 | |
|                  ((dir.Name<>'.') and
 | |
|                   (dir.Name<>'..')) then
 | |
|                 begin
 | |
|                   { Force Archive bit so the attribute always has a value. This is needed
 | |
|                     to be able to see the difference in the directoryentries lookup if a file
 | |
|                     exists or not }
 | |
|                   Dir.Attr:=Dir.Attr or faArchive;
 | |
|                   if not(tf_files_case_sensitive in source_info.flags) then
 | |
|                     if (tf_files_case_aware in source_info.flags) then
 | |
|                       begin
 | |
|                         new(entry);
 | |
|                         entry^.RealName:=Dir.Name;
 | |
|                         entry^.Attr:=Dir.Attr;
 | |
|                         DirectoryEntries.Add(Lower(Dir.Name),entry)
 | |
|                       end
 | |
|                     else
 | |
|                       DirectoryEntries.Add(Lower(Dir.Name),Pointer(Ptrint(Dir.Attr)))
 | |
|                   else
 | |
|                     DirectoryEntries.Add(Dir.Name,Pointer(Ptrint(Dir.Attr)));
 | |
|                 end;
 | |
|             until findnext(dir) <> 0;
 | |
|           end;
 | |
|         findclose(dir);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TCachedDirectory.FileExists(const AName:TCmdStr):boolean;
 | |
|       var
 | |
|         Attr : Longint;
 | |
|       begin
 | |
|         if not TryUseCache then
 | |
|           begin
 | |
|             { prepend directory name again }
 | |
|             result:=cfileutl.FileExists(Name+AName,false);
 | |
|             exit;
 | |
|           end;
 | |
|         Attr:=GetItemAttr(AName);
 | |
|         if Attr<>0 then
 | |
|           Result:=((Attr and faDirectory)=0)
 | |
|         else
 | |
|           Result:=false;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TCachedDirectory.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
 | |
|       var
 | |
|         entry : PCachedDirectoryEntry;
 | |
|       begin
 | |
|         if (tf_files_case_aware in source_info.flags) then
 | |
|           begin
 | |
|             if not TryUseCache then
 | |
|               begin
 | |
|                 Result:=FileExistsNonCase(path,fn,false,FoundName);
 | |
|                 exit;
 | |
|               end;
 | |
|             entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(ExtractFileName(fn))));
 | |
|             if assigned(entry) and
 | |
|                (entry^.Attr<>0) and
 | |
|                ((entry^.Attr and faDirectory) = 0) then
 | |
|               begin
 | |
|                  FoundName:=ExtractFilePath(path+fn)+entry^.RealName;
 | |
|                  Result:=true
 | |
|               end
 | |
|             else
 | |
|               Result:=false;
 | |
|           end
 | |
|         else
 | |
|           { should not be called in this case, use plain FileExists }
 | |
|           Result:=False;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TCachedDirectory.DirectoryExists(const AName:TCmdStr):boolean;
 | |
|       var
 | |
|         Attr : Longint;
 | |
|       begin
 | |
|         if not TryUseCache then
 | |
|           begin
 | |
|             Result:=PathExists(Name+AName,false);
 | |
|             exit;
 | |
|           end;
 | |
|         Attr:=GetItemAttr(AName);
 | |
|         if Attr<>0 then
 | |
|           Result:=((Attr and faDirectory)=faDirectory)
 | |
|         else
 | |
|           Result:=false;
 | |
|       end;
 | |
| 
 | |
| 
 | |
| {****************************************************************************
 | |
|                            TDirectoryCache
 | |
| ****************************************************************************}
 | |
| 
 | |
|     constructor TDirectoryCache.create;
 | |
|       begin
 | |
|         inherited create;
 | |
|         FDirectories:=TFPHashObjectList.Create(true);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     destructor TDirectoryCache.destroy;
 | |
|       begin
 | |
|         FDirectories.Free;
 | |
|         inherited destroy;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.GetDirectory(const ADir:TCmdStr):TCachedDirectory;
 | |
|       var
 | |
|         CachedDir : TCachedDirectory;
 | |
|         DirName   : TCmdStr;
 | |
|       begin
 | |
|         if ADir='' then
 | |
|           DirName:='.'+source_info.DirSep
 | |
|         else
 | |
|           DirName:=ADir;
 | |
|         CachedDir:=TCachedDirectory(FDirectories.Find(DirName));
 | |
|         if not assigned(CachedDir) then
 | |
|           CachedDir:=TCachedDirectory.Create(FDirectories,DirName);
 | |
|         Result:=CachedDir;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.FileExists(const AName:TCmdStr):boolean;
 | |
|       var
 | |
|         CachedDir : TCachedDirectory;
 | |
|       begin
 | |
|         Result:=false;
 | |
|         CachedDir:=GetDirectory(ExtractFilePath(AName));
 | |
|         if assigned(CachedDir) then
 | |
|           Result:=CachedDir.FileExists(ExtractFileName(AName));
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
 | |
|       var
 | |
|         CachedDir : TCachedDirectory;
 | |
|       begin
 | |
|         Result:=false;
 | |
|         CachedDir:=GetDirectory(ExtractFilePath(path+fn));
 | |
|         if assigned(CachedDir) then
 | |
|           Result:=CachedDir.FileExistsCaseAware(path,fn,FoundName);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.DirectoryExists(const AName:TCmdStr):boolean;
 | |
|       var
 | |
|         CachedDir : TCachedDirectory;
 | |
|       begin
 | |
|         Result:=false;
 | |
|         CachedDir:=GetDirectory(ExtractFilePath(AName));
 | |
|         if assigned(CachedDir) then
 | |
|           Result:=CachedDir.DirectoryExists(ExtractFileName(AName));
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
 | |
|       begin
 | |
|         Res.Pattern:=ExtractFileName(APattern);
 | |
|         Res.CachedDir:=GetDirectory(ExtractFilePath(APattern));
 | |
|         Res.CachedDir.ForceUseCache;
 | |
|         Res.EntryIndex:=0;
 | |
|         if assigned(Res.CachedDir) then
 | |
|           Result:=FindNext(Res)
 | |
|         else
 | |
|           Result:=false;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.FindNext(var Res:TCachedSearchRec):boolean;
 | |
|       var
 | |
|         entry: PCachedDirectoryEntry;
 | |
|       begin
 | |
|         if Res.EntryIndex<Res.CachedDir.DirectoryEntries.Count then
 | |
|           begin
 | |
|             if (tf_files_case_aware in source_info.flags) then
 | |
|               begin
 | |
|                 entry:=Res.CachedDir.DirectoryEntries[Res.EntryIndex];
 | |
|                 Res.Name:=entry^.RealName;
 | |
|                 Res.Attr:=entry^.Attr;
 | |
|               end
 | |
|             else
 | |
|               begin
 | |
|                 Res.Name:=Res.CachedDir.DirectoryEntries.NameOfIndex(Res.EntryIndex);
 | |
|                 Res.Attr:=PtrUInt(Res.CachedDir.DirectoryEntries[Res.EntryIndex]);
 | |
|               end;
 | |
|             inc(Res.EntryIndex);
 | |
|             Result:=true;
 | |
|           end
 | |
|         else
 | |
|           Result:=false;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function TDirectoryCache.FindClose(var Res:TCachedSearchRec):boolean;
 | |
|       begin
 | |
|         { nothing todo }
 | |
|         result:=true;
 | |
|       end;
 | |
| 
 | |
| 
 | |
| {****************************************************************************
 | |
|                                    Utils
 | |
| ****************************************************************************}
 | |
| 
 | |
|     function bstoslash(const s : TCmdStr) : TCmdStr;
 | |
|     {
 | |
|       return TCmdStr s with all \ changed into /
 | |
|     }
 | |
|       var
 | |
|          i : longint;
 | |
|       begin
 | |
|         setlength(bstoslash,length(s));
 | |
|         for i:=1to length(s) do
 | |
|          if s[i]='\' then
 | |
|           bstoslash[i]:='/'
 | |
|          else
 | |
|           bstoslash[i]:=s[i];
 | |
|       end;
 | |
| 
 | |
| 
 | |
|    {Gives the absolute path to the current directory}
 | |
|      var
 | |
|        CachedCurrentDir : TCmdStr;
 | |
|    function GetCurrentDir:TCmdStr;
 | |
|      begin
 | |
|        if CachedCurrentDir='' then
 | |
|          begin
 | |
|            GetDir(0,CachedCurrentDir);
 | |
|            CachedCurrentDir:=FixPath(CachedCurrentDir,false);
 | |
|          end;
 | |
|        result:=CachedCurrentDir;
 | |
|      end;
 | |
| 
 | |
|    {Gives the relative path to the current directory,
 | |
|     with a trailing dir separator. E. g. on unix ./ }
 | |
|    function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
 | |
| 
 | |
|    begin
 | |
|      if systeminfo.system <> system_powerpc_macos then
 | |
|        CurDirRelPath:= '.'+systeminfo.DirSep
 | |
|      else
 | |
|        CurDirRelPath:= ':'
 | |
|    end;
 | |
| 
 | |
| 
 | |
|    function path_absolute(const s : TCmdStr) : boolean;
 | |
|    {
 | |
|      is path s an absolute path?
 | |
|    }
 | |
|      begin
 | |
|         result:=false;
 | |
| {$if defined(unix)}
 | |
|         if (length(s)>0) and (s[1] in AllowDirectorySeparators) then
 | |
|           result:=true;
 | |
| {$elseif defined(amiga) or defined(morphos)}
 | |
|         (* An Amiga path is absolute, if it has a volume/device name in it (contains ":"), 
 | |
|            otherwise it's always a relative path, no matter if it starts with a directory 
 | |
|            separator or not. (KB) *)
 | |
|         if (length(s)>0) and (Pos(':',s) <> 0) then
 | |
|           result:=true;
 | |
| {$elseif defined(macos)}
 | |
|         if IsMacFullPath(s) then
 | |
|           result:=true;
 | |
| {$elseif defined(netware)}
 | |
|         if (Pos (DriveSeparator, S) <> 0) or
 | |
|                 ((Length (S) > 0) and (S [1] in AllowDirectorySeparators)) then
 | |
|           result:=true;
 | |
| {$elseif defined(win32) or defined(win64) or defined(go32v2) or defined(os2) or defined(watcom)}
 | |
|         if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
 | |
| (* The following check for non-empty AllowDriveSeparators assumes that all
 | |
|    other platforms supporting drives and not handled as exceptions above
 | |
|    should work with DOS-like paths, i.e. use absolute paths with one letter
 | |
|    for drive followed by path separator *)
 | |
|            ((length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
 | |
|           result:=true;
 | |
| {$else}
 | |
|         if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
 | |
| (* The following check for non-empty AllowDriveSeparators assumes that all
 | |
|    other platforms supporting drives and not handled as exceptions above
 | |
|    should work with DOS-like paths, i.e. use absolute paths with one letter
 | |
|    for drive followed by path separator *)
 | |
|            ((AllowDriveSeparators <> []) and (length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
 | |
|           result:=true;
 | |
| {$endif unix}
 | |
|      end;
 | |
| 
 | |
|     Function FileExists ( Const F : TCmdStr;allowcache:boolean) : Boolean;
 | |
|       begin
 | |
| {$ifdef usedircache}
 | |
|         if allowcache then
 | |
|           Result:=DirCache.FileExists(F)
 | |
|         else
 | |
| {$endif usedircache}
 | |
|           Result:=SysUtils.FileExists(F);
 | |
|         if do_checkverbosity(V_Tried) then
 | |
|          begin
 | |
|            if Result then
 | |
|              do_comment(V_Tried,'Searching file '+F+'... found')
 | |
|            else
 | |
|              do_comment(V_Tried,'Searching file '+F+'... not found');
 | |
|          end;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     function FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|       var
 | |
|         fn2 : TCmdStr;
 | |
|       begin
 | |
|         result:=false;
 | |
|         if tf_files_case_sensitive in source_info.flags then
 | |
|           begin
 | |
|             {
 | |
|               Search order for case sensitive systems:
 | |
|                1. NormalCase
 | |
|                2. lowercase
 | |
|                3. UPPERCASE
 | |
|             }
 | |
|             FoundFile:=path+fn;
 | |
|             If FileExists(FoundFile,allowcache) then
 | |
|              begin
 | |
|                result:=true;
 | |
|                exit;
 | |
|              end;
 | |
|             fn2:=Lower(fn);
 | |
|             if fn2<>fn then
 | |
|               begin
 | |
|                 FoundFile:=path+fn2;
 | |
|                 If FileExists(FoundFile,allowcache) then
 | |
|                  begin
 | |
|                    result:=true;
 | |
|                    exit;
 | |
|                  end;
 | |
|               end;
 | |
|             fn2:=Upper(fn);
 | |
|             if fn2<>fn then
 | |
|               begin
 | |
|                 FoundFile:=path+fn2;
 | |
|                 If FileExists(FoundFile,allowcache) then
 | |
|                  begin
 | |
|                    result:=true;
 | |
|                    exit;
 | |
|                  end;
 | |
|               end;
 | |
|           end
 | |
|         else
 | |
|           if tf_files_case_aware in source_info.flags then
 | |
|             begin
 | |
|               {
 | |
|                 Search order for case aware systems:
 | |
|                  1. NormalCase
 | |
|               }
 | |
| {$ifdef usedircache}
 | |
|               if allowcache then
 | |
|                 begin
 | |
|                   result:=DirCache.FileExistsCaseAware(path,fn,fn2);
 | |
|                   if result then
 | |
|                     begin
 | |
|                       FoundFile:=fn2;
 | |
|                       exit;
 | |
|                     end;
 | |
|                 end
 | |
|               else
 | |
| {$endif usedircache}
 | |
|                 begin
 | |
|                   FoundFile:=path+fn;
 | |
|                   If FileExists(FoundFile,allowcache) then
 | |
|                     begin
 | |
|                       { don't know the real name in this case }
 | |
|                       result:=true;
 | |
|                       exit;
 | |
|                    end;
 | |
|                 end;
 | |
|            end
 | |
|         else
 | |
|           begin
 | |
|             { None case sensitive only lowercase }
 | |
|             FoundFile:=path+Lower(fn);
 | |
|             If FileExists(FoundFile,allowcache) then
 | |
|              begin
 | |
|                result:=true;
 | |
|                exit;
 | |
|              end;
 | |
|           end;
 | |
|         { Set foundfile to something usefull }
 | |
|         FoundFile:=fn;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     Function PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
 | |
|       Var
 | |
|         i: longint;
 | |
|         hs : TCmdStr;
 | |
|       begin
 | |
|         if F = '' then
 | |
|           begin
 | |
|             result := true;
 | |
|             exit;
 | |
|           end;
 | |
|         hs := ExpandFileName(F);
 | |
|         I := Pos (DriveSeparator, hs);
 | |
|         if (hs [Length (hs)] = DirectorySeparator) and
 | |
|            (((I = 0) and (Length (hs) > 1)) or (I <> Length (hs) - 1)) then
 | |
|           Delete (hs, Length (hs), 1);
 | |
| {$ifdef usedircache}
 | |
|         if allowcache then
 | |
|           Result:=DirCache.DirectoryExists(hs)
 | |
|         else
 | |
| {$endif usedircache}
 | |
|           Result:=SysUtils.DirectoryExists(hs);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     Function RemoveDir(d:TCmdStr):boolean;
 | |
|       begin
 | |
|         if d[length(d)]=source_info.DirSep then
 | |
|          Delete(d,length(d),1);
 | |
|         {$I-}
 | |
|          rmdir(d);
 | |
|         {$I+}
 | |
|         RemoveDir:=(ioresult=0);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     Function FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
 | |
|       var
 | |
|         i, L : longint;
 | |
|         P: PChar;
 | |
|       begin
 | |
|         Result := s;
 | |
|         L := Length(Result);
 | |
|         if L=0 then
 | |
|           exit;
 | |
|         { Fix separator }
 | |
|         P := @Result[1];
 | |
|         for i:=0 to L-1 do
 | |
|           begin
 | |
|             if p^ in ['/','\'] then
 | |
|               p^:=source_info.DirSep;
 | |
|             inc(p);
 | |
|           end;
 | |
|         { Fix ending / }
 | |
|         if (L>0) and (Result[L]<>source_info.DirSep) and
 | |
|            (Result[L]<>DriveSeparator) then
 | |
|           Result:=Result+source_info.DirSep;  { !still results in temp AnsiString }
 | |
|         { Remove ./ }
 | |
|         if (not allowdot) and ((Length(Result)=2) and (Result[1]='.') and (Result[2] = source_info.DirSep)) then
 | |
|           begin
 | |
|             Result:='';
 | |
|             Exit;
 | |
|           end;
 | |
|         { return }
 | |
|         if not ((tf_files_case_aware in source_info.flags) or
 | |
|            (tf_files_case_sensitive in source_info.flags)) then
 | |
|           Result := lower(Result);
 | |
|       end;
 | |
| 
 | |
|   {Actually the version in macutils.pp could be used,
 | |
|    but that would not work for crosscompiling, so this is a slightly modified
 | |
|    version of it.}
 | |
|   function TranslatePathToMac (const path: TCmdStr; mpw: Boolean): TCmdStr;
 | |
| 
 | |
|     function GetVolumeIdentifier: TCmdStr;
 | |
| 
 | |
|     begin
 | |
|       GetVolumeIdentifier := '{Boot}'
 | |
|       (*
 | |
|       if mpw then
 | |
|         GetVolumeIdentifier := '{Boot}'
 | |
|       else
 | |
|         GetVolumeIdentifier := macosBootVolumeName;
 | |
|       *)
 | |
|     end;
 | |
| 
 | |
|     var
 | |
|       slashPos, oldpos, newpos, oldlen, maxpos: Longint;
 | |
| 
 | |
|   begin
 | |
|     oldpos := 1;
 | |
|     slashPos := Pos('/', path);
 | |
|     if (slashPos <> 0) then   {its a unix path}
 | |
|       begin
 | |
|         if slashPos = 1 then
 | |
|           begin      {its a full path}
 | |
|             oldpos := 2;
 | |
|             TranslatePathToMac := GetVolumeIdentifier;
 | |
|           end
 | |
|         else     {its a partial path}
 | |
|           TranslatePathToMac := ':';
 | |
|       end
 | |
|     else
 | |
|       begin
 | |
|         slashPos := Pos('\', path);
 | |
|         if (slashPos <> 0) then   {its a dos path}
 | |
|           begin
 | |
|             if slashPos = 1 then
 | |
|               begin      {its a full path, without drive letter}
 | |
|                 oldpos := 2;
 | |
|                 TranslatePathToMac := GetVolumeIdentifier;
 | |
|               end
 | |
|             else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
 | |
|               begin
 | |
|                 oldpos := 4;
 | |
|                 TranslatePathToMac := GetVolumeIdentifier;
 | |
|               end
 | |
|             else     {its a partial path}
 | |
|               TranslatePathToMac := ':';
 | |
|           end;
 | |
|       end;
 | |
| 
 | |
|     if (slashPos <> 0) then   {its a unix or dos path}
 | |
|       begin
 | |
|         {Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
 | |
|         newpos := Length(TranslatePathToMac);
 | |
|         oldlen := Length(path);
 | |
|         SetLength(TranslatePathToMac, newpos + oldlen);  {It will be no longer than what is already}
 | |
|                                                                         {prepended plus length of path.}
 | |
|         maxpos := Length(TranslatePathToMac);          {Get real maxpos, can be short if String is ShortString}
 | |
| 
 | |
|         {There is never a slash in the beginning, because either it was an absolute path, and then the}
 | |
|         {drive and slash was removed, or it was a relative path without a preceding slash.}
 | |
|         while oldpos <= oldlen do
 | |
|           begin
 | |
|             {Check if special dirs, ./ or ../ }
 | |
|             if path[oldPos] = '.' then
 | |
|               if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
 | |
|                 begin
 | |
|                   if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
 | |
|                     begin
 | |
|                       {It is "../" or ".."  translates to ":" }
 | |
|                       if newPos = maxPos then
 | |
|                         begin {Shouldn't actually happen, but..}
 | |
|                           Exit('');
 | |
|                         end;
 | |
|                       newPos := newPos + 1;
 | |
|                       TranslatePathToMac[newPos] := ':';
 | |
|                       oldPos := oldPos + 3;
 | |
|                       continue;  {Start over again}
 | |
|                     end;
 | |
|                 end
 | |
|               else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
 | |
|                 begin
 | |
|                   {It is "./" or "."  ignor it }
 | |
|                   oldPos := oldPos + 2;
 | |
|                   continue;  {Start over again}
 | |
|                 end;
 | |
| 
 | |
|             {Collect file or dir name}
 | |
|             while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
 | |
|               begin
 | |
|                 if newPos = maxPos then
 | |
|                   begin {Shouldn't actually happen, but..}
 | |
|                     Exit('');
 | |
|                   end;
 | |
|                 newPos := newPos + 1;
 | |
|                 TranslatePathToMac[newPos] := path[oldPos];
 | |
|                 oldPos := oldPos + 1;
 | |
|               end;
 | |
| 
 | |
|             {When we come here there is either a slash or we are at the end.}
 | |
|             if (oldpos <= oldlen) then
 | |
|               begin
 | |
|                 if newPos = maxPos then
 | |
|                   begin {Shouldn't actually happen, but..}
 | |
|                     Exit('');
 | |
|                   end;
 | |
|                 newPos := newPos + 1;
 | |
|                 TranslatePathToMac[newPos] := ':';
 | |
|                 oldPos := oldPos + 1;
 | |
|               end;
 | |
|           end;
 | |
| 
 | |
|         SetLength(TranslatePathToMac, newpos);
 | |
|       end
 | |
|     else if (path = '.') then
 | |
|       TranslatePathToMac := ':'
 | |
|     else if (path = '..') then
 | |
|       TranslatePathToMac := '::'
 | |
|     else
 | |
|       TranslatePathToMac := path;  {its a mac path}
 | |
|   end;
 | |
| 
 | |
| 
 | |
|    function FixFileName(const s:TCmdStr):TCmdStr;
 | |
|      var
 | |
|        i      : longint;
 | |
|      begin
 | |
|        if source_info.system = system_powerpc_MACOS then
 | |
|          FixFileName:= TranslatePathToMac(s, true)
 | |
|        else
 | |
|         if (tf_files_case_aware in source_info.flags) or
 | |
|            (tf_files_case_sensitive in source_info.flags) then
 | |
|         begin
 | |
|           setlength(FixFileName,length(s));
 | |
|           for i:=1 to length(s) do
 | |
|            begin
 | |
|              case s[i] of
 | |
|                '/','\' :
 | |
|                  FixFileName[i]:=source_info.dirsep;
 | |
|                else
 | |
|                  FixFileName[i]:=s[i];
 | |
|              end;
 | |
|            end;
 | |
|         end
 | |
|        else
 | |
|         begin
 | |
|           setlength(FixFileName,length(s));
 | |
|           for i:=1 to length(s) do
 | |
|            begin
 | |
|              case s[i] of
 | |
|                '/','\' :
 | |
|                   FixFileName[i]:=source_info.dirsep;
 | |
|                'A'..'Z' :
 | |
|                   FixFileName[i]:=char(byte(s[i])+32);
 | |
|                 else
 | |
|                   FixFileName[i]:=s[i];
 | |
|              end;
 | |
|            end;
 | |
|         end;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|     Function TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
 | |
|       var
 | |
|         i : longint;
 | |
|       begin
 | |
|         { Fix separator }
 | |
|         for i:=1 to length(s) do
 | |
|          if s[i] in ['/','\'] then
 | |
|           s[i]:=target_info.DirSep;
 | |
|         { Fix ending / }
 | |
|         if (length(s)>0) and (s[length(s)]<>target_info.DirSep) and
 | |
|            (s[length(s)]<>':') then
 | |
|          s:=s+target_info.DirSep;
 | |
|         { Remove ./ }
 | |
|         if (not allowdot) and (s='.'+target_info.DirSep) then
 | |
|          s:='';
 | |
|         { return }
 | |
|         if (tf_files_case_aware in target_info.flags) or
 | |
|            (tf_files_case_sensitive in target_info.flags) then
 | |
|          TargetFixPath:=s
 | |
|         else
 | |
|          TargetFixPath:=Lower(s);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|    function TargetFixFileName(const s:TCmdStr):TCmdStr;
 | |
|      var
 | |
|        i : longint;
 | |
|      begin
 | |
|        if target_info.system = system_powerpc_MACOS then
 | |
|          TargetFixFileName:= TranslatePathToMac(s, true)
 | |
|        else
 | |
|         if (tf_files_case_aware in target_info.flags) or
 | |
|            (tf_files_case_sensitive in target_info.flags) then
 | |
|          begin
 | |
|            setlength(TargetFixFileName,length(s));
 | |
|            for i:=1 to length(s) do
 | |
|            begin
 | |
|              case s[i] of
 | |
|                '/','\' :
 | |
|                  TargetFixFileName[i]:=target_info.dirsep;
 | |
|                else
 | |
|                  TargetFixFileName[i]:=s[i];
 | |
|              end;
 | |
|            end;
 | |
|          end
 | |
|        else
 | |
|          begin
 | |
|            setlength(TargetFixFileName,length(s));
 | |
|            for i:=1 to length(s) do
 | |
|            begin
 | |
|              case s[i] of
 | |
|                '/','\' :
 | |
|                   TargetFixFileName[i]:=target_info.dirsep;
 | |
|                'A'..'Z' :
 | |
|                   TargetFixFileName[i]:=char(byte(s[i])+32);
 | |
|                 else
 | |
|                   TargetFixFileName[i]:=s[i];
 | |
|              end;
 | |
|            end;
 | |
|          end;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|    procedure SplitBinCmd(const s:TCmdStr;var bstr:TCmdStr;var cstr:TCmdStr);
 | |
|      var
 | |
|        i : longint;
 | |
|      begin
 | |
|        i:=pos(' ',s);
 | |
|        if i>0 then
 | |
|         begin
 | |
|           bstr:=Copy(s,1,i-1);
 | |
|           cstr:=Copy(s,i+1,length(s)-i);
 | |
|         end
 | |
|        else
 | |
|         begin
 | |
|           bstr:=s;
 | |
|           cstr:='';
 | |
|         end;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|     procedure TSearchPathList.AddPath(s:TCmdStr;addfirst:boolean);
 | |
|       begin
 | |
|         AddPath('',s,AddFirst);
 | |
|       end;
 | |
| 
 | |
| 
 | |
|    procedure TSearchPathList.AddPath(SrcPath,s:TCmdStr;addfirst:boolean);
 | |
|      var
 | |
|        staridx,
 | |
|        i,j      : longint;
 | |
|        prefix,
 | |
|        suffix,
 | |
|        CurrentDir,
 | |
|        currPath : TCmdStr;
 | |
|        subdirfound : boolean;
 | |
| {$ifdef usedircache}
 | |
|        dir      : TCachedSearchRec;
 | |
| {$else usedircache}
 | |
|        dir      : TSearchRec;
 | |
| {$endif usedircache}
 | |
|        hp       : TCmdStrListItem;
 | |
| 
 | |
|        procedure WarnNonExistingPath(const path : TCmdStr);
 | |
|        begin
 | |
|          if do_checkverbosity(V_Tried) then
 | |
|            do_comment(V_Tried,'Path "'+path+'" not found');
 | |
|        end;
 | |
| 
 | |
|        procedure AddCurrPath;
 | |
|        begin
 | |
|          if addfirst then
 | |
|           begin
 | |
|             Remove(currPath);
 | |
|             Insert(currPath);
 | |
|           end
 | |
|          else
 | |
|           begin
 | |
|             { Check if already in path, then we don't add it }
 | |
|             hp:=Find(currPath);
 | |
|             if not assigned(hp) then
 | |
|              Concat(currPath);
 | |
|           end;
 | |
|        end;
 | |
| 
 | |
|      begin
 | |
|        if s='' then
 | |
|         exit;
 | |
|      { Support default macro's }
 | |
|        DefaultReplacements(s);
 | |
| {$warnings off}
 | |
|        if PathSeparator <> ';' then
 | |
|         for i:=1 to length(s) do
 | |
|          if s[i]=PathSeparator then
 | |
|           s[i]:=';';
 | |
| {$warnings on}
 | |
|      { get current dir }
 | |
|        CurrentDir:=GetCurrentDir;
 | |
|        repeat
 | |
|          { get currpath }
 | |
|          if addfirst then
 | |
|           begin
 | |
|             j:=length(s);
 | |
|             while (j>0) and (s[j]<>';') do
 | |
|              dec(j);
 | |
|             currPath:= TrimSpace(Copy(s,j+1,length(s)-j));
 | |
|             if j=0 then
 | |
|              s:=''
 | |
|             else
 | |
|              System.Delete(s,j,length(s)-j+1);
 | |
|           end
 | |
|          else
 | |
|           begin
 | |
|             j:=Pos(';',s);
 | |
|             if j=0 then
 | |
|              j:=length(s)+1;
 | |
|             currPath:= TrimSpace(Copy(s,1,j-1));
 | |
|             System.Delete(s,1,j);
 | |
|           end;
 | |
| 
 | |
|          { fix pathname }
 | |
|          DePascalQuote(currPath);
 | |
|          currPath:=SrcPath+FixPath(currPath,false);
 | |
|          if currPath='' then
 | |
|            currPath:= CurDirRelPath(source_info)
 | |
|          else
 | |
|           begin
 | |
|             currPath:=FixPath(ExpandFileName(currpath),false);
 | |
|             if (CurrentDir<>'') and (Copy(currPath,1,length(CurrentDir))=CurrentDir) then
 | |
|              begin
 | |
| {$if defined(amiga) and defined(morphos)}
 | |
|                currPath:= CurrentDir+Copy(currPath,length(CurrentDir)+1,length(currPath));
 | |
| {$else}
 | |
|                currPath:= CurDirRelPath(source_info)+Copy(currPath,length(CurrentDir)+1,length(currPath));
 | |
| {$endif}
 | |
|              end;
 | |
|           end;
 | |
|          { wildcard adding ? }
 | |
|          staridx:=pos('*',currpath);
 | |
|          if staridx>0 then
 | |
|           begin
 | |
|             prefix:=ExtractFilePath(Copy(currpath,1,staridx));
 | |
|             suffix:=Copy(currpath,staridx+1,length(currpath));
 | |
|             subdirfound:=false;
 | |
| {$ifdef usedircache}
 | |
|             if DirCache.FindFirst(Prefix+AllFilesMask,dir) then
 | |
|               begin
 | |
|                 repeat
 | |
|                   if (dir.attr and faDirectory)<>0 then
 | |
|                     begin
 | |
|                       subdirfound:=true;
 | |
|                       currpath:=prefix+dir.name+suffix;
 | |
|                       if (suffix='') or PathExists(currpath,true) then
 | |
|                         begin
 | |
|                           hp:=Find(currPath);
 | |
|                           if not assigned(hp) then
 | |
|                             AddCurrPath;
 | |
|                         end;
 | |
|                     end;
 | |
|                 until not DirCache.FindNext(dir);
 | |
|               end;
 | |
|             DirCache.FindClose(dir);
 | |
| {$else usedircache}
 | |
|             if findfirst(prefix+AllFilesMask,faDirectory,dir) = 0 then
 | |
|               begin
 | |
|                 repeat
 | |
|                   if (dir.name<>'.') and
 | |
|                       (dir.name<>'..') and
 | |
|                       ((dir.attr and faDirectory)<>0) then
 | |
|                     begin
 | |
|                       subdirfound:=true;
 | |
|                       currpath:=prefix+dir.name+suffix;
 | |
|                       if (suffix='') or PathExists(currpath,false) then
 | |
|                         begin
 | |
|                           hp:=Find(currPath);
 | |
|                           if not assigned(hp) then
 | |
|                             AddCurrPath;
 | |
|                         end;
 | |
|                     end;
 | |
|                 until findnext(dir) <> 0;
 | |
|               end;
 | |
|             FindClose(dir);
 | |
| {$endif usedircache}
 | |
|             if not subdirfound then
 | |
|               WarnNonExistingPath(currpath);
 | |
|           end
 | |
|          else
 | |
|           begin
 | |
|             if PathExists(currpath,true) then
 | |
|              AddCurrPath
 | |
|             else
 | |
|              WarnNonExistingPath(currpath);
 | |
|           end;
 | |
|        until (s='');
 | |
|      end;
 | |
| 
 | |
| 
 | |
|    procedure TSearchPathList.AddList(list:TSearchPathList;addfirst:boolean);
 | |
|      var
 | |
|        s : TCmdStr;
 | |
|        hl : TSearchPathList;
 | |
|        hp,hp2 : TCmdStrListItem;
 | |
|      begin
 | |
|        if list.empty then
 | |
|         exit;
 | |
|        { create temp and reverse the list }
 | |
|        if addfirst then
 | |
|         begin
 | |
|           hl:=TSearchPathList.Create;
 | |
|           hp:=TCmdStrListItem(list.first);
 | |
|           while assigned(hp) do
 | |
|            begin
 | |
|              hl.insert(hp.Str);
 | |
|              hp:=TCmdStrListItem(hp.next);
 | |
|            end;
 | |
|           while not hl.empty do
 | |
|            begin
 | |
|              s:=hl.GetFirst;
 | |
|              Remove(s);
 | |
|              Insert(s);
 | |
|            end;
 | |
|           hl.Free;
 | |
|         end
 | |
|        else
 | |
|         begin
 | |
|           hp:=TCmdStrListItem(list.first);
 | |
|           while assigned(hp) do
 | |
|            begin
 | |
|              hp2:=Find(hp.Str);
 | |
|              { Check if already in path, then we don't add it }
 | |
|              if not assigned(hp2) then
 | |
|               Concat(hp.Str);
 | |
|              hp:=TCmdStrListItem(hp.next);
 | |
|            end;
 | |
|         end;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|    function TSearchPathList.FindFile(const f :TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|      Var
 | |
|        p : TCmdStrListItem;
 | |
|      begin
 | |
|        FindFile:=false;
 | |
|        p:=TCmdStrListItem(first);
 | |
|        while assigned(p) do
 | |
|         begin
 | |
|           result:=FileExistsNonCase(p.Str,f,allowcache,FoundFile);
 | |
|           if result then
 | |
|             exit;
 | |
|           p:=TCmdStrListItem(p.next);
 | |
|         end;
 | |
|        { Return original filename if not found }
 | |
|        FoundFile:=f;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|    function FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|      Var
 | |
|        StartPos, EndPos, L: LongInt;
 | |
|      begin
 | |
|        Result:=False;
 | |
|        StartPos := 1;
 | |
|        L := Length(Path);
 | |
|        repeat
 | |
|          EndPos := StartPos;
 | |
|          while (EndPos <= L) and ((Path[EndPos] <> PathSeparator) and (Path[EndPos] <> ';')) do
 | |
|            Inc(EndPos);
 | |
|          Result := FileExistsNonCase(FixPath(Copy(Path, StartPos, EndPos-StartPos), False), f, allowcache, FoundFile);
 | |
|          if Result then
 | |
|            Exit;
 | |
|          StartPos := EndPos + 1;
 | |
|        until StartPos > L;
 | |
|        FoundFile:=f;
 | |
|      end;
 | |
| 
 | |
| {
 | |
|    function FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|       Var
 | |
|         singlepathstring : TCmdStr;
 | |
|         startpc,pc : pchar;
 | |
|      begin
 | |
|        FindFilePchar:=false;
 | |
|        if Assigned (Path) then
 | |
|         begin
 | |
|           pc:=path;
 | |
|           repeat
 | |
|              startpc:=pc;
 | |
|              while (pc^<>PathSeparator) and (pc^<>';') and (pc^<>#0) do
 | |
|               inc(pc);
 | |
|              SetLength(singlepathstring, pc-startpc);
 | |
|              move(startpc^,singlepathstring[1],pc-startpc);
 | |
|              singlepathstring:=FixPath(ExpandFileName(singlepathstring),false);
 | |
|              result:=FileExistsNonCase(singlepathstring,f,allowcache,FoundFile);
 | |
|              if result then
 | |
|                exit;
 | |
|              if (pc^=#0) then
 | |
|                break;
 | |
|              inc(pc);
 | |
|           until false;
 | |
|         end;
 | |
|        foundfile:=f;
 | |
|      end;
 | |
| }
 | |
| 
 | |
|    function  FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
 | |
|      var
 | |
|        Path : TCmdStr;
 | |
|        found : boolean;
 | |
|      begin
 | |
|        found:=FindFile(FixFileName(ChangeFileExt(bin,source_info.exeext)),'.;'+exepath,allowcache,foundfile);
 | |
|        if not found then
 | |
|         begin
 | |
| {$ifdef macos}
 | |
|           Path:=GetEnvironmentVariable('Commands');
 | |
| {$else}
 | |
|           Path:=GetEnvironmentVariable('PATH');
 | |
| {$endif}
 | |
|           found:=FindFile(FixFileName(ChangeFileExt(bin,source_info.exeext)),Path,allowcache,foundfile);
 | |
|         end;
 | |
|        FindExe:=found;
 | |
|      end;
 | |
| 
 | |
| 
 | |
|     function GetShortName(const n:TCmdStr):TCmdStr;
 | |
| {$ifdef win32}
 | |
|       var
 | |
|         hs,hs2 : TCmdStr;
 | |
|         i : longint;
 | |
| {$endif}
 | |
| {$if defined(go32v2) or defined(watcom)}
 | |
|       var
 | |
|         hs : shortstring;
 | |
| {$endif}
 | |
|       begin
 | |
|         GetShortName:=n;
 | |
| {$ifdef win32}
 | |
|         hs:=n+#0;
 | |
|         { may become longer in case of e.g. ".a" -> "a~1" or so }
 | |
|         setlength(hs2,length(hs)*2);
 | |
|         i:=Windows.GetShortPathName(@hs[1],@hs2[1],length(hs)*2);
 | |
|         if (i>0) and (i<=length(hs)*2) then
 | |
|           begin
 | |
|             setlength(hs2,strlen(@hs2[1]));
 | |
|             GetShortName:=hs2;
 | |
|           end;
 | |
| {$endif}
 | |
| {$if defined(go32v2) or defined(watcom)}
 | |
|         hs:=n;
 | |
|         if Dos.GetShortName(hs) then
 | |
|          GetShortName:=hs;
 | |
| {$endif}
 | |
|       end;
 | |
| 
 | |
| 
 | |
| {****************************************************************************
 | |
|                            Init / Done
 | |
| ****************************************************************************}
 | |
| 
 | |
|     procedure InitFileUtils;
 | |
|       begin
 | |
|         DirCache:=TDirectoryCache.Create;
 | |
|       end;
 | |
| 
 | |
| 
 | |
|     procedure DoneFileUtils;
 | |
|       begin
 | |
|         DirCache.Free;
 | |
|       end;
 | |
| 
 | |
| end.
 | 
