mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-14 04:39:23 +00:00
264 lines
7.3 KiB
ObjectPascal
264 lines
7.3 KiB
ObjectPascal
(*
|
|
Name: utildir
|
|
Copyright: Copyright (C) SIL International.
|
|
Documentation:
|
|
Description:
|
|
Create Date: 20 Jun 2006
|
|
|
|
Modified Date: 3 Feb 2015
|
|
Authors: mcdurdin
|
|
Related Files:
|
|
Dependencies:
|
|
|
|
Bugs:
|
|
Todo:
|
|
Notes:
|
|
History: 20 Jun 2006 - mcdurdin - Initial version
|
|
01 Aug 2006 - mcdurdin - Refactor util functions into multiple units
|
|
06 Oct 2006 - mcdurdin - Fix KGetTempFileName not returning correct extension
|
|
20 Jul 2008 - mcdurdin - Raise excpetion if GetTempFileName fails to avoid possible deletion of wrong files
|
|
16 Jan 2009 - mcdurdin - Widestring directory functions
|
|
16 Jan 2009 - mcdurdin - Fix crash when two copies of Keyman start simultaneously and try and grab the same temp folder name
|
|
04 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support
|
|
01 May 2014 - mcdurdin - I4181 - V9.0 - Stop using DeleteFileAlways, MOVEFILE_DELAY_UNTIL_REBOOT
|
|
03 Feb 2015 - mcdurdin - I4574 - V9.0 - If any files are read-only, they need the read-only flag removed on install
|
|
*)
|
|
unit utildir; // I3306
|
|
|
|
interface
|
|
|
|
function DirectoryEmpty(dir: WideString): Boolean;
|
|
function DirectoryExists(const Name: string): Boolean;
|
|
function ForceDirectories(Dir: string): Boolean;
|
|
procedure UnforceDirectories(Dir: string);
|
|
function EmptyDirectory(Dir: WideString): Boolean;
|
|
function RecursiveDelete(Dir: WideString): Boolean; // I4181
|
|
|
|
function CopyFileCleanAttr(const src, dst: string; failIfExists: Boolean): Boolean; // I4574
|
|
function DeleteFileCleanAttr(const filename: string): Boolean; // I4574
|
|
|
|
function CreateTempPath: string;
|
|
procedure DeleteTempPath(const Path: string);
|
|
|
|
function KGetTempFileName(const ext: string = ''): string; // deprecated 'has race condition, use TTempFileManager';--> only used by debug functionality now // I4181
|
|
function KGetTempPath: string;
|
|
|
|
function GetLongFileName(const fname: string): string;
|
|
|
|
implementation
|
|
|
|
uses
|
|
System.SysUtils,
|
|
Winapi.Windows;
|
|
|
|
|
|
|
|
function DirectoryEmpty(dir: WideString): Boolean;
|
|
var
|
|
f: TSearchRec;
|
|
n: Integer;
|
|
begin
|
|
Result := False;
|
|
if dir = '' then Exit;
|
|
if dir[Length(dir)] <> '\' then dir := dir + '\';
|
|
n := FindFirst(dir+'*.*', faDirectory, f);
|
|
try
|
|
while n = 0 do
|
|
begin
|
|
if (f.Name <> '.') and (f.Name <> '..') then Exit;
|
|
n := FindNext(f);
|
|
end;
|
|
finally
|
|
System.SysUtils.FindClose(f);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
function EmptyDirectory(Dir: WideString): Boolean;
|
|
var
|
|
f: TSearchRec;
|
|
n: Integer;
|
|
begin
|
|
Result := True;
|
|
if dir = '' then Exit;
|
|
if dir[Length(dir)] <> '\' then dir := dir + '\';
|
|
n := FindFirst(dir+'*.*', faDirectory, f);
|
|
try
|
|
while n = 0 do
|
|
begin
|
|
if (f.Name <> '.') and (f.Name <> '..') then
|
|
begin
|
|
if (f.Attr and faDirectory) = faDirectory
|
|
then Result := Result and EmptyDirectory(Dir + f.Name)
|
|
else Result := Result and DeleteFileCleanAttr(Dir + f.Name); // I4574
|
|
end;
|
|
n := FindNext(f);
|
|
end;
|
|
finally
|
|
System.SysUtils.FindClose(f);
|
|
end;
|
|
end;
|
|
|
|
|
|
function DirectoryExists(const Name: string): Boolean;
|
|
var
|
|
Code: Dword;
|
|
begin
|
|
Code := GetFileAttributes(PChar(Name));
|
|
Result := (Code <> $FFFFFFFF) and (FILE_ATTRIBUTE_DIRECTORY and Code <> 0);
|
|
end;
|
|
|
|
function ForceDirectories(Dir: string): Boolean;
|
|
begin
|
|
Result := False;
|
|
if Length(Dir) = 0 then
|
|
raise Exception.Create('Cannot create directory ' + Dir);
|
|
if (AnsiLastChar(Dir) <> nil) and (AnsiLastChar(Dir)^ = '\') then
|
|
Delete(Dir, Length(Dir), 1);
|
|
if (Length(Dir) < 3) or DirectoryExists(Dir)
|
|
or (ExtractFilePath(Dir) = Dir) then
|
|
begin
|
|
Result := True;
|
|
Exit; // avoid 'xyz:\' problem.
|
|
end;
|
|
if not ForceDirectories(ExtractFilePath(Dir)) then Exit;
|
|
Result := CreateDir(Dir);
|
|
end;
|
|
|
|
procedure UnforceDirectories(Dir: string);
|
|
{var
|
|
f: TSearchRec;
|
|
n: Integer;
|
|
Found: Boolean;}
|
|
begin
|
|
if (AnsiLastChar(Dir) <> nil) and (AnsiLastChar(Dir)^ = '\') then
|
|
Delete(Dir, Length(Dir), 1);
|
|
{n := FindFirst(Dir + '\*.*', faDirectory, f);
|
|
try
|
|
while n = 0 do
|
|
begin
|
|
if (f.Name <> '.') and (f.Name <> '..') then Exit;
|
|
n := FindNext(f);
|
|
end;
|
|
finally
|
|
FindClose(f);
|
|
end;}
|
|
if RemoveDirectory(PChar(Dir)) then UnforceDirectories(ExtractFilePath(Dir));
|
|
end;
|
|
|
|
function RecursiveDelete(Dir: WideString): Boolean; // I4181
|
|
var
|
|
f: TSearchRec;
|
|
n: Integer;
|
|
begin
|
|
if (Dir <> '') and (Dir[Length(Dir)] = '\') then
|
|
Delete(Dir, Length(Dir), 1);
|
|
|
|
n := FindFirst(Dir + '\*.*', faDirectory, f);
|
|
if n = 0 then
|
|
try
|
|
while n = 0 do
|
|
begin
|
|
if (f.Name <> '.') and (f.Name <> '..') then
|
|
begin
|
|
if (f.Attr and faDirectory) = faDirectory then RecursiveDelete(Dir + '\' + f.Name)
|
|
else DeleteFileCleanAttr(Dir + '\' + f.Name); // I4574
|
|
end;
|
|
n := FindNext(f);
|
|
end;
|
|
finally
|
|
System.SysUtils.FindClose(f);
|
|
end;
|
|
|
|
Result := RemoveDir(Dir); // I4181
|
|
end;
|
|
|
|
function KGetTempPath: string;
|
|
var
|
|
buf: array[0..260] of char;
|
|
begin
|
|
GetTempPath(260, buf);
|
|
Result := IncludeTrailingPathDelimiter(buf);
|
|
end;
|
|
|
|
function KGetTempFileName(const ext: string = ''): string;
|
|
var
|
|
buf: array[0..260] of char;
|
|
begin
|
|
// This has a race condition -- with the file extension change...
|
|
Result := ExcludeTrailingPathDelimiter(KGetTempPath);
|
|
|
|
if GetTempFileName(PChar(Result), 'kmn', 0, buf) = 0 then
|
|
RaiseLastOSError;
|
|
|
|
if (ext <> '') then
|
|
begin
|
|
if FileExists(buf) then DeleteFileCleanAttr(buf); // I4574
|
|
Result := ChangeFileExt(buf, ext);
|
|
end
|
|
else
|
|
Result := buf;
|
|
end;
|
|
|
|
function CreateTempPath: string;
|
|
var
|
|
buf: array[0..260] of char;
|
|
tempfile: string;
|
|
begin
|
|
tempfile := '';
|
|
|
|
repeat
|
|
Result := ExcludeTrailingPathDelimiter(KGetTempPath);
|
|
if (tempfile <> '') and FileExists(tempfile) then DeleteFileCleanAttr(tempfile); // I4574
|
|
|
|
if GetTempFileName(PChar(Result), 'kmn', 0, buf) = 0 then RaiseLastOSError;
|
|
tempfile := buf;
|
|
|
|
Result := ChangeFileExt(tempfile, '.dir');
|
|
|
|
if DirectoryExists(Result) then Continue;
|
|
|
|
if not CreateDir(Result) then
|
|
raise Exception.Create('Unable to create temporary folder: '+SysErrorMessage(GetLastError));
|
|
until True;
|
|
|
|
if FileExists(tempfile) then DeleteFileCleanAttr(tempfile); // I4574
|
|
end;
|
|
|
|
procedure DeleteTempPath(const Path: string);
|
|
begin
|
|
RecursiveDelete(Path);
|
|
end;
|
|
|
|
function GetLongFileName(const fname: string): string;
|
|
var
|
|
buf: array[0..260] of char;
|
|
p: PChar;
|
|
begin
|
|
buf[0] := #0;
|
|
if GetFullPathName(PChar(fname), 260, buf, p) = 0
|
|
then Result := fname
|
|
else Result := buf;
|
|
end;
|
|
|
|
function CopyFileCleanAttr(const src, dst: string; failIfExists: Boolean): Boolean; // I4574
|
|
begin
|
|
// Remove read-only on destination file before overwrite
|
|
if FileExists(dst) then
|
|
SetFileAttributes(PChar(dst), FILE_ATTRIBUTE_NORMAL);
|
|
|
|
Result := CopyFile(PChar(src), PChar(dst), failIfExists);
|
|
if Result then
|
|
// Remove read-only on destination file after copy
|
|
SetFileAttributes(PChar(dst), FILE_ATTRIBUTE_NORMAL);
|
|
end;
|
|
|
|
function DeleteFileCleanAttr(const filename: string): Boolean; // I4574
|
|
begin
|
|
if FileExists(filename) then
|
|
SetFileAttributes(PChar(filename), FILE_ATTRIBUTE_NORMAL);
|
|
Result := System.SysUtils.DeleteFile(filename);
|
|
end;
|
|
|
|
end.
|
|
|