UPD: Use fcopyfile function under macOS (issues #2018, #2526)

This commit is contained in:
Alexander Koblov 2025-09-30 19:31:08 +03:00
commit df447bf704
2 changed files with 144 additions and 0 deletions

View file

@ -806,6 +806,46 @@ begin
TargetFileStream.Seek(0, fsFromBeginning);
end;
end else
{$ELSEIF DEFINED(DARWIN)}
if (Mode = fsohcmDefault) then
begin
FReserveSpace:= False;
OpenTargetFile;
if not Assigned(TargetFileStream) then
Exit;
Result:= CopyFileF(SourceFileStream.Handle, TargetFileStream.Handle, Options, @FileCopyProgress, Self);
if Result then
begin
FreeAndNil(SourceFileStream);
FreeAndNil(TargetFileStream);
if FVerify then
begin
Result:= CompareFiles(SourceFile.FullPath, TargetFileName, SourceFile.Size);
end;
if Result then begin
CopyProperties(SourceFile, TargetFileName);
end;
end
else begin
bDeleteFile := True;
if FSkipCopyError then Exit;
case AskQuestion('',
Format(rsMsgErrCannotCopyFile, [WrapTextSimple(SourceFile.FullPath, 64), WrapTextSimple(TargetFileName, 64)]) +
LineEnding + LineEnding + mbSysErrorMessage,
[fsourSkip, fsourSkipAll, fsourAbort],
fsourSkip, fsourAbort) of
fsourAbort:
AbortOperation;
fsourSkipAll:
FSkipCopyError := True;
end; // case
end;
Exit;
end;
{$ENDIF}
OpenTargetFile;

View file

@ -19,6 +19,10 @@ var
FileCopyEx: TFileCopyEx = nil;
CopyAttributesOptionEx: TCopyAttributesOptions = [];
{$IF DEFINED(DARWIN)}
function CopyFileF(Source, Target: THandle; Options: UInt32; UpdateProgress: TFileCopyProgress; UserData: Pointer): LongBool;
{$ENDIF}
implementation
{$IF DEFINED(MSWINDOWS)}
@ -63,6 +67,106 @@ end;
initialization
FileCopyEx:= @CopyFile;
CopyAttributesOptionEx:= [caoCopyTimeEx, caoCopyAttrEx];
{$ELSEIF DEFINED(DARWIN)}
uses
InitC, CTypes;
const
// copyfile_state_t values
COPYFILE_STATE_STATUS_CB = 6;
COPYFILE_STATE_STATUS_CTX = 7;
COPYFILE_STATE_COPIED = 8;
const
// flags for copyfile
COPYFILE_DATA = (1 << 3);
// progress function what
COPYFILE_COPY_DATA = 4;
// progress function stages
COPYFILE_START = 1;
COPYFILE_FINISH = 2;
COPYFILE_ERR = 3;
COPYFILE_PROGRESS = 4;
// callback function return values
COPYFILE_CONTINUE = 0;
COPYFILE_SKIP = 1;
COPYFILE_QUIT = 2;
type
TCopyInfo = class
FileSize: Int64;
UserData: Pointer;
UpdateProgress: TFileCopyProgress;
end;
type
copyfile_flags_t = type cuint32;
copyfile_state_t = type pointer;
function copyfile_state_free(s: copyfile_state_t): cint; cdecl; external clib;
function copyfile_state_alloc(): copyfile_state_t; cdecl; external clib;
function copyfile_state_get(s: copyfile_state_t; flag: cuint32; dst: pointer): cint; cdecl; external clib;
function copyfile_state_set(s: copyfile_state_t; flag: cuint32; const src: pointer): cint; cdecl; external clib;
function fcopyfile(from_fd, to_fd: cint; s: copyfile_state_t; flags: copyfile_flags_t): cint; cdecl; external clib;
function Callback(what, stage: cint; state: copyfile_state_t; src, dst: pansichar; ctx: pointer): cint; cdecl;
var
BytesTransferred: coff_t;
ACopyInfo: TCopyInfo absolute ctx;
begin
if (what = COPYFILE_COPY_DATA) and (stage = COPYFILE_PROGRESS) then
begin
if (copyfile_state_get(state, COPYFILE_STATE_COPIED, @BytesTransferred) = 0) then
begin
if ACopyInfo.UpdateProgress(ACopyInfo.FileSize, BytesTransferred, ACopyInfo.UserData) then
Result:= COPYFILE_CONTINUE
else begin
Result:= COPYFILE_QUIT;
end;
Exit;
end;
end;
Result:= COPYFILE_CONTINUE;
end;
function CopyFileF(Source, Target: THandle; Options: UInt32; UpdateProgress: TFileCopyProgress; UserData: Pointer): LongBool;
var
ACopyInfo: TCopyInfo;
LastError: Integer = 0;
AState: copyfile_state_t;
begin
Result:= False;
AState:= copyfile_state_alloc();
if Assigned(AState) then
begin
ACopyInfo:= TCopyInfo.Create;
ACopyInfo.UserData:= UserData;
ACopyInfo.FileSize:= FileGetSize(Source);
ACopyInfo.UpdateProgress:= UpdateProgress;
copyfile_state_set(AState, COPYFILE_STATE_STATUS_CB, @Callback);
copyfile_state_set(AState, COPYFILE_STATE_STATUS_CTX, ACopyInfo);
Result:= fcopyfile(Source, Target, AState, COPYFILE_DATA) = 0;
if not Result then LastError:= cerrno;
copyfile_state_free(AState);
ACopyInfo.Free;
if (LastError <> 0) then
begin
SetLastOSError(LastError);
end;
end;
end;
{$ENDIF}
end.