1293 lines
37 KiB
ObjectPascal
1293 lines
37 KiB
ObjectPascal
{
|
|
Double Commander
|
|
-------------------------------------------------------------------------
|
|
This unit contains platform dependent functions dealing with operating system.
|
|
|
|
Copyright (C) 2006-2014 Koblov Alexander (Alexx2000@mail.ru)
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
}
|
|
|
|
unit DCOSUtils;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, DynLibs, DCClassesUtf8, DCBasicTypes;
|
|
|
|
type
|
|
TFileMapRec = record
|
|
FileHandle : System.THandle;
|
|
FileSize : Int64;
|
|
{$IFDEF MSWINDOWS}
|
|
MappingHandle : System.THandle;
|
|
{$ENDIF}
|
|
MappedFile : Pointer;
|
|
end;
|
|
|
|
TCopyAttributesOption = (caoCopyAttributes,
|
|
caoCopyTime,
|
|
caoCopyOwnership,
|
|
caoRemoveReadOnlyAttr);
|
|
TCopyAttributesOptions = set of TCopyAttributesOption;
|
|
|
|
const
|
|
faInvalidAttributes: TFileAttrs = TFileAttrs(-1);
|
|
CopyAttributesOptionCopyAll = [caoCopyAttributes, caoCopyTime, caoCopyOwnership];
|
|
|
|
{en
|
|
Is file a directory
|
|
@param(iAttr File attributes)
|
|
@returns(@true if file is a directory, @false otherwise)
|
|
}
|
|
function FPS_ISDIR(iAttr: TFileAttrs) : Boolean;
|
|
{en
|
|
Is file a symbolic link
|
|
@param(iAttr File attributes)
|
|
@returns(@true if file is a symbolic link, @false otherwise)
|
|
}
|
|
function FPS_ISLNK(iAttr: TFileAttrs) : Boolean;
|
|
{en
|
|
Is file executable
|
|
@param(sFileName File name)
|
|
@returns(@true if file is executable, @false otherwise)
|
|
}
|
|
function FileIsExeLib(const sFileName : String) : Boolean;
|
|
{en
|
|
Copies a file attributes (attributes, date/time, owner & group, permissions).
|
|
@param(sSrc String expression that specifies the name of the file to be copied)
|
|
@param(sDst String expression that specifies the target file name)
|
|
@param(bDropReadOnlyFlag Drop read only attribute if @true)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function FileIsReadOnly(iAttr: TFileAttrs): Boolean;
|
|
|
|
{en
|
|
Returns path to a temporary name. It ensures that returned path doesn't exist,
|
|
i.e., there is no filesystem entry by that name.
|
|
If it could not create a unique temporary name then it returns empty string.
|
|
|
|
@param(PathPrefix
|
|
This parameter is added at the beginning of each path that is tried.
|
|
The directories in this path are not created if they don't exist.
|
|
If it is empty then the system temporary directory is used.
|
|
For example:
|
|
If PathPrefix is '/tmp/myfile' then files '/tmp/myfileXXXXXX' are tried.
|
|
The path '/tmp' must already exist.)
|
|
}
|
|
function GetTempName(PathPrefix: String): String;
|
|
|
|
(* File mapping/unmapping routines *)
|
|
{en
|
|
Create memory map of a file
|
|
@param(sFileName Name of file to mapping)
|
|
@param(FileMapRec TFileMapRec structure)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function MapFile(const sFileName : UTF8String; out FileMapRec : TFileMapRec) : Boolean;
|
|
{en
|
|
Unmap previously mapped file
|
|
@param(FileMapRec TFileMapRec structure)
|
|
}
|
|
procedure UnMapFile(var FileMapRec : TFileMapRec);
|
|
|
|
{en
|
|
Convert from console to UTF8 encoding.
|
|
}
|
|
function ConsoleToUTF8(const Str: AnsiString): UTF8String;
|
|
|
|
{ File handling functions}
|
|
function mbFileOpen(const FileName: UTF8String; Mode: Word): System.THandle;
|
|
function mbFileCreate(const FileName: UTF8String): System.THandle; overload;
|
|
function mbFileCreate(const FileName: UTF8String; ShareMode: Longint): System.THandle; overload;
|
|
function mbFileCreate(const FileName: UTF8String; ShareMode: Longint; Rights: Longint): System.THandle; overload;
|
|
function mbFileAge(const FileName: UTF8String): DCBasicTypes.TFileTime;
|
|
function mbFileSame(const FirstName, SecondName: UTF8String): Boolean;
|
|
// On success returns True.
|
|
function mbFileGetTime(const FileName: UTF8String;
|
|
var ModificationTime: DCBasicTypes.TFileTime;
|
|
var CreationTime : DCBasicTypes.TFileTime;
|
|
var LastAccessTime : DCBasicTypes.TFileTime): Boolean;
|
|
// On success returns True.
|
|
function mbFileSetTime(const FileName: UTF8String;
|
|
ModificationTime: DCBasicTypes.TFileTime;
|
|
CreationTime : DCBasicTypes.TFileTime = 0;
|
|
LastAccessTime : DCBasicTypes.TFileTime = 0): Boolean;
|
|
{en
|
|
Checks if a given file exists - it can be a real file or a link to a file,
|
|
but it can be opened and read from.
|
|
Even if the result is @false, we can't be sure a file by that name can be created,
|
|
because there may still exist a directory or link by that name.
|
|
}
|
|
function mbFileExists(const FileName: UTF8String): Boolean;
|
|
function mbFileAccess(const FileName: UTF8String; Mode: Word): Boolean;
|
|
function mbFileGetAttr(const FileName: UTF8String): TFileAttrs; overload;
|
|
function mbFileSetAttr(const FileName: UTF8String; Attr: TFileAttrs) : LongInt;
|
|
function mbFileGetAttr(const FileName: UTF8String; out Attr: TSearchRec): Boolean; overload;
|
|
{en
|
|
If any operation in Options is performed and does not succeed it is included
|
|
in the result set. If all performed operations succeed the function returns empty set.
|
|
For example for Options=[caoCopyTime, caoCopyOwnership] setting ownership
|
|
doesn't succeed then the function returns [caoCopyOwnership].
|
|
}
|
|
function mbFileCopyAttr(const sSrc, sDst: UTF8String; Options: TCopyAttributesOptions): TCopyAttributesOptions;
|
|
// Returns True on success.
|
|
function mbFileSetReadOnly(const FileName: UTF8String; ReadOnly: Boolean): Boolean;
|
|
function mbDeleteFile(const FileName: UTF8String): Boolean;
|
|
|
|
function mbRenameFile(const OldName: UTF8String; NewName: UTF8String): Boolean;
|
|
function mbFileSize(const FileName: UTF8String): Int64;
|
|
function FileFlush(Handle: System.THandle): Boolean;
|
|
{ Directory handling functions}
|
|
function mbGetCurrentDir: UTF8String;
|
|
function mbSetCurrentDir(const NewDir: UTF8String): Boolean;
|
|
{en
|
|
Checks if a given directory exists - it may be a real directory or a link to directory.
|
|
Even if the result is @false, we can't be sure a directory by that name can be created,
|
|
because there may still exist a file or link by that name.
|
|
}
|
|
function mbDirectoryExists(const Directory : UTF8String) : Boolean;
|
|
function mbCreateDir(const NewDir: UTF8String): Boolean;
|
|
function mbRemoveDir(const Dir: UTF8String): Boolean;
|
|
{en
|
|
Checks if any file system entry exists at given path.
|
|
It can be file, directory, link, etc. (links are not followed).
|
|
}
|
|
function mbFileSystemEntryExists(const Path: UTF8String): Boolean;
|
|
function mbCompareFileNames(const FileName1, FileName2: UTF8String): Boolean;
|
|
function mbSameFile(const FileName1, FileName2: String): Boolean;
|
|
{ Other functions }
|
|
function mbGetEnvironmentString(Index : Integer) : UTF8String;
|
|
function mbSysErrorMessage(ErrorCode: Integer): UTF8String;
|
|
function mbLoadLibrary(const Name: UTF8String): TLibHandle;
|
|
function SafeGetProcAddress(Lib: TLibHandle; const ProcName: AnsiString): Pointer;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
Windows, JwaWinNetWk, DCDateTimeUtils,
|
|
{$ENDIF}
|
|
{$IF DEFINED(UNIX)}
|
|
{$IF DEFINED(BSD)}
|
|
{$DEFINE FPC_USE_LIBC}
|
|
{$ENDIF}
|
|
{$IF (NOT DEFINED(FPC_USE_LIBC)) OR (DEFINED(BSD) AND NOT DEFINED(DARWIN))}
|
|
SysCall,
|
|
{$ENDIF}
|
|
BaseUnix, Unix, dl,
|
|
{$ENDIF}
|
|
DCStrUtils, LazUTF8;
|
|
|
|
{$IFDEF UNIX}
|
|
function SetModeReadOnly(mode: TMode; ReadOnly: Boolean): TMode;
|
|
begin
|
|
mode := mode and not (S_IWUSR or S_IWGRP or S_IWOTH);
|
|
if ReadOnly = False then
|
|
begin
|
|
if (mode AND S_IRUSR) = S_IRUSR then
|
|
mode := mode or S_IWUSR;
|
|
if (mode AND S_IRGRP) = S_IRGRP then
|
|
mode := mode or S_IWGRP;
|
|
if (mode AND S_IROTH) = S_IROTH then
|
|
mode := mode or S_IWOTH;
|
|
end;
|
|
Result := mode;
|
|
end;
|
|
|
|
{$ENDIF}
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
const
|
|
AccessModes: array[0..2] of DWORD = (
|
|
GENERIC_READ,
|
|
GENERIC_WRITE,
|
|
GENERIC_READ or GENERIC_WRITE);
|
|
ShareModes: array[0..4] of DWORD = (
|
|
0,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_SHARE_WRITE,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
|
|
{$ELSEIF DEFINED(UNIX)}
|
|
const
|
|
AccessModes: array[0..2] of LongInt = (
|
|
O_RdOnly,
|
|
O_WrOnly,
|
|
O_RdWr);
|
|
|
|
function fpLChown(path : pChar; owner : TUid; group : TGid): cInt; {$IFDEF FPC_USE_LIBC}cdecl; external 'c' name 'lchown';{$ENDIF}
|
|
{$IFNDEF FPC_USE_LIBC}
|
|
begin
|
|
fpLChown:=do_syscall(syscall_nr_lchown,TSysParam(path),TSysParam(owner),TSysParam(group));
|
|
end;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
(*Is Directory*)
|
|
|
|
function FPS_ISDIR(iAttr: TFileAttrs) : Boolean; inline;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result := (iAttr and FILE_ATTRIBUTE_DIRECTORY <> 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result := BaseUnix.FPS_ISDIR(iAttr);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
(*Is Link*)
|
|
|
|
function FPS_ISLNK(iAttr: TFileAttrs) : Boolean; inline;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result := (iAttr and FILE_ATTRIBUTE_REPARSE_POINT <> 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result := BaseUnix.FPS_ISLNK(iAttr);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function FileIsExeLib(const sFileName : String) : Boolean;
|
|
var
|
|
fsExeLib : TFileStreamEx;
|
|
{$IFDEF MSWINDOWS}
|
|
Sign : Word;
|
|
{$ELSE}
|
|
Sign : DWord;
|
|
{$ENDIF}
|
|
begin
|
|
Result := False;
|
|
if mbFileExists(sFileName) and (mbFileSize(sFileName) >= SizeOf(Sign)) then
|
|
begin
|
|
fsExeLib := TFileStreamEx.Create(sFileName, fmOpenRead or fmShareDenyNone);
|
|
try
|
|
{$IFDEF MSWINDOWS}
|
|
Sign := fsExeLib.ReadWord;
|
|
Result := (Sign = $5A4D);
|
|
{$ELSE}
|
|
Sign := fsExeLib.ReadDWord;
|
|
Result := (Sign = $464C457F);
|
|
{$ENDIF}
|
|
finally
|
|
fsExeLib.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function FileIsReadOnly(iAttr: TFileAttrs): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= (iAttr and faReadOnly) <> 0;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= (((iAttr AND S_IRUSR) = S_IRUSR) and ((iAttr AND S_IWUSR) <> S_IWUSR));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileCopyAttr(const sSrc, sDst: UTF8String; Options: TCopyAttributesOptions): TCopyAttributesOptions;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Attr : TFileAttrs;
|
|
ModificationTime, CreationTime, LastAccessTime: DCBasicTypes.TFileTime;
|
|
begin
|
|
Result := [];
|
|
|
|
if caoCopyAttributes in Options then
|
|
begin
|
|
Attr := mbFileGetAttr(sSrc);
|
|
if Attr <> faInvalidAttributes then
|
|
begin
|
|
if (caoRemoveReadOnlyAttr in Options) and ((Attr and faReadOnly) <> 0) then
|
|
Attr := (Attr and not faReadOnly);
|
|
if mbFileSetAttr(sDst, Attr) <> 0 then
|
|
Include(Result, caoCopyAttributes);
|
|
end
|
|
else
|
|
Include(Result, caoCopyAttributes);
|
|
end;
|
|
|
|
if caoCopyTime in Options then
|
|
begin
|
|
if not (mbFileGetTime(sSrc, ModificationTime, CreationTime, LastAccessTime) and
|
|
mbFileSetTime(sDst, ModificationTime, CreationTime, LastAccessTime)) then
|
|
Include(Result, caoCopyTime);
|
|
end;
|
|
end;
|
|
{$ELSE} // *nix
|
|
var
|
|
StatInfo : BaseUnix.Stat;
|
|
utb : BaseUnix.TUTimBuf;
|
|
mode : TMode;
|
|
begin
|
|
if fpLStat(PChar(UTF8ToSys(sSrc)), StatInfo) >= 0 then
|
|
begin
|
|
Result := [];
|
|
if FPS_ISLNK(StatInfo.st_mode) then
|
|
begin
|
|
if caoCopyOwnership in Options then
|
|
begin
|
|
// Only group/owner can be set for links.
|
|
if fpLChown(PChar(UTF8ToSys(sDst)), StatInfo.st_uid, StatInfo.st_gid) = -1 then
|
|
begin
|
|
Include(Result, caoCopyOwnership);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if caoCopyTime in Options then
|
|
begin
|
|
utb.actime := time_t(StatInfo.st_atime); // last access time
|
|
utb.modtime := time_t(StatInfo.st_mtime); // last modification time
|
|
if fputime(PChar(UTF8ToSys(sDst)), @utb) <> 0 then
|
|
Include(Result, caoCopyTime);
|
|
end;
|
|
|
|
if caoCopyOwnership in Options then
|
|
begin
|
|
if fpChown(PChar(UTF8ToSys(sDst)), StatInfo.st_uid, StatInfo.st_gid) = -1 then
|
|
begin
|
|
Include(Result, caoCopyOwnership);
|
|
end;
|
|
end;
|
|
|
|
if caoCopyAttributes in Options then
|
|
begin
|
|
mode := StatInfo.st_mode;
|
|
if caoRemoveReadOnlyAttr in Options then
|
|
mode := SetModeReadOnly(mode, False);
|
|
if fpChmod(PChar(UTF8ToSys(sDst)), mode) = -1 then
|
|
begin
|
|
Include(Result, caoCopyAttributes);
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
Result := Options;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function GetTempName(PathPrefix: String): String;
|
|
const
|
|
MaxTries = 100;
|
|
var
|
|
TryNumber: Integer = 0;
|
|
begin
|
|
if PathPrefix = '' then
|
|
PathPrefix := GetTempDir;
|
|
repeat
|
|
Result := PathPrefix + IntToStr(System.Random(MaxInt)); // or use CreateGUID()
|
|
Inc(TryNumber);
|
|
if TryNumber = MaxTries then
|
|
Exit('');
|
|
until not mbFileSystemEntryExists(Result);
|
|
end;
|
|
|
|
function MapFile(const sFileName : UTF8String; out FileMapRec : TFileMapRec) : Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result := False;
|
|
with FileMapRec do
|
|
begin
|
|
MappedFile := nil;
|
|
MappingHandle := 0;
|
|
FileHandle := feInvalidHandle;
|
|
|
|
FileSize := mbFileSize(sFileName);
|
|
if FileSize = 0 then Exit; // Cannot map empty files
|
|
|
|
FileHandle := mbFileOpen(sFileName, fmOpenRead);
|
|
if FileHandle = feInvalidHandle then Exit;
|
|
|
|
MappingHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
|
|
if MappingHandle <> 0 then
|
|
begin
|
|
MappedFile := MapViewOfFile(MappingHandle, FILE_MAP_READ, 0, 0, 0);
|
|
if not Assigned(MappedFile) then
|
|
begin
|
|
UnMapFile(FileMapRec);
|
|
Exit;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
UnMapFile(FileMapRec);
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
StatInfo: BaseUnix.Stat;
|
|
begin
|
|
Result:= False;
|
|
with FileMapRec do
|
|
begin
|
|
MappedFile := nil;
|
|
FileHandle:= fpOpen(PChar(UTF8ToSys(sFileName)), O_RDONLY);
|
|
|
|
if FileHandle = feInvalidHandle then Exit;
|
|
if fpfstat(FileHandle, StatInfo) <> 0 then
|
|
begin
|
|
UnMapFile(FileMapRec);
|
|
Exit;
|
|
end;
|
|
|
|
FileSize := StatInfo.st_size;
|
|
if FileSize = 0 then // Cannot map empty files
|
|
begin
|
|
UnMapFile(FileMapRec);
|
|
Exit;
|
|
end;
|
|
|
|
MappedFile:= fpmmap(nil,FileSize,PROT_READ, MAP_PRIVATE{SHARED},FileHandle,0 );
|
|
if MappedFile = MAP_FAILED then
|
|
begin
|
|
MappedFile := nil;
|
|
UnMapFile(FileMapRec);
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure UnMapFile(var FileMapRec : TFileMapRec);
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
with FileMapRec do
|
|
begin
|
|
if Assigned(MappedFile) then
|
|
begin
|
|
UnmapViewOfFile(MappedFile);
|
|
MappedFile := nil;
|
|
end;
|
|
|
|
if MappingHandle <> 0 then
|
|
begin
|
|
CloseHandle(MappingHandle);
|
|
MappingHandle := 0;
|
|
end;
|
|
|
|
if FileHandle <> feInvalidHandle then
|
|
begin
|
|
FileClose(FileHandle);
|
|
FileHandle := feInvalidHandle;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
with FileMapRec do
|
|
begin
|
|
if FileHandle <> feInvalidHandle then
|
|
begin
|
|
fpClose(FileHandle);
|
|
FileHandle := feInvalidHandle;
|
|
end;
|
|
|
|
if Assigned(MappedFile) then
|
|
begin
|
|
fpmunmap(MappedFile,FileSize);
|
|
MappedFile := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ConsoleToUTF8(const Str: AnsiString): UTF8String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Dst: PChar;
|
|
{$ENDIF}
|
|
begin
|
|
Result:= Str;
|
|
{$IFDEF MSWINDOWS}
|
|
Dst:= AllocMem((Length(Result) + 1) * SizeOf(Char));
|
|
if OEMToChar(PChar(Result), Dst) then
|
|
Result:= SysToUTF8(Dst);
|
|
FreeMem(Dst);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function mbFileOpen(const FileName: UTF8String; Mode: Word): System.THandle;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= CreateFileW(PWideChar(UTF8Decode(FileName)), AccessModes[Mode and 3],
|
|
ShareModes[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpOpen(UTF8ToSys(FileName), AccessModes[Mode and 3]);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileCreate(const FileName: UTF8String): System.THandle;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result := mbFileCreate(FileName, fmShareDenyWrite, 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpOpen(UTF8ToSys(FileName), O_Creat or O_RdWr or O_Trunc);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileCreate(const FileName: UTF8String; ShareMode: Longint): System.THandle;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= mbFileCreate(FileName, ShareMode, 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
{$IF (FPC_VERSION > 2) or ((FPC_VERSION = 2) and (FPC_RELEASE >= 5))}
|
|
Result:= FileCreate(UTF8ToSys(FileName), ShareMode, 438); // 438 = 666 octal
|
|
{$ELSE}
|
|
Result:= FileCreate(UTF8ToSys(FileName), 438); // 438 = 666 octal
|
|
{$ENDIF}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileCreate(const FileName: UTF8String; ShareMode: Longint; Rights: Longint): System.THandle;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= CreateFileW(PWideChar(UTF8Decode(FileName)), GENERIC_READ or GENERIC_WRITE,
|
|
ShareModes[(ShareMode and $F0) shr 4], nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
{$IF (FPC_VERSION > 2) or ((FPC_VERSION = 2) and (FPC_RELEASE >= 5))}
|
|
Result:= FileCreate(UTF8ToSys(FileName), ShareMode, Rights);
|
|
{$ELSE}
|
|
Result:= FileCreate(UTF8ToSys(FileName), Rights);
|
|
{$ENDIF}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileAge(const FileName: UTF8String): DCBasicTypes.TFileTime;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Handle: System.THandle;
|
|
FindData: TWin32FindDataW;
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Handle := FindFirstFileW(PWChar(wFileName), FindData);
|
|
if Handle <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
Windows.FindClose(Handle);
|
|
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
|
|
Exit(DCBasicTypes.TWinFileTime(FindData.ftLastWriteTime));
|
|
end;
|
|
Result:= DCBasicTypes.TFileTime(-1);
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
Result:= DCBasicTypes.TFileTime(-1);
|
|
if fpStat(UTF8ToSys(FileName), Info) >= 0 then
|
|
{$PUSH}{$R-}
|
|
Result := Info.st_mtime;
|
|
{$POP}
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileSame(const FirstName, SecondName: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Handle: System.THandle;
|
|
lpFirstFileInfo,
|
|
lpSecondFileInfo: TByHandleFileInformation;
|
|
begin
|
|
// Read first file info
|
|
Handle:= CreateFileW(PWideChar(UTF8Decode(FirstName)), FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
if Handle = INVALID_HANDLE_VALUE then Exit(False);
|
|
Result:= GetFileInformationByHandle(Handle, lpFirstFileInfo);
|
|
CloseHandle(Handle);
|
|
if not Result then Exit;
|
|
// Read second file info
|
|
Handle:= CreateFileW(PWideChar(UTF8Decode(SecondName)), FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
if Handle = INVALID_HANDLE_VALUE then Exit(False);
|
|
Result:= GetFileInformationByHandle(Handle, lpSecondFileInfo);
|
|
CloseHandle(Handle);
|
|
if not Result then Exit;
|
|
// Compare file info
|
|
Result:= CompareByte(lpFirstFileInfo, lpSecondFileInfo,
|
|
SizeOf(TByHandleFileInformation)) = 0;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
FirstStat,
|
|
SecondStat: BaseUnix.Stat;
|
|
begin
|
|
// Read first file info
|
|
if fpStat(UTF8ToSys(FirstName), FirstStat) < 0 then Exit(False);
|
|
// Read second file info
|
|
if fpStat(UTF8ToSys(SecondName), SecondStat) < 0 then Exit(False);
|
|
// Compare file info
|
|
Result:= (FirstStat.st_dev = SecondStat.st_dev) and
|
|
(FirstStat.st_ino = SecondStat.st_ino);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileGetTime(const FileName: UTF8String;
|
|
var ModificationTime: DCBasicTypes.TFileTime;
|
|
var CreationTime : DCBasicTypes.TFileTime;
|
|
var LastAccessTime : DCBasicTypes.TFileTime): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Handle: System.THandle;
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Handle := CreateFileW(PWChar(wFileName),
|
|
FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS, // needed for opening directories
|
|
0);
|
|
|
|
if Handle <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
Result := Windows.GetFileTime(Handle,
|
|
@CreationTime,
|
|
@LastAccessTime,
|
|
@ModificationTime);
|
|
CloseHandle(Handle);
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
StatInfo : BaseUnix.Stat;
|
|
begin
|
|
Result := fpLStat(PChar(UTF8ToSys(FileName)), StatInfo) >= 0;
|
|
if Result then
|
|
begin
|
|
LastAccessTime := StatInfo.st_atime;
|
|
ModificationTime := StatInfo.st_mtime;
|
|
CreationTime := StatInfo.st_ctime;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileSetTime(const FileName: UTF8String;
|
|
ModificationTime: DCBasicTypes.TFileTime;
|
|
CreationTime : DCBasicTypes.TFileTime = 0;
|
|
LastAccessTime : DCBasicTypes.TFileTime = 0): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Handle: System.THandle;
|
|
wFileName: WideString;
|
|
PWinModificationTime: Windows.LPFILETIME = nil;
|
|
PWinCreationTime: Windows.LPFILETIME = nil;
|
|
PWinLastAccessTime: Windows.LPFILETIME = nil;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Handle := CreateFileW(PWChar(wFileName),
|
|
FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS, // needed for opening directories
|
|
0);
|
|
|
|
if Handle <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
if ModificationTime <> 0 then
|
|
begin
|
|
PWinModificationTime := @ModificationTime;
|
|
end;
|
|
if CreationTime <> 0 then
|
|
begin
|
|
PWinCreationTime := @CreationTime;
|
|
end;
|
|
if LastAccessTime <> 0 then
|
|
begin
|
|
PWinLastAccessTime := @LastAccessTime;
|
|
end;
|
|
|
|
Result := Windows.SetFileTime(Handle,
|
|
PWinCreationTime,
|
|
PWinLastAccessTime,
|
|
PWinModificationTime);
|
|
CloseHandle(Handle);
|
|
end
|
|
else
|
|
Result := False;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
t: TUTimBuf;
|
|
begin
|
|
t.actime := time_t(LastAccessTime);
|
|
t.modtime := time_t(ModificationTime);
|
|
Result := (fputime(PChar(UTF8ToSys(FileName)), @t) <> -1);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileExists(const FileName: UTF8String) : Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Attr: Dword;
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Attr:= GetFileAttributesW(PWChar(wFileName));
|
|
if Attr <> DWORD(-1) then
|
|
Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) = 0
|
|
else
|
|
Result:=False;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
// Can use fpStat, because link to an existing filename can be opened as if it were a real file.
|
|
if fpStat(UTF8ToSys(FileName), Info) >= 0 then
|
|
Result:= fpS_ISREG(Info.st_mode)
|
|
else
|
|
Result:= False;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileAccess(const FileName: UTF8String; Mode: Word): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
const
|
|
AccessMode: array[0..2] of DWORD = (
|
|
GENERIC_READ,
|
|
GENERIC_WRITE,
|
|
GENERIC_READ or GENERIC_WRITE);
|
|
var
|
|
hFile: System.THandle;
|
|
wFileName: WideString;
|
|
dwDesiredAccess: DWORD;
|
|
dwShareMode: DWORD = 0;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
dwDesiredAccess := AccessMode[Mode and 3];
|
|
if Mode = fmOpenRead then // If checking Read mode no sharing mode given
|
|
Mode := Mode or fmShareDenyNone;
|
|
dwShareMode := ShareModes[(Mode and $F0) shr 4];
|
|
hFile:= CreateFileW(PWChar(wFileName), dwDesiredAccess, dwShareMode,
|
|
nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
Result := hFile <> INVALID_HANDLE_VALUE;
|
|
if Result then
|
|
FileClose(hFile);
|
|
end;
|
|
{$ELSE}
|
|
const
|
|
AccessMode: array[0..2] of LongInt = (
|
|
R_OK,
|
|
W_OK,
|
|
R_OK or W_OK);
|
|
begin
|
|
Result:= fpAccess(UTF8ToSys(FileName), AccessMode[Mode and 3]) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFOPT R+}
|
|
{$DEFINE uOSUtilsRangeCheckOn}
|
|
{$R-}
|
|
{$ENDIF}
|
|
|
|
function mbFileGetAttr(const FileName: UTF8String): TFileAttrs;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Result := GetFileAttributesW(PWChar(wFileName));
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
if fpLStat(UTF8ToSys(FileName), @Info) >= 0 then
|
|
Result:= Info.st_mode
|
|
else
|
|
Result:= faInvalidAttributes;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileSetAttr(const FileName: UTF8String; Attr: TFileAttrs): LongInt;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
if SetFileAttributesW(PWChar(wFileName), Attr) then
|
|
Result:= 0
|
|
else
|
|
Result:= GetLastError;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpchmod(PChar(UTF8ToSys(FileName)), Attr);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileGetAttr(const FileName: UTF8String; out Attr: TSearchRec): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
FileInfo: Windows.TWin32FileAttributeData;
|
|
begin
|
|
Result:= GetFileAttributesExW(PWideChar(UTF8Decode(FileName)),
|
|
GetFileExInfoStandard, @FileInfo);
|
|
if Result then
|
|
begin
|
|
WinToDosTime(FileInfo.ftLastWriteTime, Attr.Time);
|
|
Int64Rec(Attr.Size).Lo:= FileInfo.nFileSizeLow;
|
|
Int64Rec(Attr.Size).Hi:= FileInfo.nFileSizeHigh;
|
|
Attr.Attr:= FileInfo.dwFileAttributes;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
StatInfo: BaseUnix.Stat;
|
|
begin
|
|
Result:= fpLStat(PAnsiChar(UTF8ToSys(FileName)), StatInfo) >= 0;
|
|
if Result then
|
|
begin
|
|
Attr.Time:= StatInfo.st_mtime;
|
|
Attr.Size:= StatInfo.st_size;
|
|
Attr.Attr:= StatInfo.st_mode;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{$IFDEF uOSUtilsRangeCheckOn}
|
|
{$R+}
|
|
{$UNDEF uOSUtilsRangeCheckOn}
|
|
{$ENDIF}
|
|
|
|
function mbFileSetReadOnly(const FileName: UTF8String; ReadOnly: Boolean): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
iAttr: DWORD;
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
iAttr := GetFileAttributesW(PWChar(wFileName));
|
|
if iAttr = DWORD(-1) then Exit(False);
|
|
if ReadOnly then
|
|
iAttr:= iAttr or faReadOnly
|
|
else
|
|
iAttr:= iAttr and not faReadOnly;
|
|
Result:= SetFileAttributesW(PWChar(wFileName), iAttr) = True;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
StatInfo: BaseUnix.Stat;
|
|
mode: TMode;
|
|
begin
|
|
if fpStat(PChar(UTF8ToSys(FileName)), StatInfo) <> 0 then Exit(False);
|
|
mode := SetModeReadOnly(StatInfo.st_mode, ReadOnly);
|
|
Result:= fpchmod(PChar(UTF8ToSys(FileName)), mode) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbDeleteFile(const FileName: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wFileName: WideString;
|
|
begin
|
|
wFileName:= UTF8Decode(FileName);
|
|
Result:= Windows.DeleteFileW(PWChar(wFileName));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpUnLink(UTF8ToSys(FileName)) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbRenameFile(const OldName: UTF8String; NewName: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wOldName,
|
|
wNewName: WideString;
|
|
begin
|
|
wOldName:= UTF8Decode(OldName);
|
|
wNewName:= UTF8Decode(NewName);
|
|
Result:= MoveFileExW(PWChar(wOldName), PWChar(wNewName), MOVEFILE_REPLACE_EXISTING);
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
tmpFileName: UTF8String;
|
|
OldFileStat, NewFileStat: stat;
|
|
begin
|
|
if GetPathType(NewName) <> ptAbsolute then
|
|
NewName := ExtractFilePath(OldName) + NewName;
|
|
|
|
if OldName = NewName then
|
|
Exit(True);
|
|
|
|
if fpLstat(UTF8ToSys(OldName), OldFileStat) <> 0 then
|
|
Exit(False);
|
|
|
|
// Check if target file exists.
|
|
if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
|
|
begin
|
|
// Check if source and target are the same files (same inode and same device).
|
|
if (OldFileStat.st_ino = NewFileStat.st_ino) and
|
|
(OldFileStat.st_dev = NewFileStat.st_dev) then
|
|
begin
|
|
// Check number of links.
|
|
// If it is 1 then source and target names most probably differ only
|
|
// by case on a case-insensitive filesystem. Direct rename() in such case
|
|
// fails on Linux, so we use a temporary file name and rename in two stages.
|
|
// If number of links is more than 1 then it's enough to simply unlink
|
|
// the source file, since both files are technically identical.
|
|
// (On Linux rename() returns success but doesn't do anything
|
|
// if renaming a file to its hard link.)
|
|
// We cannot use st_nlink for directories because it means "number of
|
|
// subdirectories"; hard links to directories are not supported on Linux
|
|
// or Windows anyway (on MacOSX they are). Therefore we always treat
|
|
// directories as if they were a single link and rename them using temporary name.
|
|
|
|
if (NewFileStat.st_nlink = 1) or BaseUnix.fpS_ISDIR(NewFileStat.st_mode) then
|
|
begin
|
|
tmpFileName := GetTempName(OldName);
|
|
|
|
if FpRename(UTF8ToSys(OldName), UTF8ToSys(tmpFileName)) = 0 then
|
|
begin
|
|
if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
|
|
begin
|
|
// We have renamed the old file but the new file name still exists,
|
|
// so this wasn't a single file on a case-insensitive filesystem
|
|
// accessible by two names that differ by case.
|
|
|
|
FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName)); // Restore old file.
|
|
{$IFDEF DARWIN}
|
|
// If it's a directory with multiple hard links then simply unlink the source.
|
|
if BaseUnix.fpS_ISDIR(NewFileStat.st_mode) and (NewFileStat.st_nlink > 1) then
|
|
Result := (fpUnLink(UTF8ToSys(OldName)) = 0)
|
|
else
|
|
{$ENDIF}
|
|
Result := False;
|
|
end
|
|
else if FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(NewName)) = 0 then
|
|
begin
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName)); // Restore old file.
|
|
Result := False;
|
|
end;
|
|
end
|
|
else
|
|
Result := False;
|
|
end
|
|
else
|
|
begin
|
|
// Multiple links - simply unlink the source file.
|
|
Result := (fpUnLink(UTF8ToSys(OldName)) = 0);
|
|
end;
|
|
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := FpRename(UTF8ToSys(OldName), UTF8ToSys(NewName)) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileSize(const FileName: UTF8String): Int64;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Handle: System.THandle;
|
|
FindData: TWin32FindDataW;
|
|
wFileName: WideString;
|
|
begin
|
|
Result:= 0;
|
|
wFileName:= UTF8Decode(FileName);
|
|
Handle := FindFirstFileW(PWideChar(wFileName), FindData);
|
|
if Handle <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
Windows.FindClose(Handle);
|
|
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
|
|
begin
|
|
Int64Rec(Result).Lo:= FindData.nFileSizeLow;
|
|
Int64Rec(Result).Hi:= FindData.nFileSizeHigh;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
Result:= 0;
|
|
if fpStat(UTF8ToSys(FileName), Info) >= 0 then
|
|
Result:= Info.st_size;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function FileFlush(Handle: System.THandle): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= FlushFileBuffers(Handle);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= (fpfsync(Handle) = 0);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbGetCurrentDir: UTF8String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
iSize: Integer;
|
|
wsDir: WideString;
|
|
begin
|
|
Result:= '';
|
|
iSize:= GetCurrentDirectoryW(0, nil);
|
|
if iSize > 0 then
|
|
begin
|
|
SetLength(wsDir, iSize);
|
|
GetCurrentDirectoryW(iSize, PWideChar(wsDir));
|
|
wsDir:= PWideChar(wsDir);
|
|
Result:= UTF8Encode(wsDir);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
GetDir(0, Result);
|
|
Result := SysToUTF8(Result);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbSetCurrentDir(const NewDir: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsNewDir: WideString;
|
|
NetResource: TNetResourceW;
|
|
begin
|
|
// Function WNetAddConnection2W works very slow
|
|
// when the final character is a backslash ('\')
|
|
wsNewDir:= UTF8Decode(ExcludeTrailingPathDelimiter(NewDir));
|
|
if Pos('\\', wsNewDir) = 1 then
|
|
begin
|
|
FillChar(NetResource, SizeOf(NetResource), #0);
|
|
NetResource.dwType:= RESOURCETYPE_ANY;
|
|
NetResource.lpRemoteName:= PWideChar(wsNewDir);
|
|
WNetAddConnection2W(NetResource, nil, nil, CONNECT_INTERACTIVE);
|
|
end;
|
|
// MSDN says that the final character must be a backslash ('\').
|
|
wsNewDir:= wsNewDir + DirectorySeparator;
|
|
Result:= SetCurrentDirectoryW(PWideChar(wsNewDir));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpChDir(PChar(UTF8ToSys(NewDir))) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbDirectoryExists(const Directory: UTF8String) : Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
Attr:Dword;
|
|
wDirectory: WideString;
|
|
begin
|
|
wDirectory:= UTF8Decode(Directory);
|
|
Attr:= GetFileAttributesW(PWChar(wDirectory));
|
|
if Attr <> DWORD(-1) then
|
|
Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) > 0
|
|
else
|
|
Result:= False;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
// We can use fpStat here instead of fpLstat, so that True is returned
|
|
// when target is a directory or a link to an existing directory.
|
|
// Note that same behaviour would be achieved by passing paths
|
|
// that end with path delimiter to fpLstat.
|
|
// Paths with links can be used the same way as if they were real directories.
|
|
if fpStat(UTF8ToSys(Directory), Info) >= 0 then
|
|
Result:= fpS_ISDIR(Info.st_mode)
|
|
else
|
|
Result:= False;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbCreateDir(const NewDir: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wNewDir: WideString;
|
|
begin
|
|
wNewDir:= UTF8Decode(NewDir);
|
|
Result:= CreateDirectoryW(PWChar(wNewDir), nil);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpMkDir(PChar(UTF8ToSys(NewDir)), $1FF) = 0; // $1FF = &0777
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbRemoveDir(const Dir: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wDir: WideString;
|
|
begin
|
|
wDir:= UTF8Decode(Dir);
|
|
Result:= RemoveDirectoryW(PWChar(wDir));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= fpRmDir(PChar(UTF8ToSys(Dir))) = 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileSystemEntryExists(const Path: UTF8String): Boolean;
|
|
begin
|
|
Result := mbFileGetAttr(Path) <> faInvalidAttributes;
|
|
end;
|
|
|
|
function mbCompareFileNames(const FileName1, FileName2: UTF8String): Boolean; inline;
|
|
{$IF DEFINED(WINDOWS) OR DEFINED(DARWIN)}
|
|
begin
|
|
Result:= (WideCompareText(UTF8Decode(FileName1), UTF8Decode(FileName2)) = 0);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= (WideCompareStr(UTF8Decode(FileName1), UTF8Decode(FileName2)) = 0);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbSameFile(const FileName1, FileName2: String): Boolean;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
var
|
|
FileHandle1, FileHandle2: System.THandle;
|
|
FileInfo1, FileInfo2: BY_HANDLE_FILE_INFORMATION;
|
|
begin
|
|
Result := mbCompareFileNames(FileName1, FileName2);
|
|
|
|
if not Result then
|
|
begin
|
|
FileHandle1 := CreateFileW(PWideChar(UTF8Decode(FileName1)), FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil, OPEN_EXISTING, 0, 0);
|
|
|
|
if FileHandle1 <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
FileHandle2 := CreateFileW(PWideChar(UTF8Decode(FileName2)), FILE_READ_ATTRIBUTES,
|
|
FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
|
|
nil, OPEN_EXISTING, 0, 0);
|
|
|
|
if FileHandle2 <> INVALID_HANDLE_VALUE then
|
|
begin
|
|
if GetFileInformationByHandle(FileHandle1, FileInfo1) and
|
|
GetFileInformationByHandle(FileHandle2, FileInfo2) then
|
|
begin
|
|
// Check if both files have the same index on the same volume.
|
|
// This check is valid only while both files are open.
|
|
Result := (FileInfo1.dwVolumeSerialNumber = FileInfo2.dwVolumeSerialNumber) and
|
|
(FileInfo1.nFileIndexHigh = FileInfo2.nFileIndexHigh) and
|
|
(FileInfo1.nFileIndexLow = FileInfo2.nFileIndexLow);
|
|
end;
|
|
CloseHandle(FileHandle2);
|
|
end;
|
|
CloseHandle(FileHandle1);
|
|
end
|
|
end;
|
|
end;
|
|
{$ELSEIF DEFINED(UNIX)}
|
|
var
|
|
File1Stat, File2Stat: stat;
|
|
begin
|
|
Result := mbCompareFileNames(FileName1, FileName2) or
|
|
(
|
|
(fpLstat(UTF8ToSys(FileName1), File1Stat) = 0) and
|
|
(fpLstat(UTF8ToSys(FileName2), File2Stat) = 0) and
|
|
(File1Stat.st_ino = File2Stat.st_ino) and
|
|
(File1Stat.st_dev = File2Stat.st_dev)
|
|
);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbGetEnvironmentString(Index: Integer): UTF8String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
hp, p: PWideChar;
|
|
begin
|
|
Result:= '';
|
|
p:= GetEnvironmentStringsW;
|
|
hp:= p;
|
|
if (hp <> nil) then
|
|
begin
|
|
while (hp^ <> #0) and (Index > 1) do
|
|
begin
|
|
Dec(Index);
|
|
hp:= hp + lstrlenW(hp) + 1;
|
|
end;
|
|
if (hp^ <> #0) then
|
|
Result:= UTF8Encode(WideString(hp));
|
|
end;
|
|
FreeEnvironmentStringsW(p);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= SysToUTF8(GetEnvironmentString(Index));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbSysErrorMessage(ErrorCode: Integer): UTF8String;
|
|
begin
|
|
Result :=
|
|
{$IFDEF WINDOWS}
|
|
UTF8Encode(SysErrorMessage(ErrorCode));
|
|
{$ELSE}
|
|
SysToUTF8(SysErrorMessage(ErrorCode));
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function mbLoadLibrary(const Name: UTF8String): TLibHandle;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsName: WideString;
|
|
begin
|
|
wsName:= UTF8Decode(Name);
|
|
Result:= LoadLibraryW(PWideChar(wsName));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= TLibHandle(dlopen(PChar(UTF8ToSys(Name)), RTLD_LAZY));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function SafeGetProcAddress(Lib: TLibHandle; const ProcName: AnsiString): Pointer;
|
|
begin
|
|
Result:= GetProcedureAddress(Lib, ProcName);
|
|
if (Result = nil) then raise Exception.Create(ProcName);
|
|
end;
|
|
|
|
end.
|