mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-28 10:02:14 +00:00
FIX: Try to fix bug [2923641] "Change case of filenames on VFAT-volume fails".
This commit is contained in:
parent
d9490837dd
commit
37e6610a7e
7 changed files with 352 additions and 70 deletions
|
|
@ -734,8 +734,7 @@ begin
|
|||
for c:=0 to lsvwFile.Items.Count-1 do
|
||||
with lsvwFile.Items do
|
||||
begin
|
||||
if RenameFile(FFileSource, TFile(Item[c].Data),
|
||||
Item[c].SubItems[1]+pathdelim+Item[c].SubItems[0], True) = True then
|
||||
if RenameFile(FFileSource, TFile(Item[c].Data), Item[c].SubItems[0], True) = True then
|
||||
begin
|
||||
Item[c].Caption := Item[c].SubItems[0]; // write the new name to table
|
||||
TFile(Item[c].Data).Name := Item[c].SubItems[0]; // and to the file object
|
||||
|
|
@ -1020,4 +1019,4 @@ end;
|
|||
initialization
|
||||
{$I fmultirename.lrs}
|
||||
end.
|
||||
|
||||
|
||||
|
|
@ -23,6 +23,8 @@ type
|
|||
|
||||
TFilePropertiesDescriptions = array of String;//TFileProperty;
|
||||
|
||||
EInvalidFileProperty = class(Exception);
|
||||
|
||||
// Forward declarations.
|
||||
IFilePropertyFormatter = interface;
|
||||
|
||||
|
|
@ -67,6 +69,8 @@ type
|
|||
private
|
||||
FName: UTF8String; // only name, no path
|
||||
|
||||
procedure SetName(NewName: UTF8String);
|
||||
|
||||
public
|
||||
constructor Create; override;
|
||||
constructor Create(Name: UTF8String); virtual; overload;
|
||||
|
|
@ -79,7 +83,7 @@ type
|
|||
|
||||
function Format(Formatter: IFilePropertyFormatter): String; override;
|
||||
|
||||
property Value: UTF8String read FName write FName;
|
||||
property Value: UTF8String read FName write SetName;
|
||||
end;
|
||||
|
||||
TFileSizeProperty = class(TFileProperty)
|
||||
|
|
@ -341,6 +345,17 @@ begin
|
|||
Result := Formatter.FormatFileName(Self);
|
||||
end;
|
||||
|
||||
procedure TFileNameProperty.SetName(NewName: UTF8String);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i := 1 to Length(NewName) do
|
||||
if NewName[i] in AllowDirectorySeparators then
|
||||
raise EInvalidFileProperty.Create('Name cannot have directory separators');
|
||||
|
||||
FName := NewName;
|
||||
end;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
constructor TFileSizeProperty.Create;
|
||||
|
|
|
|||
|
|
@ -508,7 +508,6 @@ uses
|
|||
uFileSourceOperationOptions,
|
||||
uFileSourceCalcStatisticsOperation,
|
||||
uFileSystemFile,
|
||||
uFileSystemFileSource,
|
||||
fColumnsSetConf,
|
||||
uKeyboard,
|
||||
uFileSourceUtil
|
||||
|
|
@ -1838,7 +1837,6 @@ procedure TColumnsFileView.edtRenameKeyDown(Sender: TObject; var Key: Word;
|
|||
var
|
||||
NewFileName: String;
|
||||
OldFileNameAbsolute: String;
|
||||
NewFileNameAbsolute: String;
|
||||
begin
|
||||
case Key of
|
||||
VK_ESCAPE:
|
||||
|
|
@ -1855,25 +1853,21 @@ begin
|
|||
|
||||
NewFileName := edtRename.Text;
|
||||
OldFileNameAbsolute := edtRename.Hint;
|
||||
NewFileNameAbsolute := ExtractFilePath(OldFileNameAbsolute) + NewFileName;
|
||||
|
||||
if (FileSource.IsClass(TFileSystemFileSource)) and mbFileExists(NewFileNameAbsolute) then
|
||||
begin
|
||||
if MsgBox(Format(rsMsgFileExistsRwrt, [NewFileName]),
|
||||
[msmbYes, msmbNo], msmbYes, msmbNo) = mmrNo then
|
||||
try
|
||||
if RenameFile(FileSource, ActiveFile, NewFileName, True) = True then
|
||||
begin
|
||||
Exit;
|
||||
end;
|
||||
edtRename.Visible:=False;
|
||||
LastActive := NewFileName;
|
||||
SetFocus;
|
||||
end
|
||||
else
|
||||
msgError(Format(rsMsgErrRename, [ExtractFileName(OldFileNameAbsolute), NewFileName]));
|
||||
|
||||
except
|
||||
on e: EInvalidFileProperty do
|
||||
msgError(Format(rsMsgErrRename + ':' + LineEnding + '%s (%s)', [ExtractFileName(OldFileNameAbsolute), NewFileName, rsMsgInvalidFileName, e.Message]));
|
||||
end;
|
||||
|
||||
if RenameFile(FileSource, ActiveFile, NewFileNameAbsolute, True) = True then
|
||||
begin
|
||||
edtRename.Visible:=False;
|
||||
LastActive := NewFileName;
|
||||
SetFocus;
|
||||
end
|
||||
else
|
||||
msgError(Format(rsMsgErrRename, [ExtractFileName(OldFileNameAbsolute), NewFileName]));
|
||||
end;
|
||||
|
||||
{$IFDEF LCLGTK2}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ uses
|
|||
|
||||
type
|
||||
|
||||
TSetFilePropertyResult = (sfprSuccess, sfprError, sfprSkipped);
|
||||
|
||||
TFileSourceSetFilePropertyOperationStatistics = record
|
||||
CurrentFile: String;
|
||||
TotalFiles: Int64;
|
||||
|
|
@ -80,7 +82,7 @@ type
|
|||
procedure UpdateStatisticsAtStartTime; override;
|
||||
|
||||
procedure SetProperties(aFile: TFile; aTemplateFile: TFile);
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): Boolean; virtual abstract;
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): TSetFilePropertyResult; virtual abstract;
|
||||
|
||||
function GetErrorString(aFile: TFile; aProperty: TFileProperty): String;
|
||||
|
||||
|
|
@ -245,7 +247,7 @@ var
|
|||
templateProperty: TFileProperty;
|
||||
bRetry: Boolean;
|
||||
sMessage, sQuestion: String;
|
||||
Success: Boolean;
|
||||
SetResult: TSetFilePropertyResult;
|
||||
{$IFNDEF AssignFileNameProperty}
|
||||
TargetName: UTF8String; // for reporting errors when setting fpName property
|
||||
{$ENDIF}
|
||||
|
|
@ -256,7 +258,7 @@ begin
|
|||
begin
|
||||
repeat
|
||||
bRetry := False;
|
||||
Success := True;
|
||||
SetResult := sfprSuccess;
|
||||
|
||||
{$IFNDEF AssignFileNameProperty}
|
||||
if prop = fpName then
|
||||
|
|
@ -265,13 +267,13 @@ begin
|
|||
begin
|
||||
templateProperty := TFileNameProperty.Create(aTemplateFile.Name);
|
||||
TargetName := aTemplateFile.Name;
|
||||
Success := SetNewProperty(aFile, templateProperty);
|
||||
SetResult := SetNewProperty(aFile, templateProperty);
|
||||
FreeAndNil(templateProperty);
|
||||
end
|
||||
else if Assigned(NewProperties[fpName]) then
|
||||
begin
|
||||
TargetName := (NewProperties[fpName] as TFileNameProperty).Value;
|
||||
Success := SetNewProperty(aFile, NewProperties[fpName]);
|
||||
SetResult := SetNewProperty(aFile, NewProperties[fpName]);
|
||||
end;
|
||||
end
|
||||
else
|
||||
|
|
@ -287,10 +289,10 @@ begin
|
|||
|
||||
// Check if there is a new property to be set.
|
||||
if Assigned(templateProperty) then
|
||||
Success := SetNewProperty(aFile, templateProperty);
|
||||
SetResult := SetNewProperty(aFile, templateProperty);
|
||||
end;
|
||||
|
||||
if not Success then
|
||||
if SetResult = sfprError then
|
||||
begin
|
||||
{$IFNDEF AssignFileNameProperty}
|
||||
if prop = fpName then
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ type
|
|||
// Options.
|
||||
FSymLinkOption: TFileSourceOperationOptionSymLink;
|
||||
|
||||
function RenameFile(const OldName: UTF8String; NewName: UTF8String): TSetFilePropertyResult;
|
||||
|
||||
protected
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): Boolean; override;
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): TSetFilePropertyResult; override;
|
||||
|
||||
public
|
||||
constructor Create(aTargetFileSource: IFileSource;
|
||||
|
|
@ -43,8 +45,14 @@ type
|
|||
implementation
|
||||
|
||||
uses
|
||||
uGlobs, uOSUtils, uLng, uDateTimeUtils, uFileSystemUtil,
|
||||
uFileSourceOperationUI;
|
||||
uGlobs, uOSUtils, uDCUtils, uLng, uDateTimeUtils, uFileSystemUtil, uTypes,
|
||||
uFileSourceOperationUI
|
||||
{$IF DEFINED(MSWINDOWS)}
|
||||
, LCLProc
|
||||
{$ELSEIF DEFINED(UNIX)}
|
||||
, BaseUnix
|
||||
{$ENDIF}
|
||||
;
|
||||
|
||||
constructor TFileSystemSetFilePropertyOperation.Create(aTargetFileSource: IFileSource;
|
||||
var theTargetFiles: TFiles;
|
||||
|
|
@ -130,61 +138,83 @@ begin
|
|||
end;
|
||||
|
||||
function TFileSystemSetFilePropertyOperation.SetNewProperty(aFile: TFile;
|
||||
aTemplateProperty: TFileProperty): Boolean;
|
||||
aTemplateProperty: TFileProperty): TSetFilePropertyResult;
|
||||
begin
|
||||
Result := True;
|
||||
Result := sfprSuccess;
|
||||
|
||||
try
|
||||
case aTemplateProperty.GetID of
|
||||
fpName:
|
||||
if (aTemplateProperty as TFileNameProperty).Value <> aFile.Name then
|
||||
begin
|
||||
Result := mbRenameFile(
|
||||
Result := RenameFile(
|
||||
aFile.FullPath,
|
||||
(aTemplateProperty as TFileNameProperty).Value);
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpAttributes:
|
||||
if (aTemplateProperty as TFileAttributesProperty).Value <>
|
||||
(aFile.Properties[fpAttributes] as TFileAttributesProperty).Value then
|
||||
begin
|
||||
Result := mbFileSetAttr(
|
||||
if mbFileSetAttr(
|
||||
aFile.FullPath,
|
||||
(aTemplateProperty as TFileAttributesProperty).Value) = 0;
|
||||
end;
|
||||
(aTemplateProperty as TFileAttributesProperty).Value) <> 0 then
|
||||
begin
|
||||
Result := sfprError;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpModificationTime:
|
||||
if (aTemplateProperty as TFileModificationDateTimeProperty).Value <>
|
||||
(aFile.Properties[fpModificationTime] as TFileModificationDateTimeProperty).Value then
|
||||
begin
|
||||
Result := mbFileSetTime(
|
||||
if not mbFileSetTime(
|
||||
aFile.FullPath,
|
||||
DateTimeToFileTime((aTemplateProperty as TFileModificationDateTimeProperty).Value),
|
||||
0,
|
||||
0);
|
||||
end;
|
||||
0) then
|
||||
begin
|
||||
Result := sfprError;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpCreationTime:
|
||||
if (aTemplateProperty as TFileCreationDateTimeProperty).Value <>
|
||||
(aFile.Properties[fpCreationTime] as TFileCreationDateTimeProperty).Value then
|
||||
begin
|
||||
Result := mbFileSetTime(
|
||||
if not mbFileSetTime(
|
||||
aFile.FullPath,
|
||||
0,
|
||||
DateTimeToFileTime((aTemplateProperty as TFileCreationDateTimeProperty).Value),
|
||||
0);
|
||||
end;
|
||||
0) then
|
||||
begin
|
||||
Result := sfprError;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpLastAccessTime:
|
||||
if (aTemplateProperty as TFileLastAccessDateTimeProperty).Value <>
|
||||
(aFile.Properties[fpLastAccessTime] as TFileLastAccessDateTimeProperty).Value then
|
||||
begin
|
||||
Result := mbFileSetTime(
|
||||
if not mbFileSetTime(
|
||||
aFile.FullPath,
|
||||
0,
|
||||
0,
|
||||
DateTimeToFileTime((aTemplateProperty as TFileLastAccessDateTimeProperty).Value));
|
||||
end;
|
||||
DateTimeToFileTime((aTemplateProperty as TFileLastAccessDateTimeProperty).Value)) then
|
||||
begin
|
||||
Result := sfprError;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
else
|
||||
raise Exception.Create('Trying to set unsupported property');
|
||||
|
|
@ -194,14 +224,161 @@ begin
|
|||
on e: EConvertError do
|
||||
begin
|
||||
if not gSkipFileOpError then
|
||||
if AskQuestion(rsMsgLogError + e.Message, '', [fsourSkip, fsourAbort],
|
||||
fsourSkip, fsourAbort) = fsourAbort then
|
||||
begin
|
||||
RaiseAbortOperation;
|
||||
case AskQuestion(rsMsgLogError + e.Message, '', [fsourSkip, fsourAbort],
|
||||
fsourSkip, fsourAbort) of
|
||||
fsourSkip:
|
||||
Result := sfprSkipped;
|
||||
fsourAbort:
|
||||
RaiseAbortOperation;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TFileSystemSetFilePropertyOperation.RenameFile(const OldName: UTF8String; NewName: UTF8String): TSetFilePropertyResult;
|
||||
|
||||
function AskIfOverwrite(Attrs: TFileAttrs): TFileSourceOperationUIResponse;
|
||||
var
|
||||
sQuestion: String;
|
||||
begin
|
||||
if uOSUtils.FPS_ISDIR(Attrs) then
|
||||
sQuestion := rsMsgFolderExistsRwrt
|
||||
else
|
||||
sQuestion := rsMsgFileExistsRwrt;
|
||||
|
||||
Result := AskQuestion(Format(sQuestion, [NewName]), '',
|
||||
[fsourYes, fsourNo, fsourAbort], fsourYes, fsourNo);
|
||||
end;
|
||||
|
||||
var
|
||||
{$IFDEF UNIX}
|
||||
tmpFileName: UTF8String;
|
||||
OldFileStat, NewFileStat: stat;
|
||||
{$ELSE}
|
||||
NewFileAttrs: TFileAttrs;
|
||||
{$ENDIF}
|
||||
begin
|
||||
if FileSource.GetPathType(NewName) <> ptAbsolute then
|
||||
NewName := ExtractFilePath(OldName) + NewName;
|
||||
|
||||
if OldName = NewName then
|
||||
Exit(sfprSkipped);
|
||||
|
||||
{$IFDEF UNIX}
|
||||
if fpLstat(OldName, OldFileStat) <> 0 then
|
||||
Exit(sfprError);
|
||||
|
||||
// Check if target file exists.
|
||||
if fpLstat(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(OldName, tmpFileName) = 0 then
|
||||
begin
|
||||
if fpLstat(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(tmpFileName, OldName); // Restore old file.
|
||||
{$IFDEF DARWIN}
|
||||
// If it was a directory with multiple hard links then fall through
|
||||
// to asking for overwrite and unlinking source link.
|
||||
if not (BaseUnix.fpS_ISDIR(NewFileStat.st_mode) and (NewFileStat.st_nlink > 1)) then
|
||||
{$ENDIF}
|
||||
Exit(sfprError);
|
||||
end
|
||||
else if FpRename(tmpFileName, NewName) = 0 then
|
||||
begin
|
||||
Exit(sfprSuccess);
|
||||
end
|
||||
else
|
||||
begin
|
||||
FpRename(tmpFileName, OldName); // Restore old file.
|
||||
Exit(sfprError);
|
||||
end;
|
||||
end
|
||||
else
|
||||
Exit(sfprError);
|
||||
end;
|
||||
|
||||
// Both names are hard links to the same file.
|
||||
|
||||
case AskIfOverwrite(NewFileStat.st_mode) of
|
||||
fsourYes: ; // continue
|
||||
fsourNo:
|
||||
Exit(sfprSkipped);
|
||||
fsourAbort:
|
||||
RaiseAbortOperation;
|
||||
end;
|
||||
|
||||
// Multiple links - simply unlink the source file.
|
||||
if fpUnLink(OldName) = 0 then
|
||||
Result := sfprSuccess
|
||||
else
|
||||
Result := sfprError;
|
||||
|
||||
Exit;
|
||||
end
|
||||
else
|
||||
begin
|
||||
case AskIfOverwrite(NewFileStat.st_mode) of
|
||||
fsourYes: ; // continue
|
||||
fsourNo:
|
||||
Exit(sfprSkipped);
|
||||
fsourAbort:
|
||||
RaiseAbortOperation;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if FpRename(OldName, NewName) = 0 then
|
||||
|
||||
{$ELSE}
|
||||
|
||||
// Windows XP doesn't allow two filenames that differ only by case (even on NTFS).
|
||||
if UTF8LowerCase(OldName) <> UTF8LowerCase(NewName) then
|
||||
begin
|
||||
NewFileAttrs := mbFileGetAttr(NewName);
|
||||
if NewFileAttrs <> faInvalidAttributes then // If target file exists.
|
||||
begin
|
||||
case AskIfOverwrite(NewFileAttrs) of
|
||||
fsourYes: ; // continue
|
||||
fsourNo:
|
||||
Exit(sfprSkipped);
|
||||
fsourAbort:
|
||||
RaiseAbortOperation;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if mbRenameFile(OldName, NewName) then
|
||||
{$ENDIF}
|
||||
|
||||
Result := sfprSuccess
|
||||
else
|
||||
Result := sfprError;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ type
|
|||
FSymLinkOption: TFileSourceOperationOptionSymLink;
|
||||
|
||||
protected
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): Boolean; override;
|
||||
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): TSetFilePropertyResult; override;
|
||||
|
||||
public
|
||||
constructor Create(aTargetFileSource: IFileSource;
|
||||
|
|
@ -136,20 +136,23 @@ begin
|
|||
end;
|
||||
|
||||
function TWfxPluginSetFilePropertyOperation.SetNewProperty(aFile: TFile;
|
||||
aTemplateProperty: TFileProperty): Boolean;
|
||||
aTemplateProperty: TFileProperty): TSetFilePropertyResult;
|
||||
var
|
||||
FileName: UTF8String;
|
||||
NewAttributes: TFileAttrs;
|
||||
ftTime: TFileTime;
|
||||
begin
|
||||
Result := True;
|
||||
Result := sfprSuccess;
|
||||
|
||||
case aTemplateProperty.GetID of
|
||||
fpName:
|
||||
if (aTemplateProperty as TFileNameProperty).Value <> aFile.Name then
|
||||
begin
|
||||
Result := WfxRenameFile(FWfxPluginFileSource, aFile, (aTemplateProperty as TFileNameProperty).Value);
|
||||
end;
|
||||
if not WfxRenameFile(FWfxPluginFileSource, aFile, (aTemplateProperty as TFileNameProperty).Value) then
|
||||
Result := sfprError;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpAttributes:
|
||||
if (aTemplateProperty as TFileAttributesProperty).Value <>
|
||||
|
|
@ -160,12 +163,20 @@ begin
|
|||
|
||||
with FWfxPluginFileSource.WfxModule do
|
||||
if aTemplateProperty is TNtfsFileAttributesProperty then
|
||||
Result:= WfxSetAttr(FileName, NewAttributes)
|
||||
begin
|
||||
if not WfxSetAttr(FileName, NewAttributes) then
|
||||
Result := sfprError;
|
||||
end
|
||||
else if aTemplateProperty is TUnixFileAttributesProperty then
|
||||
Result:= WfxExecuteFile(0, FileName, 'chmod' + #32 + DecToOct(NewAttributes)) = FS_EXEC_OK
|
||||
begin
|
||||
if WfxExecuteFile(0, FileName, 'chmod' + #32 + DecToOct(NewAttributes)) <> FS_EXEC_OK then
|
||||
Result := sfprError;
|
||||
end
|
||||
else
|
||||
raise Exception.Create('Unsupported file attributes type');
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpModificationTime:
|
||||
if (aTemplateProperty as TFileModificationDateTimeProperty).Value <>
|
||||
|
|
@ -173,8 +184,11 @@ begin
|
|||
begin
|
||||
ftTime := DateTimeToWfxFileTime((aTemplateProperty as TFileModificationDateTimeProperty).Value);
|
||||
with FWfxPluginFileSource.WfxModule do
|
||||
Result := WfxSetTime(aFile.FullPath, nil, nil, @ftTime);
|
||||
end;
|
||||
if not WfxSetTime(aFile.FullPath, nil, nil, @ftTime) then
|
||||
Result := sfprError;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpCreationTime:
|
||||
if (aTemplateProperty as TFileCreationDateTimeProperty).Value <>
|
||||
|
|
@ -182,8 +196,11 @@ begin
|
|||
begin
|
||||
ftTime := DateTimeToWfxFileTime((aTemplateProperty as TFileCreationDateTimeProperty).Value);
|
||||
with FWfxPluginFileSource.WfxModule do
|
||||
Result := WfxSetTime(aFile.FullPath, @ftTime, nil, nil);
|
||||
end;
|
||||
if not WfxSetTime(aFile.FullPath, @ftTime, nil, nil) then
|
||||
Result := sfprError;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
fpLastAccessTime:
|
||||
if (aTemplateProperty as TFileLastAccessDateTimeProperty).Value <>
|
||||
|
|
@ -191,8 +208,11 @@ begin
|
|||
begin
|
||||
ftTime := DateTimeToWfxFileTime((aTemplateProperty as TFileLastAccessDateTimeProperty).Value);
|
||||
with FWfxPluginFileSource.WfxModule do
|
||||
Result := WfxSetTime(aFile.FullPath, nil, @ftTime, nil);
|
||||
end;
|
||||
if not WfxSetTime(aFile.FullPath, nil, @ftTime, nil) then
|
||||
Result := sfprError;
|
||||
end
|
||||
else
|
||||
Result := sfprSkipped;
|
||||
|
||||
else
|
||||
raise Exception.Create('Trying to set unsupported property');
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ function mbDeleteToTrash(const FileName: UTF8String): Boolean;
|
|||
// 14.05.2009 - this funtion checks 'gvfs-trash' BEFORE deleting. Need for various linux disributives.
|
||||
function mbCheckTrash(sPath: UTF8String): Boolean;
|
||||
// ----------------
|
||||
function mbRenameFile(const OldName, NewName : UTF8String): Boolean;
|
||||
function mbRenameFile(const OldName: UTF8String; NewName: UTF8String): Boolean;
|
||||
function mbFileSize(const FileName: UTF8String): Int64;
|
||||
function FileFlush(Handle: THandle): Boolean;
|
||||
{ Directory handling functions}
|
||||
|
|
@ -1535,7 +1535,7 @@ begin
|
|||
end;
|
||||
{$ELSE}
|
||||
begin
|
||||
Result:= fpUnLink(FileName) >= 0;
|
||||
Result:= fpUnLink(FileName) = 0;
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
|
|
@ -1621,7 +1621,7 @@ end;
|
|||
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
function mbRenameFile(const OldName, NewName: UTF8String): Boolean;
|
||||
function mbRenameFile(const OldName: UTF8String; NewName: UTF8String): Boolean;
|
||||
{$IFDEF MSWINDOWS}
|
||||
var
|
||||
wOldName,
|
||||
|
|
@ -1632,8 +1632,83 @@ begin
|
|||
Result:= MoveFileExW(PWChar(wOldName), PWChar(wNewName), MOVEFILE_REPLACE_EXISTING);
|
||||
end;
|
||||
{$ELSE}
|
||||
var
|
||||
tmpFileName: UTF8String;
|
||||
OldFileStat, NewFileStat: stat;
|
||||
begin
|
||||
Result:= BaseUnix.FpRename(OldNAme, NewName) >= 0;
|
||||
if GetPathType(NewName) <> ptAbsolute then
|
||||
NewName := ExtractFilePath(OldName) + NewName;
|
||||
|
||||
if OldName = NewName then
|
||||
Exit(True);
|
||||
|
||||
if fpLstat(OldName, OldFileStat) <> 0 then
|
||||
Exit(False);
|
||||
|
||||
// Check if target file exists.
|
||||
if fpLstat(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(OldName, tmpFileName) = 0 then
|
||||
begin
|
||||
if fpLstat(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(tmpFileName, 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(OldName) = 0)
|
||||
else
|
||||
{$ENDIF}
|
||||
Result := False;
|
||||
end
|
||||
else if FpRename(tmpFileName, NewName) = 0 then
|
||||
begin
|
||||
Result := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
FpRename(tmpFileName, OldName); // Restore old file.
|
||||
Result := False;
|
||||
end;
|
||||
end
|
||||
else
|
||||
Result := False;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Multiple links - simply unlink the source file.
|
||||
Result := (fpUnLink(OldName) = 0);
|
||||
end;
|
||||
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
Result := FpRename(OldName, NewName) = 0;
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
|
|
@ -2051,4 +2126,4 @@ finalization
|
|||
|
||||
{$ENDIF}
|
||||
|
||||
end.
|
||||
end.
|
||||
Loading…
Add table
Add a link
Reference in a new issue