Componentes.Terceros.jvcl/official/3.32/install/release/want/WildPaths.pas

1120 lines
30 KiB
ObjectPascal

(****************************************************************************
* WANT - A build management tool. *
* Copyright (c) 2001-2003 Juancarlo Anez, Caracas, Venezuela. *
* All rights reserved. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library 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 *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
****************************************************************************)
{
@brief
@author Juanco Añez
}
unit WildPaths;
{:
@BUG: SplitPath is not consisten with PathConcat. The output of the first
can not be put back together with the second. This is a problem for
UNC paths.
@TODO: File-by-file matching currently relies too much on the OS, so it may
not work consistently in Windows and Linux.
To make it consistent:
* Allways retreive lists of files with an '*' pattern
* Filter the resulting list using IsMatch and our own pattern.
Besides consistency, our own matching would allow us to use extended,
Unix-like file matching ([-] ranges, etc.) on Windows.
Do not make these changes until specific user stories prove them
necessary.
@TODO: Factor out stuff that JCL already handles well.
}
interface
uses
Windows,
SysUtils,
Math,
Classes,
JclFileUtils,
WantUtils;
const
WildChars = '?*';
InvalidPathChars = string(';' {$IFDEF LINUX} + ':' {$ENDIF});
{$IFDEF LINUX}
SystemPathDelimiter: string = '/';
{$ELSE}
SystemPathDelimiter: string = '\';
{$ENDIF}
type
TPath = type string;
TPattern = TPath;
TPaths = array of TPath;
TPatterns = TPaths;
TSystemPath = string;
TSystemPaths = array of TSystemPath;
EPathException = class(Exception);
EFileOpException = class(EPathException);
TFileAttribute = (
ReadOnly, {= $00000001 }
Hidden, {= $00000002 }
SysFile, {= $00000004 }
VolumeID, {= $00000008 }
Directory, {= $00000010 }
Archive, {= $00000020 }
NoFile {= -1}
);
TFileAttributes = set of TFileAttribute;
const
AnyFileAttribute = [];
function IsLocalPath(const Path: TPath): boolean;
procedure AssertIsLocalPath(const Path: TPath);
function IsWindowsPath(const Path: TPath):boolean;
function PathDrive(const Path: TPath): string;
function PathServer(Path: TPath): string;
function PathFile(const Path: TPath): TPath;
function PathDir(const Path: TPath): TPath;
function RemovePathDrive(Path: TPath):TPath;
function RemovePathServer(Path: TPath):TPath;
function PathConcat(Path1, Path2: TPath): TPath;
function ToSystemPath(const Path: TPath; const BasePath: TPath = ''):TSystemPath;
function ToPath(SystemPath: TSystemPath; const BasePath: TPath = ''):TPath;
function ToSystemPaths(const Paths: TPaths; const BasePath: TPath = ''): TSystemPaths; overload;
function ToPaths(OSPaths: TSystemPaths; const BasePath: TPath = ''): TPaths;
procedure ToSystemPaths(Paths: TStrings; const BasePath: TPath = ''); overload;
function StringsToPaths(S: TStrings):TPaths;
function SplitPath(Path: TPath): TPaths;
function JoinPaths(Paths: TPaths): TPath;
function MovePath(Path, FromBase: TPath; ToBase: TPath = ''): TPath;
function MovePaths(const Paths: TPaths; const FromBase: TPath; const ToBase: TPath = ''): TPaths;
function ToRelativePath(Path, BasePath: TPath):TPath;
function ToRelativePaths(const Paths: TPaths; const BasePath: TPath):TPaths; overload;
function ToRelativePaths(Paths: TStrings; const BasePath: TPath):TPaths; overload;
function NormalizePath(Path :TPath) :TPath;
function PathIsAbsolute(Path: TPath): boolean;
procedure ForceRelativePath(var Path, BasePath: TPath);
function FindPaths(Path: TPath; BasePath: TPath = '';
IncludeAttr: TFileAttributes = AnyFileAttribute;
ExcludeAttr: TFileAttributes = []
): TPaths;
function FindFiles(const Path: TPath; const BasePath: TPath = ''): TPaths;
function FindDirs(const Path: TPath; const BasePath: TPath = ''): TPaths;
function Wild( Pattern: TPath; BasePath: TPath = '';
IncludeAttr: TFileAttributes = AnyFileAttribute;
ExcludeAttr: TFileAttributes = []
):TPaths; overload;
procedure Wild(Files: TStrings; Pattern: TPath; BasePath: TPath = '';
IncludeAttr: TFileAttributes = AnyFileAttribute;
ExcludeAttr: TFileAttributes = []
); overload;
function IsMatch(Pattern, Path: TPath):boolean; overload;
function IsMatch(const Patterns: TPatterns; const Paths: TPaths; s: Integer = 0; p: Integer = 0):boolean; overload;
function PathExists(Path: TPath):boolean;
function PathIsDir(Path: TPath):boolean;
function PathIsFile(Path: TPath):boolean;
function SuperPath(Path: TPath): TPath;
// file operations
procedure MakeDir(const Path: TPath);
function ChangeDir(const Path: TPath; Verify :boolean = true) :boolean;
function CurrentDir: TPath;
function CurrentDrive :TPath;
procedure CopyFile(const Src, Dst: TPath);
procedure CopyFiles(const Sources, Dests: TPaths); overload;
procedure CopyFiles(const Files: TPaths; FromPath, ToPath: TPath); overload;
procedure CopyFiles(const Pattern: TPattern; const FromPath, ToPath: TPath); overload;
procedure MoveFile(const Src, Dst: TPath);
procedure MoveFiles(const Sources, Dests: TPaths); overload;
procedure MoveFiles(const Files: TPaths; const FromPath, ToPath: TPath); overload;
procedure MoveFiles(const Pattern: TPattern; const FromPath, ToPath: TPath); overload;
procedure DeleteFile(const Path: TPath; DeleteReadOnly: boolean = false);
procedure DeleteFiles(const Files: TPaths; DeleteReadOnly: boolean = false); overload;
procedure DeleteFiles(const Pattern: TPath; const BasePath: TPath= ''; DeleteReadOnly: boolean = false); overload;
procedure TouchFile(const Path: TPath; When: TDateTime = 0); overload;
procedure TouchFile(const Path: TPath; When: string); overload;
function FileAttributes(const Path: TPath):TFileAttributes;
procedure SetFileAttributes(const Path: TPath; const Attr: TFileAttributes);
function FileTime(const Path: TPath): TDateTime;
function SystemFileAttributes(const Path: TPath): Integer;
function SystemFileTime(const Path: TPath): Longint;
function TimeToSystemFileTime(const Time: TDateTime):Integer;
function FileAttributesToSystemAttributes(const Attr: TFileAttributes):Byte;
function SystemAttributesToFileAttributes(Attr: Integer) :TFileAttributes;
function ChangeExtension( Path: TPath; extension : String ): TPath;
implementation
procedure Wild( Files: TStrings; const Patterns: TPatterns; BasePath: TPath = '';
Index: Integer = 0;
IncludeAttr: TFileAttributes = AnyFileAttribute;
ExcludeAttr: TFileAttributes = []
);
overload; forward;
function IsLocalPath(const Path: TPath): boolean;
begin
Result := ( Pos(SystemPathDelimiter, Path) = 0 );
end;
function IsWindowsPath(const Path: TPath):boolean;
begin
Result := Pos(':', Path) <> 0;
end;
function CurrentDrive :TPath;
begin
Result := ToPath(ExtractFileDrive(GetCurrentDir));
end;
function PathDrive(const Path: TPath): string;
var
p: Integer;
begin
if not IsWindowsPath(Path) then
Result := ''
else
begin
p := Pos(':', Path);
Result := StrLeft(Path, p);
end;
end;
function PathServer(Path: TPath): string;
var
P: string;
begin
Path := ToPath(Path);
if StrLeft(Path, 2) <> '//' then
Result := PathDrive(Path)
else
begin
P := Copy(Path, 3, Length(Path));
Result := '//'+ StrToken(P, '/');
end;
end;
function PathFile(const Path: TPath): TPath;
var
splits :TPaths;
begin
Result := '';
splits := SplitPath(Path);
if Length(splits) > 0 then
Result := splits[High(splits)];
end;
function PathDir(const Path: TPath): TPath;
var
splits :TPaths;
begin
Result := '';
splits := SplitPath(Path);
if Length(splits) > 0 then
begin
SetLength(splits, Length(splits)-1);
Result := JoinPaths(splits);
end;
end;
function RemovePathDrive(Path: TPath):TPath;
var
p: Integer;
begin
Result := Path;
p := Pos(':', Result);
Delete(Result, 1, p);
end;
function RemovePathServer(Path: TPath):TPath;
var
Server: string;
begin
Result := Path;
Server := PathServer(Path);
if Server <> '' then
Delete(Result, 1, 3 + Length(Server));
end;
procedure AssertIsLocalPath(const Path: TPath);
begin
if Pos(SystemPathDelimiter, Path) <> 0 then
raise EPathException.Create( Format( '"%s" looks like a system path. Expected a system independent one.',
[Path])
);
end;
function PathConcat(Path1, Path2: TPath): TPath;
var
Parts: TPaths;
i : Integer;
P1 : TPath;
P2 : TPath;
begin
Path1 := ToPath(Path1);
Path2 := ToPath(Path2);
Parts := nil;
if (Length(Path1) = 0)
//or (P1 = '.')
or PathIsAbsolute(Path2) then
Result := Path2
else if Length(Path2) = 0 then
Result := Path1
else begin
P1 := Path1;
P2 := Path2;
if P1[Length(P1)] = '/' then
Delete(P1, Length(P1), 1);
Result := P1;
Parts := SplitPath(P2);
for i := Low(Parts) to High(Parts) do
begin
if Parts[i] = '..' then
Result := SuperPath(Result)
else if Parts[i] = '.' then
// do nothing
else
Result := Result + '/' + Parts[i];
end;
end;
Assert(Pos('//', Result) <= 1);
end;
procedure CheckPath(Path :TPath);
var
i :Integer;
begin
for i := 1 to Length(InvalidPathChars) do
begin
if Pos(InvalidPathChars[i], Path) <> 0 then
raise EPathException.Create('invalid path chars in ' + Path);
end;
end;
function ToPath(SystemPath: TSystemPath; const BasePath: TPath): TPath;
begin
CheckPath(SystemPath);
CheckPath(BasePath);
Result := SystemPath;
if (Length(Result) >= 2)
and (Result[2] = ':')
and (Result[1] in ['a'..'z', 'A'..'Z'])
then
begin
Result[1] := LowerCase(''+Result[1])[1];
Result := SystemPathDelimiter + Result;
end;
Result := StringReplace(Result, SystemPathDelimiter, '/', [rfReplaceAll]);
if (BasePath <> '') then
begin
if (Length(Result) = 0) then
Result := ToPath(BasePath)
else if Result[1] <> '/' then
begin
Result := ToPath(BasePath) + '/' + Result;
end;
end;
end;
function ToSystemPath(const Path: TPath; const BasePath: TPath): string;
begin
CheckPath(Path);
CheckPath(BasePath);
Result := MovePath(Path, '', BasePath);
if (Length(Result) >= 3) and (Result[3] = ':') and (Result[1] = '/') then
Delete(Result,1, 1);
if (Length(Result) >= 1) and (Result[Length(Result)] = '/') then
Delete(Result,Length(Result), 1);
Result := StringReplace(Result, '/', SystemPathDelimiter, [rfReplaceAll]);
if PathIsAbsolute(Path) then
Result := ExpandFileName(Result);
end;
function ToSystemPaths(const Paths: TPaths; const BasePath: TPath = ''): TSystemPaths;
var
i: Integer;
begin
SetLength(Result, Length(Paths));
for i := 0 to High(Result) do
Result[i] := ToSystemPath(Paths[i], BasePath);
end;
procedure ToSystemPaths(Paths: TStrings; const BasePath: TPath = '');
var
i: Integer;
begin
for i := 0 to Paths.Count-1 do
Paths[i] := ToSystemPath(Paths[i], BasePath);
end;
function ToPaths(OSPaths: TSystemPaths; const BasePath: TPath = ''): TPaths;
var
i: Integer;
begin
SetLength(Result, Length(OSPaths));
for i := 0 to High(Result) do
Result[i] := ToPath(OSPaths[i], BasePath);
end;
function FindPaths( Path: TPath; BasePath: TPath;
IncludeAttr: TFileAttributes;
ExcludeAttr: TFileAttributes
):TPaths;
var
S: TStringList;
Search: TSearchRec;
SearchResult: Integer;
Pattern :TPath;
Dir :TPath;
Lookup :TPath;
begin
Path := ToPath(Path);
Dir := PathDir(Path);
Pattern := PathFile(Path);
if (Pos('*', Pattern) > 0) or (Pos('?', Pattern) > 0)then
Lookup := ToSystemPath(PathConcat(Dir, '*'), BasePath)
else
Lookup := ToSystemPath(Path, BasePath);
S := TStringList.Create;
S.Sorted := True;
try
SearchResult := FindFirst(Lookup, faAnyFile, Search);
try
while SearchResult = 0 do
begin
if (Search.Name <> '.' )
and (Search.Name <> '..' )
and ((IncludeAttr = AnyFileAttribute) or ((SystemAttributesToFileAttributes(Search.Attr) * IncludeAttr) <> []))
and ((SystemAttributesToFileAttributes(Search.Attr) * ExcludeAttr) = [])
and IsMatch(Pattern, Search.Name)
then
S.Add(ToPath(Search.Name, BasePath));
SearchResult := FindNext(Search);
end;
finally
FindClose(Search);
end;
Result := StringsToPaths(S);
finally
FreeAndNil(S);
end;
end;
function FindDirs(const Path: TPath; const BasePath: TPath): TPaths;
begin
Result := FindPaths(Path, BasePath, [Directory]);
end;
function FindFiles(const Path: TPath; const BasePath: TPath): TPaths;
begin
Result := FindPaths(Path, BasePath, AnyFileAttribute, [Directory]);
end;
function SplitPath(Path: TPath): TPaths;
var
S : TStrings;
n,
i : Integer;
Base: TPath;
begin
Path := ToPath(Path);
S := TStringList.Create;
try
n := 0;
if PathIsAbsolute(Path) then
begin
ForceRelativePath(Path, Base);
SetLength(Result, 1);
Result[0] := Base;
n := 1;
end;
StrToStrings(Path, '/', S);
SetLength(Result, n+S.Count);
for i := 0 to S.Count-1 do
Result[i+n] := S[i];
finally
FreeAndNil(S);
end;
end;
function JoinPaths(Paths: TPaths): TPath;
var
i :Integer;
begin
if Length(Paths) = 0 then
Result := ''
else
begin
Result := Paths[0];
for i := 1 to High(Paths) do
Result := PathConcat(Result, Paths[i]);
end;
end;
function StringsToPaths(S: TStrings): TPaths;
var
i: Integer;
begin
SetLength(Result, S.Count);
for i := 0 to S.Count-1 do
Result[i] := S[i];
end;
function MovePath(Path, FromBase, ToBase: TPath): TPath;
begin
Path := ToPath(Path);
FromBase := ToPath(FromBase);
ToBase := ToPath(ToBase);
if (FromBase <> '') and (Pos(FromBase+'/', Path) = 1) then
Result := PathConcat(ToBase, Copy(Path, 2+Length(FromBase), Length(Path)))
else if PathIsAbsolute(Path) then
Result := Path
else
Result := PathConcat(ToBase, Path);
end;
function MovePaths(const Paths: TPaths; const FromBase: TPath; const ToBase: TPath): TPaths;
var
i: Integer;
begin
SetLength(Result, Length(Paths));
for i := Low(Paths) to High(Paths) do
Result[i] := MovePath(Paths[i], FromBase, ToBase);
end;
function NormalizePath(Path :TPath) :TPath;
begin
Result := ToPath(Path);
if not PathIsAbsolute(Path) then
Result := PathConcat(CurrentDir, Result);
if PathDrive(Result) = '' then
Result := CurrentDrive + Result;
end;
function ToRelativePath(Path, BasePath: TPath):TPath;
var
P, B : TPaths;
i, j: Integer;
begin
P := nil;
B := nil;
Path := PathConcat('.', ToPath(Path));
if not PathIsAbsolute(Path)
and PathIsAbsolute(BasePath) then
Result := Path
else
begin
BasePath := ToPath(BasePath);
if (PathDrive(Path) <> PathDrive(BasePath)) then
begin
Path := NormalizePath(Path);
BasePath := NormalizePath(BasePath);
end;
if (PathDrive(Path) <> PathDrive(BasePath)) then
Result := Path
else
begin
Result := '';
P := SplitPath(Path);
B := SplitPath(BasePath);
i := 0;
j := 0;
while (i <= High(P))
and (j <= High(B))
and (P[i] = B[j])
do begin
Inc(i);
Inc(j);
end;
if j > High(B) then
Result := '.'
else
begin
while j <= High(B) do
begin
if Result = '' then
Result := '..'
else
Result := Result + '/..';
Inc(j);
end;
end;
while i <= High(P) do
begin
Result := PathConcat(Result, P[i]);
Inc(i);
end;
end;
if Result = '' then
Result := '.';
end;
end;
function ToRelativePaths(const Paths: TPaths; const BasePath: TPath):TPaths;
begin
Result := MovePaths(Paths, BasePath, '');
end;
function ToRelativePaths(Paths: TStrings; const BasePath: TPath):TPaths;
var
i :Integer;
begin
for i := 0 to Paths.Count-1 do
Paths[i] := ToRelativePath(Paths[i], BasePath);
end;
function PathIsAbsolute(Path: TPath): boolean;
begin
Path := ToPath(Path);
Result := (Length(Path) > 0) and (Path[1] = '/')
or (Length(Path) >= 3) and (Path[2] = ':') and (Path[3] = '/');
end;
procedure ForceRelativePath(var Path, BasePath: TPath);
var
p: Integer;
begin
Path := ToPath(Path);
BasePath := ToPath(BasePath);
if PathIsAbsolute(Path) then
begin
BasePath := '';
p := Pos('/', Path);
BasePath := BasePath + Copy(Path, 1, p);
Delete(Path, 1, p);
if PathIsAbsolute(Path) then
begin // must be UNC, URI, or C:/ style
p := 1+Pos('/', Copy(Path, 2, Length(Path)));
if p = 1 then
begin
BasePath := BasePath + Path;
Path := '';
end
else
begin
BasePath := BasePath + Copy(Path, 1, p-1);
Delete(Path, 1, p);
end;
end;
end;
end;
function Wild( Pattern: TPath; BasePath: TPath;
IncludeAttr: TFileAttributes;
ExcludeAttr: TFileAttributes
):TPaths;
var
Files: TStringList;
begin
Pattern := ToPath(Pattern);
BasePath := ToPath(BasePath);
Files := TStringList.Create;
try
Files.Sorted := True;
Wild(Files, Pattern, BasePath, IncludeAttr, ExcludeAttr);
Result := StringsToPaths(Files);
finally
FreeAndNil(Files);
end;
end;
procedure Wild(Files: TStrings; Pattern: TPath; BasePath: TPath;
IncludeAttr: TFileAttributes;
ExcludeAttr: TFileAttributes
);
var
Pats: string;
Pat : TPath;
begin
Pattern := ToPath(Pattern);
BasePath := ToPath(BasePath);
Pats := Pattern;
Pat := StrToken(Pats, ',');
while Pat <> '' do
begin
//ForceRelativePath(Pattern, BasePath);
if PathIsAbsolute(Pattern) then
Wild(Files, SplitPath(Pat), '', 0, IncludeAttr, ExcludeAttr)
else
Wild(Files, SplitPath(Pat), BasePath, 0, IncludeAttr, ExcludeAttr);
Pat := StrToken(Pats, ',');
end;
end;
procedure Wild( Files: TStrings; const Patterns: TPatterns; BasePath: TPath;
Index: Integer;
IncludeAttr: TFileAttributes;
ExcludeAttr: TFileAttributes
);
var
i : Integer;
Matches: TPaths;
NewBase: TPath;
begin
BasePath := ToPath(BasePath);
Matches := nil;
if Index > High(Patterns) then
EXIT;
NewBase := BasePath;
// absorb all non-wildcard patterns
while (Index < High(Patterns))
and ((LastDelimiter(WildChars, Patterns[Index]) = 0)
or (Patterns[Index] = '.')
)
do
begin
NewBase := PathConcat(NewBase, Patterns[Index]);
Inc(Index);
end;
Assert(Index <= High(Patterns));
if Index = High(Patterns) then
begin // add files (works for '**' too)
Matches := FindPaths(Patterns[Index], NewBase, IncludeAttr, ExcludeAttr);
for i := Low(Matches) to High(Matches) do
Files.Add(PathConcat(NewBase, Matches[i]))
end;
// handle wildcards
if Patterns[Index] = '**' then
begin // match anything and recurse
Wild(Files, Patterns, NewBase, Index+1, IncludeAttr, ExcludeAttr);
Matches := FindDirs('*', NewBase);
// use same Index ('**') to recurse
for i := Low(Matches) to High(Matches) do
Wild(Files, Patterns, PathConcat(NewBase, Matches[i]), Index, IncludeAttr, ExcludeAttr);
end
else if Index < High(Patterns) then
begin // match directories
Matches := FindDirs(Patterns[Index], NewBase);
for i := Low(Matches) to High(Matches) do
Wild(Files, Patterns, PathConcat(NewBase, Matches[i]), Index+1, IncludeAttr, ExcludeAttr);
end;
end;
function Matches(A, B: TPath; i: Integer = 1; j: Integer = 1):boolean;
begin
while (i <= Length(A))
and (j <= Length(B))
and (UpCase(A[i]) = UpCase(B[j])) do
begin
Inc(i);
Inc(j);
end;
if B = '.' then
Result := True
else if j > Length(B) then
Result := i > Length(A)
else if i > Length(A) then
Result := False
else if B[j] = '?' then
Result := Matches(A, B, i+1, j+1)
or Matches(A, B, i, j+1)
else if B[j] = '*' then
Result := Matches(A, B, i+1, j+1)
or Matches(A, B, i+1, j)
or Matches(A, B, i , j+1)
else
Result := False;
end;
function IsMatch(const Patterns: TPatterns; const Paths: TPaths; s: Integer = 0; p: Integer = 0):boolean;
begin
while (p <= High(Paths))
and (s <= High(Patterns))
and (Patterns[s] <> '**')
and Matches(Paths[p], Patterns[s]) do
begin
Inc(p);
Inc(s);
end;
if s > High(Patterns) then
Result := p > High(Paths)
else if Patterns[s] = '**' then
begin
if p > High(Paths) then
Result := s = High(Patterns)
else
Result := IsMatch(Patterns, Paths, s+1, p)
or IsMatch(Patterns, Paths, s+1, p+1)
or IsMatch(Patterns, Paths, s, p+1)
end
else
Result := False;
end;
function IsMatch(Pattern, Path : TPath):boolean;
begin
Pattern := ToPath(Pattern);
Path := ToPath(Path);
Result := IsMatch(SplitPath(Pattern), SplitPath(Path));
end;
function PathExists(Path: TPath):boolean;
begin
Path := ToPath(Path);
Result := Length(FindPaths(Path)) >= 1;
end;
function PathIsDir(Path: TPath):boolean;
begin
Path := ToPath(Path);
Result := (FileAttributes(Path) * [NoFile, Directory]) = [Directory];
end;
function PathIsFile(Path: TPath):boolean;
begin
Path := ToPath(Path);
Result := (FileAttributes(Path) * [NoFile, Directory]) = [];
end;
function SuperPath(Path: TPath): TPath;
var
p : Integer;
f : string;
begin
Path := ToPath(Path);
if (Path = '.') or (Path = '') then
Result := '..'
else if Path = '..' then
Result := '../..'
else
begin
Result := Path;
p := LastDelimiter('/', Result);
f := Copy(Result, p+1, Length(Result));
if (p = Length(Result)) or (f = '.') then
begin
Result := Copy(Result, 1, p-1);
p := LastDelimiter('/', Result);
f := Copy(Result, p+1, Length(Result))
end;
if f = '..' then
Result := Result + '/..'
else
Result := Copy(Result, 1, p-1);
end;
end;
// file operations
procedure MakeDir(const Path: TPath);
begin
if (Length(Path) > 0)
and (Path[Length(Path)] <> ':') // Oops! Windows specific!
and not PathIsDir(Path) then
begin
MakeDir(SuperPath(Path));
SysUtils.CreateDir(ToSystemPath(Path));
if not PathIsDir(Path) then
raise EFileOpException.Create(Format('Could not create directory "%s"', [Path]));
end;
end;
function ChangeDir(const Path: TPath; Verify :boolean) :boolean;
begin
Result := True;
if (Path <> '') and (Path <> CurrentDir) then
Result := SetCurrentDir(ToSystemPath(Path));
if not Result and Verify then
raise EFileOpException.CreateFmt('Could not change to directory "%s"',[Path]);
end;
function CurrentDir: TPath;
begin
Result := ToPath(SysUtils.GetCurrentDir);
end;
procedure CopyFile(const Src, Dst: TPath);
begin
MakeDir(SuperPath(Dst));
if PathIsDir(Src) then
MakeDir(Dst)
else if not Windows.CopyFile( PChar(ToSystemPath(Src)),
PChar(ToSystemPath(Dst)),
False)
then
raise EFileOpException.Create(SysErrorMessage(GetLastError));
end;
procedure CopyFiles(const Pattern: TPattern; const FromPath, ToPath: TPath);
begin
CopyFiles(Wild(Pattern, FromPath), FromPath, ToPath);
end;
procedure CopyFiles(const Files: TPaths; FromPath, ToPath: TPath);
begin
CopyFiles(Files, MovePaths(Files, FromPath, ToPath));
end;
procedure CopyFiles(const Sources, Dests: TPaths);
var
f : Integer;
begin
for f := Max(Low(Sources), Low(Dests)) to Min(High(Sources), High(Dests)) do
begin
CopyFile(Sources[f], Dests[f]);
end;
end;
procedure MoveFile(const Src, Dst: TPath);
begin
if PathIsDir(Src) then
begin
raise EFileOpException.Create(Format('Don''t know how to move dir "%s" to "%s"', [Src, Dst]));
end;
(* MoveFileEx not implemented on Win9x. Some help files say it is, but see
this link for current doc:
http://msdn.microsoft.com/library/psdk/winbase/filesio_9oe0.htm
In addition, MOVEFILE_REPLACE_EXISTING does not work with read only files
if not Windows.MoveFileEx(PChar(ToSystemPath(Src)), PChar(ToSystemPath(Dst)),
MOVEFILE_COPY_ALLOWED or
MOVEFILE_REPLACE_EXISTING or
MOVEFILE_WRITE_THROUGH) -- Chrismo *)
WildPaths.CopyFile(Src, Dst);
WildPaths.DeleteFile(Src);
end;
procedure MoveFiles(const Pattern: TPattern; const FromPath, ToPath: TPath);
begin
MoveFiles(Wild(Pattern, FromPath), FromPath, ToPath);
end;
procedure MoveFiles(const Files: TPaths; const FromPath, ToPath: TPath);
begin
MoveFiles(Files, MovePaths(Files, FromPath, ToPath));
end;
procedure MoveFiles(const Sources, Dests: TPaths);
var
f : Integer;
begin
for f := Max(Low(Sources), Low(Dests)) to Min(High(Sources), High(Dests)) do
begin
MoveFile(Sources[f], Dests[f]);
end;
end;
procedure DeleteFile(const Path: TPath; DeleteReadOnly: boolean = false);
var
SysPath: string;
FileAttr: Integer;
begin
SysPath := ToSystemPath(Path);
if not PathIsDir(Path) then
begin
if DeleteReadOnly then
begin
{ take off read only attribute if it exists }
FileAttr := SysUtils.FileGetAttr(SysPath);
FileAttr := FileAttr and (not $00000001); // faReadOnly. Avoid warning.
SysUtils.FileSetAttr(SysPath, FileAttr);
end;
SysUtils.DeleteFile(SysPath)
end
else
SysUtils.RemoveDir(SysPath);
end;
procedure DeleteFiles(const Pattern: TPath; const BasePath: TPath; DeleteReadOnly :boolean);
begin
DeleteFiles(Wild(Pattern, BasePath), DeleteReadOnly);
end;
procedure DeleteFiles(const Files: TPaths; DeleteReadOnly: boolean = false);
var
f: Integer;
begin
for f := Low(Files) to High(Files) do
if not PathIsDir(Files[f]) then
DeleteFile(Files[f], DeleteReadOnly);
for f := Low(Files) to High(Files) do
if PathIsDir(Files[f]) then
DeleteFile(Files[f], DeleteReadOnly);
end;
procedure TouchFile(const Path: TPath; When: string);
begin
//!!! StrToDateTime changes with locale and platform!!
TouchFile(Path, StrToDateTime(When));
end;
procedure TouchFile(const Path: TPath; When: TDateTime);
var
Handle: Integer;
begin
if When = 0 then
When := Now;
MakeDir(SuperPath(Path));
Handle := FileOpen(ToSystemPath(Path), fmOpenWrite or fmShareDenyNone);
if Handle < 0 then
Handle := FileCreate(ToSystemPath(Path));
try
FileSetDate(Handle, DateTimeToFileDate(When))
finally
FileClose(Handle);
end;
end;
function FileAttributes(const Path: TPath):TFileAttributes;
var
Attr :Integer;
begin
Attr := SystemFileAttributes(Path);
if Attr < 0 then
Result := [NoFile]
else
Result := TFileAttributes(Byte(Attr));
end;
procedure SetFileAttributes(const Path: TPath; const Attr: TFileAttributes);
begin
SysUtils.FileSetAttr(ToSystemPath(Path), FileAttributesToSystemAttributes(Attr));
end;
function FileTime(const Path: TPath): TDateTime;
var
SystemTime: Longint;
begin
SystemTime := SystemFileTime(Path);
if SystemTime <= 0 then
Result := 0
else
Result := FileDateToDateTime(SystemTime);
end;
function SystemFileAttributes(const Path: TPath): Integer;
begin
Result := Byte(SysUtils.FileGetAttr(ToSystemPath(Path)));
end;
function SystemFileTime(const Path: TPath) : Longint;
begin
Result := SysUtils.FileAge(ToSystemPath(Path));
if Result < 0 then
Result := 0;
end;
function TimeToSystemFileTime(const Time: TDateTime):Integer;
begin
Result := DateTimeToFileDate(Time);
end;
function FileAttributesToSystemAttributes(const Attr: TFileAttributes):Byte;
begin
Result := Byte(Attr);
end;
function SystemAttributesToFileAttributes(Attr: Integer) :TFileAttributes;
begin
Result := TFileAttributes(Byte(Attr));
end;
function ChangeExtension( Path: TPath; extension : String ): TPath;
var
p : integer ;
begin
p:=LastDelimiter('.',Path);
if (p < Length(Path)) then
begin
Result:=copy(Path,1,p)+extension;
end
else Result:=Path
end;
end.